diff --git a/pytket/binders/circuit/Circuit/add_op.cpp b/pytket/binders/circuit/Circuit/add_op.cpp index c26e0d7614..20447d429d 100644 --- a/pytket/binders/circuit/Circuit/add_op.cpp +++ b/pytket/binders/circuit/Circuit/add_op.cpp @@ -1333,7 +1333,7 @@ void init_circuit_add_op(py::class_> &c) { [](Circuit *circ, const Expr &angle, unsigned qb, const py::kwargs &kwargs) { return add_gate_method_oneparam( - circ, OpType::GPI, angle, {qb}, kwargs); + circ, OpType::GPI2, angle, {qb}, kwargs); }, "Appends a GPI2 gate with a possibly symbolic angle " "(specified in half-turns)." diff --git a/pytket/binders/circuit_library.cpp b/pytket/binders/circuit_library.cpp index 0fcf6706c7..61a879f2ba 100644 --- a/pytket/binders/circuit_library.cpp +++ b/pytket/binders/circuit_library.cpp @@ -294,5 +294,32 @@ PYBIND11_MODULE(circuit_library, library_m) { library_m.def( "TK1_to_TK1", &CircPool::tk1_to_tk1, "A circuit of a single tk1 gate with given parameters"); + library_m.def( + "Rx_using_GPI", &CircPool::Rx_using_GPI, + "Equivalent to Rx, using GPI and GPI2 gates"); + library_m.def( + "Ry_using_GPI", &CircPool::Ry_using_GPI, + "Equivalent to Ry, using GPI and GPI2 gates"); + library_m.def( + "Rz_using_GPI", &CircPool::Rz_using_GPI, + "Equivalent to Rz, using GPI gates"); + library_m.def( + "XXPhase_using_AAMS", &CircPool::XXPhase_using_AAMS, + "Equivalent to XXPhase, using AAMS gates"); + library_m.def( + "YYPhase_using_AAMS", &CircPool::YYPhase_using_AAMS, + "Equivalent to YYPhase, using AAMS gates"); + library_m.def( + "ZZPhase_using_AAMS", &CircPool::ZZPhase_using_AAMS, + "Equivalent to ZZPhase, using AAMS, GPI and GPI2 gates"); + library_m.def( + "CX_using_AAMS", &CircPool::CX_using_AAMS, + "Equivalent to CX, using AAMS, GPI and GPI2 gates"); + library_m.def( + "TK1_using_GPI", &CircPool::TK1_using_GPI, + "Equivalent to TK1, using GPI and GPI2 gates"); + library_m.def( + "TK2_using_AAMS", &CircPool::TK2_using_AAMS, + "Equivalent to TK2, using AAMS, GPI and GPI2 gates"); } } // namespace tket diff --git a/pytket/binders/transform.cpp b/pytket/binders/transform.cpp index b912b4ebec..8c074c3a1e 100644 --- a/pytket/binders/transform.cpp +++ b/pytket/binders/transform.cpp @@ -152,6 +152,10 @@ PYBIND11_MODULE(transform, m) { "Rebase from any gate set into the gate set supported by " "ProjectQ (Rx, Ry, Rz, X, Y, Z, S, T, V, H, CX, CZ, CRz, " "SWAP).") + .def_static( + "RebaseToIonQ", &Transforms::rebase_ionq, + "Rebase from any gate set into the gate set supported by " + "IonQ (GPI, GPI2, AAMS).") .def_static( "DecomposeCCX", &Transforms::decomp_CCX, "Decomposes all 3-qubit Toffoli (CCX) gates into " diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 0fc08c2376..2dc87bfd8b 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.125@tket/stable") + self.requires("tket/1.2.126@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 9606ea010b..3140ed281d 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -12,6 +12,8 @@ Features: * Add ``GreedyPauliSimp`` optimisation pass. * Add ``BitWiseOp.ZERO`` and ``BitWiseOp.ONE`` to allow construction of constant conditional expressions. +* Add target gateset ``(GPI, GPI2, AAMS)`` to ``auto_rebase_pass``. +* Add ``RebaseToIonQ`` transform. Fixes: diff --git a/pytket/pytket/_tket/circuit_library.pyi b/pytket/pytket/_tket/circuit_library.pyi index 92d11c805b..66d86bf18a 100644 --- a/pytket/pytket/_tket/circuit_library.pyi +++ b/pytket/pytket/_tket/circuit_library.pyi @@ -2,7 +2,7 @@ from __future__ import annotations import pytket._tket.circuit import sympy import typing -__all__ = ['BRIDGE', 'BRIDGE_using_CX_0', 'BRIDGE_using_CX_1', 'C3X_normal_decomp', 'C4X_normal_decomp', 'CCX', 'CCX_modulo_phase_shift', 'CCX_normal_decomp', 'CH_using_CX', 'CRx_using_CX', 'CRx_using_TK2', 'CRy_using_CX', 'CRy_using_TK2', 'CRz_using_CX', 'CRz_using_TK2', 'CSWAP_using_CX', 'CSX_using_CX', 'CSXdg_using_CX', 'CS_using_CX', 'CSdg_using_CX', 'CU1_using_CX', 'CU1_using_TK2', 'CU3_using_CX', 'CV_using_CX', 'CVdg_using_CX', 'CX', 'CX_S_CX_reduced', 'CX_S_V_XC_reduced', 'CX_VS_CX_reduced', 'CX_V_CX_reduced', 'CX_V_S_XC_reduced', 'CX_XC_reduced', 'CX_using_ECR', 'CX_using_TK2', 'CX_using_XXPhase_0', 'CX_using_XXPhase_1', 'CX_using_ZZMax', 'CX_using_ZZPhase', 'CX_using_flipped_CX', 'CY_using_CX', 'CZ_using_CX', 'ECR_using_CX', 'ESWAP_using_CX', 'ESWAP_using_TK2', 'FSim_using_CX', 'FSim_using_TK2', 'H_CZ_H', 'ISWAP_using_CX', 'ISWAP_using_TK2', 'NPhasedX_using_PhasedX', 'PhasedISWAP_using_CX', 'PhasedISWAP_using_TK2', 'SWAP_using_CX_0', 'SWAP_using_CX_1', 'TK1_to_PhasedXRz', 'TK1_to_RzH', 'TK1_to_RzRx', 'TK1_to_RzSX', 'TK1_to_TK1', 'TK2_using_3xCX', 'TK2_using_CX', 'TK2_using_CX_and_swap', 'TK2_using_TK2_or_swap', 'TK2_using_ZZMax', 'TK2_using_ZZMax_and_swap', 'TK2_using_ZZPhase', 'TK2_using_ZZPhase_and_swap', 'TK2_using_normalised_TK2', 'X', 'X1_CX', 'XXPhase3_using_CX', 'XXPhase3_using_TK2', 'XXPhase_using_CX', 'XXPhase_using_TK2', 'YYPhase_using_CX', 'YYPhase_using_TK2', 'Z0_CX', 'ZZMax_using_CX', 'ZZPhase_using_CX', 'ZZPhase_using_TK2', 'approx_TK2_using_1xCX', 'approx_TK2_using_1xZZPhase', 'approx_TK2_using_2xCX', 'approx_TK2_using_2xZZPhase', 'ladder_down', 'ladder_down_2', 'ladder_up'] +__all__ = ['BRIDGE', 'BRIDGE_using_CX_0', 'BRIDGE_using_CX_1', 'C3X_normal_decomp', 'C4X_normal_decomp', 'CCX', 'CCX_modulo_phase_shift', 'CCX_normal_decomp', 'CH_using_CX', 'CRx_using_CX', 'CRx_using_TK2', 'CRy_using_CX', 'CRy_using_TK2', 'CRz_using_CX', 'CRz_using_TK2', 'CSWAP_using_CX', 'CSX_using_CX', 'CSXdg_using_CX', 'CS_using_CX', 'CSdg_using_CX', 'CU1_using_CX', 'CU1_using_TK2', 'CU3_using_CX', 'CV_using_CX', 'CVdg_using_CX', 'CX', 'CX_S_CX_reduced', 'CX_S_V_XC_reduced', 'CX_VS_CX_reduced', 'CX_V_CX_reduced', 'CX_V_S_XC_reduced', 'CX_XC_reduced', 'CX_using_AAMS', 'CX_using_ECR', 'CX_using_TK2', 'CX_using_XXPhase_0', 'CX_using_XXPhase_1', 'CX_using_ZZMax', 'CX_using_ZZPhase', 'CX_using_flipped_CX', 'CY_using_CX', 'CZ_using_CX', 'ECR_using_CX', 'ESWAP_using_CX', 'ESWAP_using_TK2', 'FSim_using_CX', 'FSim_using_TK2', 'H_CZ_H', 'ISWAP_using_CX', 'ISWAP_using_TK2', 'NPhasedX_using_PhasedX', 'PhasedISWAP_using_CX', 'PhasedISWAP_using_TK2', 'Rx_using_GPI', 'Ry_using_GPI', 'Rz_using_GPI', 'SWAP_using_CX_0', 'SWAP_using_CX_1', 'TK1_to_PhasedXRz', 'TK1_to_RzH', 'TK1_to_RzRx', 'TK1_to_RzSX', 'TK1_to_TK1', 'TK1_using_GPI', 'TK2_using_3xCX', 'TK2_using_AAMS', 'TK2_using_CX', 'TK2_using_CX_and_swap', 'TK2_using_TK2_or_swap', 'TK2_using_ZZMax', 'TK2_using_ZZMax_and_swap', 'TK2_using_ZZPhase', 'TK2_using_ZZPhase_and_swap', 'TK2_using_normalised_TK2', 'X', 'X1_CX', 'XXPhase3_using_CX', 'XXPhase3_using_TK2', 'XXPhase_using_AAMS', 'XXPhase_using_CX', 'XXPhase_using_TK2', 'YYPhase_using_AAMS', 'YYPhase_using_CX', 'YYPhase_using_TK2', 'Z0_CX', 'ZZMax_using_CX', 'ZZPhase_using_AAMS', 'ZZPhase_using_CX', 'ZZPhase_using_TK2', 'approx_TK2_using_1xCX', 'approx_TK2_using_1xZZPhase', 'approx_TK2_using_2xCX', 'approx_TK2_using_2xZZPhase', 'ladder_down', 'ladder_down_2', 'ladder_up'] def BRIDGE() -> pytket._tket.circuit.Circuit: """ Just a BRIDGE[0,1,2] gate @@ -131,6 +131,10 @@ def CX_XC_reduced() -> pytket._tket.circuit.Circuit: """ CX-reduced form of CX/XC """ +def CX_using_AAMS() -> pytket._tket.circuit.Circuit: + """ + Equivalent to CX, using AAMS, GPI and GPI2 gates + """ def CX_using_ECR() -> pytket._tket.circuit.Circuit: """ Equivalent to CX, using only ECR, Rx and U3 gates @@ -211,6 +215,18 @@ def PhasedISWAP_using_TK2(arg0: sympy.Expr | float, arg1: sympy.Expr | float) -> """ Equivalent to PhasedISWAP, using a TK2 and Rz gates """ +def Rx_using_GPI(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to Rx, using GPI and GPI2 gates + """ +def Ry_using_GPI(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to Ry, using GPI and GPI2 gates + """ +def Rz_using_GPI(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to Rz, using GPI gates + """ def SWAP_using_CX_0() -> pytket._tket.circuit.Circuit: """ Equivalent to SWAP, using three CX, outer CX have control on qubit 0 @@ -239,12 +255,20 @@ def TK1_to_TK1(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.E """ A circuit of a single tk1 gate with given parameters """ +def TK1_using_GPI(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to TK1, using GPI and GPI2 gates + """ def TK2_using_3xCX(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ Given expressions α, β and γ, return circuit equivalent to TK2(α, β, γ) using 3 CX and single-qubit gates. Prefer using `_TK2_using_CX` unless you wish to explicitly use 3 CX or if α, β and γ are not normalised to the Weyl chamber. """ +def TK2_using_AAMS(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to TK2, using AAMS, GPI and GPI2 gates + """ def TK2_using_CX(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ Given expressions α, β and γ, return circuit equivalent to TK2(α, β, γ) using up to 3 CX and single-qubit gates. @@ -297,6 +321,10 @@ def XXPhase3_using_TK2(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit """ Equivalent to XXPhase3, using three TK2 gates """ +def XXPhase_using_AAMS(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to XXPhase, using AAMS gates + """ def XXPhase_using_CX(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ Equivalent to XXPhase, using CX and U3 gates @@ -305,6 +333,10 @@ def XXPhase_using_TK2(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ Equivalent to XXPhase, using a TK2 gate """ +def YYPhase_using_AAMS(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to YYPhase, using AAMS gates + """ def YYPhase_using_CX(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ Equivalent to YYPhase, using two CX gates and one Ry, one Sdg and one S gate. @@ -321,6 +353,10 @@ def ZZMax_using_CX() -> pytket._tket.circuit.Circuit: """ Equivalent to ZZMax, using CX, Rz and U3 gates """ +def ZZPhase_using_AAMS(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to ZZPhase, using AAMS, GPI and GPI2 gates + """ def ZZPhase_using_CX(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ Equivalent to ZZPhase, using CX and Rz gates diff --git a/pytket/pytket/_tket/transform.pyi b/pytket/pytket/_tket/transform.pyi index 5fd1e85040..0795f97cbf 100644 --- a/pytket/pytket/_tket/transform.pyi +++ b/pytket/pytket/_tket/transform.pyi @@ -247,6 +247,11 @@ class Transform: Replace all single-qubit unitary gates outside the set {Z, X, S, V} that are recognized as Clifford operations with an equivalent sequence of gates from that set. """ @staticmethod + def RebaseToIonQ() -> Transform: + """ + Rebase from any gate set into the gate set supported by IonQ (GPI, GPI2, AAMS). + """ + @staticmethod def RebaseToProjectQ() -> Transform: """ Rebase from any gate set into the gate set supported by ProjectQ (Rx, Ry, Rz, X, Y, Z, S, T, V, H, CX, CZ, CRz, SWAP). diff --git a/pytket/pytket/circuit_library/__init__.py b/pytket/pytket/circuit_library/__init__.py index 114cfe5020..1cd3275f1e 100644 --- a/pytket/pytket/circuit_library/__init__.py +++ b/pytket/pytket/circuit_library/__init__.py @@ -27,6 +27,7 @@ CX_using_ZZPhase, CX_using_XXPhase_0, CX_using_XXPhase_1, + CX_using_AAMS, CX_VS_CX_reduced, CX_V_CX_reduced, CX_S_CX_reduced, @@ -100,4 +101,12 @@ TK1_to_RzH, TK1_to_RzSX, TK1_to_TK1, + Rx_using_GPI, + Ry_using_GPI, + Rz_using_GPI, + XXPhase_using_AAMS, + YYPhase_using_AAMS, + ZZPhase_using_AAMS, + TK1_using_GPI, + TK2_using_AAMS, ) diff --git a/pytket/pytket/passes/auto_rebase.py b/pytket/pytket/passes/auto_rebase.py index 4998963198..5cc5db4e4d 100644 --- a/pytket/pytket/passes/auto_rebase.py +++ b/pytket/pytket/passes/auto_rebase.py @@ -31,6 +31,7 @@ class NoAutoRebase(Exception): OpType.XXPhase: _library.CX_using_XXPhase_0, OpType.ECR: _library.CX_using_ECR, OpType.CZ: _library.H_CZ_H, + OpType.AAMS: _library.CX_using_AAMS, } @@ -43,6 +44,7 @@ def _TK2_using_TK2(a: Param, b: Param, c: Param) -> Circuit: OpType.ZZPhase: _library.TK2_using_ZZPhase, OpType.CX: _library.TK2_using_CX, OpType.ZZMax: _library.TK2_using_ZZMax, + OpType.AAMS: _library.TK2_using_AAMS, } _TK2_CIRCS_WIRE_SWAP: Dict[OpType, Callable[[Param, Param, Param], "Circuit"]] = { @@ -101,6 +103,7 @@ def get_tk2_decomposition( frozenset({OpType.Rz, OpType.SX}): _TK1_to_X_SX_Rz, frozenset({OpType.Rz, OpType.SX}): _library.TK1_to_RzSX, frozenset({OpType.U3}): _TK1_to_U, + frozenset({OpType.GPI, OpType.GPI2}): _library.TK1_using_GPI, } diff --git a/pytket/pytket/utils/symbolic.py b/pytket/pytket/utils/symbolic.py index 26b839fadb..a0d0926344 100644 --- a/pytket/pytket/utils/symbolic.py +++ b/pytket/pytket/utils/symbolic.py @@ -252,6 +252,48 @@ def symb_fsim(params: ParamsType) -> ImmutableMatrix: ) +def symb_gpi(params: ParamsType) -> ImmutableMatrix: + t = sympy.exp(I * sympy.pi * params[0]) + + return ImmutableMatrix( # type: ignore + [ + [0, 1 / t], + [t, 0], + ] + ) + + +def symb_gpi2(params: ParamsType) -> ImmutableMatrix: + t = sympy.exp(I * sympy.pi * params[0]) + c = 1 / sympy.sqrt(2) # type: ignore + + return c * ImmutableMatrix( # type: ignore + [ + [1, -I / t], + [-I * t, 1], + ] + ) + + +def symb_aams(params: ParamsType) -> ImmutableMatrix: + alpha, beta, gamma = params + c = sympy.cos(sympy.pi / 2 * alpha) + s = sympy.sin(sympy.pi / 2 * alpha) + s1 = -I * sympy.exp(I * sympy.pi * (-beta - gamma)) * s + s2 = -I * sympy.exp(I * sympy.pi * (-beta + gamma)) * s + s3 = -I * sympy.exp(I * sympy.pi * (beta - gamma)) * s + s4 = -I * sympy.exp(I * sympy.pi * (beta + gamma)) * s + + return ImmutableMatrix( # type: ignore + [ + [c, 0, 0, s1], + [0, c, s2, 0], + [0, s3, c, 0], + [s4, 0, 0, c], + ] + ) + + # end symbolic matrix definitions @@ -282,6 +324,9 @@ class SymGateRegister: OpType.PhasedX: symb_phasedx, OpType.ESWAP: symb_eswap, OpType.FSim: symb_fsim, + OpType.GPI: symb_gpi, + OpType.GPI2: symb_gpi2, + OpType.AAMS: symb_aams, } @classmethod diff --git a/pytket/tests/transform_test.py b/pytket/tests/transform_test.py index 850b8fadd3..e6212c3f5a 100644 --- a/pytket/tests/transform_test.py +++ b/pytket/tests/transform_test.py @@ -1074,6 +1074,16 @@ def test_auto_rebase() -> None: _library.CX(), _library.TK1_to_TK1, ), + ( + {OpType.GPI, OpType.GPI2, OpType.AAMS}, + _library.CX_using_AAMS(), + _library.TK1_using_GPI, + ), + ( + {OpType.GPI, OpType.GPI2, OpType.AAMS}, + _library.TK2_using_AAMS, + _library.TK1_using_GPI, + ), ] circ = get_test_circuit() diff --git a/tket/conanfile.py b/tket/conanfile.py index 180dfffec2..d8a0c63a25 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.125" + version = "1.2.126" 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 461e909f0f..33de57c09e 100644 --- a/tket/include/tket/Circuit/CircPool.hpp +++ b/tket/include/tket/Circuit/CircPool.hpp @@ -49,6 +49,9 @@ const Circuit &CX_using_XXPhase_0(); /** Equivalent to CX, using only XXPhase, Rx and Rz gates */ const Circuit &CX_using_XXPhase_1(); +/** Equivalent to CX, using only AAMS, GPI and GPI2 gates */ +const Circuit &CX_using_AAMS(); + /** * CX-reduced form of CX/V,S/CX * @@ -536,6 +539,30 @@ Circuit CnU_gray_code_decomp(unsigned n, const Gate_ptr &gate); Circuit CnSU2_linear_decomp( unsigned n, const Expr &alpha, const Expr &theta, const Expr &beta); +/** Equivalent to Rx, using GPI and GPI2 gates */ +Circuit Rx_using_GPI(const Expr &theta); + +/** Equivalent to Ry, using GPI and GPI2 gates */ +Circuit Ry_using_GPI(const Expr &theta); + +/** Equivalent to Rz, using GPI gates */ +Circuit Rz_using_GPI(const Expr &theta); + +/** Equivalent to XXPhase, using AAMS gates */ +Circuit XXPhase_using_AAMS(const Expr &theta); + +/** Equivalent to YYPhase, using AAMS gates */ +Circuit YYPhase_using_AAMS(const Expr &theta); + +/** Equivalent to ZZPhase, using AAMS, GPI and GPI2 gates */ +Circuit ZZPhase_using_AAMS(const Expr &theta); + +/** Equivalent to TK1, using GPI and GPI2 gates */ +Circuit TK1_using_GPI(const Expr &alpha, const Expr &beta, const Expr &gamma); + +/** Equivalent to TK2, using AAMS, GPI and GPI2 gates */ +Circuit TK2_using_AAMS(const Expr &alpha, const Expr &beta, const Expr &gamma); + } // namespace CircPool } // namespace tket diff --git a/tket/include/tket/Transformations/Rebase.hpp b/tket/include/tket/Transformations/Rebase.hpp index 3eea674873..9bc011bd41 100644 --- a/tket/include/tket/Transformations/Rebase.hpp +++ b/tket/include/tket/Transformations/Rebase.hpp @@ -77,6 +77,10 @@ Transform rebase_TK(); // Singleqs: Rz, PhasedX Transform rebase_UMD(); +// Multiqs: AAMS +// Singleqs: GPI, GPI2 +Transform rebase_ionq(); + } // namespace Transforms } // namespace tket diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index 1b9d84368f..d0a6a4e1c8 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -153,6 +153,20 @@ const Circuit &CX_using_XXPhase_1() { return *C; } +const Circuit &CX_using_AAMS() { + static std::unique_ptr C = std::make_unique([]() { + Circuit c(2); + c.add_op(OpType::GPI2, 0.5, {0}); + c.add_op(OpType::GPI2, 1, {0}); + c.add_op(OpType::GPI2, 1, {1}); + c.add_op(OpType::AAMS, {0.5, 0, 0}, {0, 1}); + c.add_op(OpType::GPI2, -0.5, {0}); + c.add_phase(-0.25); + return c; + }()); + return *C; +} + const Circuit &CX_VS_CX_reduced() { static std::unique_ptr C = std::make_unique([]() { Circuit c(2); @@ -1377,6 +1391,80 @@ Circuit tk1_to_PhasedXRz( return c; } +Circuit Rx_using_GPI(const Expr &theta) { + Circuit c(1); + c.add_op(OpType::GPI2, 0.5, {0}); + c.add_op(OpType::GPI, 0.5 * theta, {0}); + c.add_op(OpType::GPI, 0, {0}); + c.add_op(OpType::GPI2, -0.5, {0}); + return c; +} + +Circuit Ry_using_GPI(const Expr &theta) { + Circuit c(1); + c.add_op(OpType::GPI2, 1, {0}); + c.add_op(OpType::GPI, 0.5 * theta, {0}); + c.add_op(OpType::GPI, 0, {0}); + c.add_op(OpType::GPI2, 0, {0}); + return c; +} + +Circuit Rz_using_GPI(const Expr &theta) { + Circuit c(1); + c.add_op(OpType::GPI, -0.5 * theta, {0}); + c.add_op(OpType::GPI, 0, {0}); + return c; +} + +Circuit XXPhase_using_AAMS(const Expr &theta) { + Circuit c(2); + c.add_op(OpType::AAMS, {theta, 0, 0}, {0, 1}); + return c; +} + +Circuit YYPhase_using_AAMS(const Expr &theta) { + Circuit c(2); + c.add_op(OpType::AAMS, {theta, 0.5, 0.5}, {0, 1}); + return c; +} + +Circuit ZZPhase_using_AAMS(const Expr &theta) { + Circuit c(2); + c.add_op(OpType::GPI2, 0.5, {0}); + c.add_op(OpType::GPI2, 1, {0}); + c.add_op(OpType::GPI2, 1, {1}); + c.add_op(OpType::AAMS, {theta, 0, 0.5}, {0, 1}); + c.add_op(OpType::GPI2, 0, {1}); + c.add_op(OpType::GPI2, 0, {0}); + c.add_op(OpType::GPI2, -0.5, {0}); + return c; +} + +Circuit TK1_using_GPI(const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c(1); + c.add_op(OpType::GPI, 0, {0}); + c.add_op(OpType::GPI, 0.5 * gamma, {0}); + c.add_op(OpType::GPI2, 0.5, {0}); + c.add_op(OpType::GPI, 0.5 * beta, {0}); + c.add_op(OpType::GPI2, 0.5, {0}); + c.add_op(OpType::GPI, 0.5 * alpha, {0}); + return c; +} + +Circuit TK2_using_AAMS(const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c(2); + c.add_op(OpType::AAMS, {alpha, 0, 0}, {0, 1}); + c.add_op(OpType::AAMS, {beta, 0.5, 0.5}, {0, 1}); + c.add_op(OpType::GPI2, 0.5, {0}); + c.add_op(OpType::GPI2, 1, {0}); + c.add_op(OpType::GPI2, 1, {1}); + c.add_op(OpType::AAMS, {gamma, 0, 0.5}, {0, 1}); + c.add_op(OpType::GPI2, 0, {1}); + c.add_op(OpType::GPI2, 0, {0}); + c.add_op(OpType::GPI2, -0.5, {0}); + return c; +} + } // namespace CircPool } // namespace tket diff --git a/tket/src/Transformations/Rebase.cpp b/tket/src/Transformations/Rebase.cpp index a6f62cff0a..497dc7452f 100644 --- a/tket/src/Transformations/Rebase.cpp +++ b/tket/src/Transformations/Rebase.cpp @@ -287,6 +287,14 @@ Transform rebase_UMD() { CircPool::CX_using_XXPhase_0(), CircPool::tk1_to_PhasedXRz); } +// Multiqs: AAMS +// Singleqs: GPI, GPI2 +Transform rebase_ionq() { + return rebase_factory( + {OpType::GPI, OpType::GPI2, OpType::AAMS}, CircPool::CX_using_AAMS(), + CircPool::TK1_using_GPI); +} + } // namespace Transforms } // namespace tket diff --git a/tket/test/src/Circuit/test_CircPool.cpp b/tket/test/src/Circuit/test_CircPool.cpp index 70ac4e1af0..d469525bda 100644 --- a/tket/test/src/Circuit/test_CircPool.cpp +++ b/tket/test/src/Circuit/test_CircPool.cpp @@ -45,6 +45,11 @@ SCENARIO("Simple CircPool identities") { orig.add_op(OpType::CX, {0, 1}); res = CircPool::H_CZ_H(); } + GIVEN("CX_using_AAMS") { + orig = Circuit(2); + orig.add_op(OpType::CX, {0, 1}); + res = CircPool::CX_using_AAMS(); + } auto u_orig = tket_sim::get_unitary(orig); auto u_res = tket_sim::get_unitary(res); @@ -234,5 +239,210 @@ SCENARIO("Test remove_noops") { } } +SCENARIO( + "Rx_using_GPI, Ry_using_GPI, Rz_using_GPI, XXPhase_using_AAMS," + "YYPhase_using_AAMS, ZZPhase_using_AAMS") { + Expr e1; + Sym asym = SymEngine::symbol("a"); + Expr a(asym); + + GIVEN("Normalised concrete angles (1)") { e1 = .3; } + GIVEN("Normalised concrete angles (2)") { e1 = -0.32; } + GIVEN("Not normalised concrete angles (1)") { e1 = 1.4; } + GIVEN("Not normalised concrete angles (2)") { e1 = -5.7; } + GIVEN("Symbolic angles (1)") { e1 = a; } + + Circuit rx_orig(1); + rx_orig.add_op(OpType::Rx, {e1}, {0}); + Circuit ry_orig(1); + ry_orig.add_op(OpType::Ry, {e1}, {0}); + Circuit rz_orig(1); + rz_orig.add_op(OpType::Rz, {e1}, {0}); + Circuit xxphase_orig(2); + xxphase_orig.add_op(OpType::XXPhase, {e1}, {0, 1}); + Circuit yyphase_orig(2); + yyphase_orig.add_op(OpType::YYPhase, {e1}, {0, 1}); + Circuit zzphase_orig(2); + zzphase_orig.add_op(OpType::ZZPhase, {e1}, {0, 1}); + std::vector circuits_orig = { + rx_orig, ry_orig, rz_orig, xxphase_orig, yyphase_orig, zzphase_orig}; + + Circuit rx_res = CircPool::Rx_using_GPI(e1); + Circuit ry_res = CircPool::Ry_using_GPI(e1); + Circuit rz_res = CircPool::Rz_using_GPI(e1); + Circuit xxphase_res = CircPool::XXPhase_using_AAMS(e1); + Circuit yyphase_res = CircPool::YYPhase_using_AAMS(e1); + Circuit zzphase_res = CircPool::ZZPhase_using_AAMS(e1); + std::vector circuits_res = {rx_res, ry_res, rz_res, + xxphase_res, yyphase_res, zzphase_res}; + + // check unitary identity + auto symset = circuits_orig[0].free_symbols(); + std::vector symbols(symset.begin(), symset.end()); + if (symbols.empty()) { + for (unsigned k = 0; k < 6; k++) { + auto u_orig = tket_sim::get_unitary(circuits_orig[k]); + auto u_res = tket_sim::get_unitary(circuits_res[k]); + REQUIRE(u_res.isApprox(u_orig)); + } + } else { + std::vector rands{0.1231, 2.3124, 34.23, 2.23, 3.15, 1.2, 0.93}; + // substitute random values for symbolics and check equality + unsigned i = 0; + while (i + symbols.size() <= rands.size()) { + symbol_map_t symmap; + for (unsigned j = 0; j < symbols.size(); ++j) { + symmap[symbols[j]] = rands[i + j]; + } + for (unsigned k = 0; k < 6; ++k) { + Circuit orig_sub = circuits_orig[k]; + orig_sub.symbol_substitution(symmap); + auto u_orig = tket_sim::get_unitary(orig_sub); + Circuit res_sub = circuits_res[k]; + res_sub.symbol_substitution(symmap); + auto u_res = tket_sim::get_unitary(res_sub); + REQUIRE(u_res.isApprox(u_orig)); + } + ++i; + } + } +} + +SCENARIO("TK1_using_GPI, TK2_using_AAMS") { + Expr e1, e2, e3; + Sym asym = SymEngine::symbol("a"); + Expr a(asym); + Sym bsym = SymEngine::symbol("b"); + Expr b(bsym); + Sym csym = SymEngine::symbol("c"); + Expr c(csym); + + GIVEN("Normalised concrete angles (1)") { + e1 = .3; + e2 = .1; + e3 = .05; + } + GIVEN("Normalised concrete angles (2)") { + e1 = 0.32; + e2 = 0.31; + e3 = -0.3; + } + GIVEN("Not normalised concrete angles (1)") { + e1 = .3; + e2 = .4; + e3 = .45; + } + GIVEN("Not normalised concrete angles (2)") { + e1 = .3; + e2 = 1.4; + e3 = .489; + } + GIVEN("Not normalised concrete angles (3)") { + e1 = 2.3; + e2 = 3.4; + e3 = .489; + } + GIVEN("Not normalised concrete angles (4)") { + e1 = .3; + e2 = -.2; + e3 = .1; + } + GIVEN("Not normalised concrete angles (5)") { + e1 = -.3; + e2 = -.2; + e3 = .1; + } + GIVEN("Not normalised concrete angles (6)") { + e1 = .3; + e2 = .2; + e3 = -.3; + } + GIVEN("Not normalised concrete angles (7)") { + e1 = 0; + e2 = 0; + e3 = -1.2; + } + GIVEN("Not normalised concrete angles (8)") { + e1 = 0.1; + e2 = 0.3; + e3 = 0.2; + } + GIVEN("Symbolic angles (1)") { + e1 = a; + e2 = 3.4; + e3 = .489; + } + GIVEN("Symbolic angles (2)") { + e1 = a; + e2 = b; + e3 = 2.42; + } + GIVEN("Symbolic angles (3)") { + e1 = 2.3; + e2 = b; + e3 = 1.489; + } + GIVEN("Symbolic angles (4)") { + e1 = 2.3; + e2 = 123.08174; + e3 = c; + } + GIVEN("Symbolic angles (5)") { + e1 = a; + e2 = 123.08174; + e3 = c; + } + GIVEN("Symbolic angles (6)") { + e1 = 0.10012; + e2 = b; + e3 = c; + } + + Circuit tk1_orig(1); + tk1_orig.add_op(OpType::TK1, {e1, e2, e3}, {0}); + Circuit tk1_res = CircPool::TK1_using_GPI(e1, e2, e3); + + Circuit tk2_orig(2); + tk2_orig.add_op(OpType::TK2, {e1, e2, e3}, {0, 1}); + Circuit tk2_res = CircPool::TK2_using_AAMS(e1, e2, e3); + + // check unitary identity + auto symset = tk1_orig.free_symbols(); + std::vector symbols(symset.begin(), symset.end()); + if (symbols.empty()) { + auto u_tk1_orig = tket_sim::get_unitary(tk1_orig); + auto u_tk2_orig = tket_sim::get_unitary(tk2_orig); + auto u_tk1_res = tket_sim::get_unitary(tk1_res); + auto u_tk2_res = tket_sim::get_unitary(tk2_res); + REQUIRE(u_tk1_res.isApprox(u_tk1_orig)); + REQUIRE(u_tk2_res.isApprox(u_tk2_orig)); + } else { + std::vector rands{0.1231, 2.3124, 34.23, 2.23, 3.15, 1.2, 0.93}; + // substitute random values for symbolics and check equality + unsigned i = 0; + while (i + symbols.size() <= rands.size()) { + symbol_map_t symmap; + for (unsigned j = 0; j < symbols.size(); ++j) { + symmap[symbols[j]] = rands[i + j]; + } + Circuit tk1_orig_sub = tk1_orig; + Circuit tk2_orig_sub = tk2_orig; + tk1_orig_sub.symbol_substitution(symmap); + tk2_orig_sub.symbol_substitution(symmap); + auto u_tk1_orig = tket_sim::get_unitary(tk1_orig_sub); + auto u_tk2_orig = tket_sim::get_unitary(tk2_orig_sub); + Circuit tk1_res_sub = tk1_res; + Circuit tk2_res_sub = tk2_res; + tk1_res_sub.symbol_substitution(symmap); + tk2_res_sub.symbol_substitution(symmap); + auto u_tk1_res = tket_sim::get_unitary(tk1_res_sub); + auto u_tk2_res = tket_sim::get_unitary(tk2_res_sub); + REQUIRE(u_tk1_res.isApprox(u_tk1_orig)); + REQUIRE(u_tk2_res.isApprox(u_tk2_orig)); + ++i; + } + } +} + } // namespace test_CircPool } // namespace tket diff --git a/tket/test/src/Circuit/test_Symbolic.cpp b/tket/test/src/Circuit/test_Symbolic.cpp index 4e95122770..2389786d6e 100644 --- a/tket/test/src/Circuit/test_Symbolic.cpp +++ b/tket/test/src/Circuit/test_Symbolic.cpp @@ -207,5 +207,57 @@ SCENARIO("Symbolic squashing, correctness") { } } +SCENARIO("Symbolic GPI, GPI2, AAMS") { + Sym asym = SymEngine::symbol("a"); + Expr a(asym); + Sym bsym = SymEngine::symbol("b"); + Expr b(bsym); + Sym csym = SymEngine::symbol("c"); + Expr c(csym); + + Circuit gpi_orig(1); + gpi_orig.add_op(OpType::GPI, a, {0}); + Circuit gpi2_orig(1); + gpi2_orig.add_op(OpType::GPI2, a, {0}); + Circuit aams_orig(2); + aams_orig.add_op(OpType::AAMS, {a, b, c}, {0, 1}); + + std::vector rands{0.1231, 2.3124, 34.23, 2.23, 3.15, 1.2, 0.93}; + for (unsigned i = 0; i < rands.size(); ++i) { + Circuit gpi_orig_sub = gpi_orig; + Circuit gpi2_orig_sub = gpi2_orig; + double an = rands[i]; + symbol_map_t symmap; + symmap[asym] = an; + gpi_orig_sub.symbol_substitution(symmap); + gpi2_orig_sub.symbol_substitution(symmap); + auto u_gpi_orig = tket_sim::get_unitary(gpi_orig_sub); + auto u_gpi2_orig = tket_sim::get_unitary(gpi2_orig_sub); + Circuit gpi_res(1); + Circuit gpi2_res(1); + gpi_res.add_op(OpType::GPI, an, {0}); + gpi2_res.add_op(OpType::GPI2, an, {0}); + auto u_gpi_res = tket_sim::get_unitary(gpi_res); + auto u_gpi2_res = tket_sim::get_unitary(gpi2_res); + REQUIRE(u_gpi_res.isApprox(u_gpi_orig)); + REQUIRE(u_gpi2_res.isApprox(u_gpi2_orig)); + for (unsigned j = 0; j < rands.size(); ++j) { + double bn = rands[j]; + for (unsigned k = 0; k < rands.size(); ++k) { + double cn = rands[k]; + symmap[bsym] = bn; + symmap[csym] = cn; + Circuit aams_orig_sub = aams_orig; + aams_orig_sub.symbol_substitution(symmap); + auto u_aams_orig = tket_sim::get_unitary(aams_orig_sub); + Circuit aams_res(2); + aams_res.add_op(OpType::AAMS, {an, bn, cn}, {0, 1}); + auto u_aams_res = tket_sim::get_unitary(aams_res); + REQUIRE(u_aams_res.isApprox(u_aams_orig)); + } + } + } +} + } // namespace test_Symbolic } // namespace tket