From d07ad5434ef256b93e9044e104b921ebe23a3038 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Mon, 24 Mar 2025 15:33:29 +0000 Subject: [PATCH 01/12] Add ISWAPMax_using_TK2 and ISWAPMax_using_CX. --- tket/include/tket/Circuit/CircPool.hpp | 6 ++++++ tket/src/Circuit/CircPool.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/tket/include/tket/Circuit/CircPool.hpp b/tket/include/tket/Circuit/CircPool.hpp index b5570f75f4..563471255b 100644 --- a/tket/include/tket/Circuit/CircPool.hpp +++ b/tket/include/tket/Circuit/CircPool.hpp @@ -196,6 +196,12 @@ const Circuit &ECR_using_CX(); /** Equivalent to ZZMax, using CX, Rz and U3 gates */ const Circuit &ZZMax_using_CX(); +/** Equivalent to ISWAPMax, using a TK2 gate **/ +const Circuit &ISWAPMax_using_TK2(); + +/** Equivalent to ISWAPMax, using CX, Rz and U3 gates **/ +const Circuit &ISWAPMax_using_CX(); + /** Equivalent to CRz, using a TK2 and TK1 gates */ Circuit CRz_using_TK2(const Expr &alpha); diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index 3d78eda2e0..a149158c2d 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -684,6 +684,31 @@ const Circuit &ZZMax_using_CX() { return *C; } +const Circuit &ISWAPMax_using_TK2() { + static std::unique_ptr C = std::make_unique([]() { + Circuit c(2); + c.add_op(OpType::TK2, {-0.5, -0.5, 0}, {0, 1}); + return c; + }()); + return *C; +} + +const Circuit &ISWAPMax_using_CX() { + static std::unique_ptr C = std::make_unique([]() { + Circuit c(2); + c.add_op(OpType::U3, {0.5, -0.5, 0.5}, {0}); + c.add_op(OpType::U3, {0.5, -0.5, 0.5}, {1}); + c.add_op(OpType::CX, {0, 1}); + c.add_op(OpType::U3, {-0.5, -0.5, 0.5}, {0}); + c.add_op(OpType::Rz, -0.5, {1}); + c.add_op(OpType::CX, {0, 1}); + c.add_op(OpType::U3, {-0.5, -0.5, 0.5}, {0}); + c.add_op(OpType::U3, {-0.5, -0.5, 0.5}, {1}); + return c; + }()); + return *C; +} + Circuit CRz_using_TK2(const Expr &alpha) { Circuit c(2); c.add_op(OpType::TK1, {0.5, 0.5, 1}, {0}); From be199a7e2564482b60abbb0e2e5d7889e4126dc9 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Mon, 24 Mar 2025 15:35:42 +0000 Subject: [PATCH 02/12] Use in decompositions. --- tket/src/Circuit/CircUtils.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tket/src/Circuit/CircUtils.cpp b/tket/src/Circuit/CircUtils.cpp index 744d770eab..48d80805d7 100644 --- a/tket/src/Circuit/CircUtils.cpp +++ b/tket/src/Circuit/CircUtils.cpp @@ -475,6 +475,8 @@ Circuit with_TK2(Gate_ptr op) { switch (op->get_type()) { case OpType::ISWAP: return CircPool::ISWAP_using_TK2(params[0]); + case OpType::ISWAPMax: + return CircPool::ISWAPMax_using_TK2(); case OpType::PhasedISWAP: return CircPool::PhasedISWAP_using_TK2(params[0], params[1]); case OpType::XXPhase: @@ -603,7 +605,7 @@ Circuit with_CX(Gate_ptr op) { case OpType::Sycamore: return CircPool::FSim_using_CX(1. / 2., 1. / 6.); case OpType::ISWAPMax: - return CircPool::ISWAP_using_CX(1.); + return CircPool::ISWAPMax_using_CX(); case OpType::PhasedISWAP: return CircPool::PhasedISWAP_using_CX(params[0], params[1]); case OpType::NPhasedX: From 99e77621ec49c3ef279a5f489ec7c65a0f4d55ff Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Mon, 24 Mar 2025 15:55:36 +0000 Subject: [PATCH 03/12] Add to library. --- pytket/binders/circuit_library.cpp | 6 ++++++ pytket/pytket/circuit_library/__init__.py | 2 ++ 2 files changed, 8 insertions(+) diff --git a/pytket/binders/circuit_library.cpp b/pytket/binders/circuit_library.cpp index 714a827d91..572faad3cd 100644 --- a/pytket/binders/circuit_library.cpp +++ b/pytket/binders/circuit_library.cpp @@ -213,6 +213,12 @@ PYBIND11_MODULE(circuit_library, library_m) { library_m.def( "ISWAP_using_CX", &CircPool::ISWAP_using_CX, "Equivalent to ISWAP, using CX, U3 and Rz gates"); + library_m.def( + "ISWAPMax_using_TK2", &CircPool::ISWAPMax_using_TK2, + "Equivalent to ISWAPMax, using a TK2 gate"); + library_m.def( + "ISWAPMax_using_CX", &CircPool::ISWAPMax_using_CX, + "Equivalent to ISWAPMax, using CX, U3 and Rz gates"); library_m.def( "XXPhase_using_TK2", &CircPool::XXPhase_using_TK2, "Equivalent to XXPhase, using a TK2 gate"); diff --git a/pytket/pytket/circuit_library/__init__.py b/pytket/pytket/circuit_library/__init__.py index 1c45edd9ff..1833e20c68 100644 --- a/pytket/pytket/circuit_library/__init__.py +++ b/pytket/pytket/circuit_library/__init__.py @@ -66,6 +66,8 @@ FSim_using_TK2, ISWAP_using_CX, ISWAP_using_TK2, + ISWAPMax_using_CX, + ISWAPMax_using_TK2, NPhasedX_using_PhasedX, PhasedISWAP_using_CX, PhasedISWAP_using_TK2, From a64dcfd1271d9cf6dd4c79f7569332893999dce6 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Tue, 25 Mar 2025 08:39:55 +0000 Subject: [PATCH 04/12] Add TK2_using_ISWAPMax and CX_using_ISWAPMax. --- tket/include/tket/Circuit/CircPool.hpp | 6 ++++++ tket/src/Circuit/CircPool.cpp | 30 ++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/tket/include/tket/Circuit/CircPool.hpp b/tket/include/tket/Circuit/CircPool.hpp index 563471255b..e48cb159c2 100644 --- a/tket/include/tket/Circuit/CircPool.hpp +++ b/tket/include/tket/Circuit/CircPool.hpp @@ -40,6 +40,9 @@ const Circuit &CX_using_ECR(); /** Equivalent to CX, using only ZZMax, Rx and Rz gates */ const Circuit &CX_using_ZZMax(); +/** Equivalent to CX, using only ISWAPMax and single-qubit gates */ +const Circuit &CX_using_ISWAPMax(); + /** Equivalent to CX, using only ZZPhase, Rx and Rz gates */ const Circuit &CX_using_ZZPhase(); @@ -430,6 +433,9 @@ Circuit TK2_using_ZZMax(const Expr &alpha, const Expr &beta, const Expr &gamma); Circuit TK2_using_ZZMax_and_swap( const Expr &alpha, const Expr &beta, const Expr &gamma); +Circuit TK2_using_ISWAPMax( + const Expr &alpha, const Expr &beta, const Expr &gamma); + /** Equivalent to XXPhase3, using three TK2 gates */ Circuit XXPhase3_using_TK2(const Expr &alpha); diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index a149158c2d..a11bc1926e 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -105,6 +105,19 @@ const Circuit &CX_using_ZZMax() { return *C; } +const Circuit &CX_using_ISWAPMax() { + static std::unique_ptr C = std::make_unique([]() { + Circuit c(2); + c.add_op(OpType::ISWAPMax, {0, 1}); + c.add_op(OpType::H, {0}); + c.add_op(OpType::ISWAPMax, {0, 1}); + c.add_op(OpType::Sdg, {0}); + c.add_op(OpType::V, {1}); + return c; + }()); + return *C; +} + const Circuit &CX_using_ZZPhase() { static std::unique_ptr C = std::make_unique([]() { Circuit c(2); @@ -1141,6 +1154,23 @@ Circuit TK2_using_ZZMax_and_swap( return TK2_using_ZZMax(alpha, beta, gamma); } +Circuit TK2_using_ISWAPMax( + const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c = TK2_using_CX(alpha, beta, gamma); + // Find the CX gates and replace them with ISWAPMax. + VertexSet bin; + BGL_FORALL_VERTICES(v, c.dag, DAG) { + Op_ptr op = c.get_Op_ptr_from_Vertex(v); + if (op->get_type() == OpType::CX) { + c.substitute(CX_using_ISWAPMax(), v, Circuit::VertexDeletion::No); + bin.insert(v); + } + } + c.remove_vertices( + bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes); + return c; +} + Circuit XXPhase3_using_TK2(const Expr &alpha) { Circuit c(3); c.add_op(OpType::TK2, {alpha, 0, 0}, {0, 1}); From a93d2dea83bd454195d218c5c093cb21230aa8d7 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Tue, 25 Mar 2025 08:51:16 +0000 Subject: [PATCH 05/12] Use in decompositions. --- pytket/binders/circuit_library.cpp | 6 ++++++ pytket/pytket/circuit_library/__init__.py | 2 ++ tket/src/Predicates/PassGenerators.cpp | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/pytket/binders/circuit_library.cpp b/pytket/binders/circuit_library.cpp index 572faad3cd..4a4e2bfb90 100644 --- a/pytket/binders/circuit_library.cpp +++ b/pytket/binders/circuit_library.cpp @@ -68,6 +68,9 @@ PYBIND11_MODULE(circuit_library, library_m) { library_m.def( "CX_using_ZZMax", &CircPool::CX_using_ZZMax, "Equivalent to CX, using only ZZMax, Rx and Rz gates"); + library_m.def( + "CX_using_ISWAPMax", &CircPool::CX_using_ISWAPMax, + "Equivalent to CX, using only ISWAPMax and single-qubit gates"); library_m.def( "CX_using_ZZPhase", &CircPool::CX_using_ZZPhase, "Equivalent to CX, using only ZZPhase, Rx and Rz gates"); @@ -267,6 +270,9 @@ PYBIND11_MODULE(circuit_library, library_m) { "TK2_using_ZZMax_and_swap", &CircPool::TK2_using_ZZMax_and_swap, "Equivalent to TK2, up to a wire swap that is encoded in the implicit " "qubit permutation of the Circuit, using up to 3 ZZMax gates."); + library_m.def( + "TK2_using_ISWAPMax", &CircPool::TK2_using_ISWAPMax, + "Equivalent to TK2, using only ISWAPMax and single-qubit gates."); library_m.def( "XXPhase3_using_TK2", &CircPool::XXPhase3_using_TK2, "Equivalent to XXPhase3, using three TK2 gates"); diff --git a/pytket/pytket/circuit_library/__init__.py b/pytket/pytket/circuit_library/__init__.py index 1833e20c68..ab789fccd2 100644 --- a/pytket/pytket/circuit_library/__init__.py +++ b/pytket/pytket/circuit_library/__init__.py @@ -48,6 +48,7 @@ CX_using_AAMS, CX_using_ECR, CX_using_flipped_CX, + CX_using_ISWAPMax, CX_using_TK2, CX_using_XXPhase_0, CX_using_XXPhase_1, @@ -86,6 +87,7 @@ TK2_using_AAMS, TK2_using_CX, TK2_using_CX_and_swap, + TK2_using_ISWAPMax, TK2_using_normalised_TK2, TK2_using_TK2_or_swap, TK2_using_ZZMax, diff --git a/tket/src/Predicates/PassGenerators.cpp b/tket/src/Predicates/PassGenerators.cpp index b50d0d0933..af7fb057b4 100644 --- a/tket/src/Predicates/PassGenerators.cpp +++ b/tket/src/Predicates/PassGenerators.cpp @@ -170,6 +170,9 @@ static Circuit find_cx_replacement(const OpTypeSet& gateset) { if (gateset.contains(OpType::ZZMax)) { return CircPool::CX_using_ZZMax(); } + if (gateset.contains(OpType::ISWAPMax)) { + return CircPool::CX_using_ISWAPMax(); + } if (gateset.contains(OpType::XXPhase)) { return CircPool::CX_using_XXPhase_0(); } @@ -200,6 +203,9 @@ find_tk2_replacement(const OpTypeSet& gateset, bool allow_swaps) { if (gateset.contains(OpType::ZZMax)) { return CircPool::TK2_using_ZZMax; } + if (gateset.contains(OpType::ISWAPMax)) { + return CircPool::TK2_using_ISWAPMax; + } if (gateset.contains(OpType::AAMS)) { return CircPool::TK2_using_AAMS; } From 7960ab6844b9d90394c8820b9d8e46af2376f210 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Tue, 25 Mar 2025 09:14:14 +0000 Subject: [PATCH 06/12] Implement versions using implicit swaps. --- tket/include/tket/Circuit/CircPool.hpp | 12 ++++++++++ tket/src/Circuit/CircPool.cpp | 33 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/tket/include/tket/Circuit/CircPool.hpp b/tket/include/tket/Circuit/CircPool.hpp index e48cb159c2..df67ec4c40 100644 --- a/tket/include/tket/Circuit/CircPool.hpp +++ b/tket/include/tket/Circuit/CircPool.hpp @@ -43,6 +43,11 @@ const Circuit &CX_using_ZZMax(); /** Equivalent to CX, using only ISWAPMax and single-qubit gates */ const Circuit &CX_using_ISWAPMax(); +/** Equivalent to CX, using only ISWAPMax and single-qubit gates, with an + * implicit swap. + */ +const Circuit &CX_using_ISWAPMax_and_swap(); + /** Equivalent to CX, using only ZZPhase, Rx and Rz gates */ const Circuit &CX_using_ZZPhase(); @@ -433,9 +438,16 @@ Circuit TK2_using_ZZMax(const Expr &alpha, const Expr &beta, const Expr &gamma); Circuit TK2_using_ZZMax_and_swap( const Expr &alpha, const Expr &beta, const Expr &gamma); +/** Equivalent to TK2, using only ISWAPMax and single-qubit gates */ Circuit TK2_using_ISWAPMax( const Expr &alpha, const Expr &beta, const Expr &gamma); +/** Equivalent to TK2, using only ISWAPMax and single-qubit gates, with an + * implicit swap. + */ +Circuit TK2_using_ISWAPMax_and_swap( + const Expr &alpha, const Expr &beta, const Expr &gamma); + /** Equivalent to XXPhase3, using three TK2 gates */ Circuit XXPhase3_using_TK2(const Expr &alpha); diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index a11bc1926e..027f5060f6 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -118,6 +118,21 @@ const Circuit &CX_using_ISWAPMax() { return *C; } +const Circuit &CX_using_ISWAPMax_and_swap() { + static std::unique_ptr C = std::make_unique([]() { + Circuit c(2); + c.add_op(OpType::H, {1}); + c.add_op(OpType::Sdg, {0}); + c.add_op(OpType::Sdg, {1}); + c.add_op(OpType::ISWAPMax, {0, 1}); + c.add_op(OpType::H, {0}); + c.add_op(OpType::SWAP, {0, 1}); + c.replace_SWAPs(); + return c; + }()); + return *C; +} + const Circuit &CX_using_ZZPhase() { static std::unique_ptr C = std::make_unique([]() { Circuit c(2); @@ -1171,6 +1186,24 @@ Circuit TK2_using_ISWAPMax( return c; } +Circuit TK2_using_ISWAPMax_and_swap( + const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c = TK2_using_CX_and_swap(alpha, beta, gamma); + // Find the CX gates and replace them with ISWAPMax. + VertexSet bin; + BGL_FORALL_VERTICES(v, c.dag, DAG) { + Op_ptr op = c.get_Op_ptr_from_Vertex(v); + if (op->get_type() == OpType::CX) { + c.substitute( + CX_using_ISWAPMax_and_swap(), v, Circuit::VertexDeletion::No); + bin.insert(v); + } + } + c.remove_vertices( + bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes); + return c; +} + Circuit XXPhase3_using_TK2(const Expr &alpha) { Circuit c(3); c.add_op(OpType::TK2, {alpha, 0, 0}, {0, 1}); From d9da7074da5c806db17c28aaa02c65381c8dec3a Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Tue, 25 Mar 2025 10:34:34 +0000 Subject: [PATCH 07/12] Use in decompositions. --- pytket/binders/circuit_library.cpp | 10 ++++ pytket/pytket/circuit_library/__init__.py | 2 + tket/src/Predicates/PassGenerators.cpp | 62 ++++++++++------------- 3 files changed, 39 insertions(+), 35 deletions(-) diff --git a/pytket/binders/circuit_library.cpp b/pytket/binders/circuit_library.cpp index 4a4e2bfb90..8ed5fc4c7f 100644 --- a/pytket/binders/circuit_library.cpp +++ b/pytket/binders/circuit_library.cpp @@ -71,6 +71,11 @@ PYBIND11_MODULE(circuit_library, library_m) { library_m.def( "CX_using_ISWAPMax", &CircPool::CX_using_ISWAPMax, "Equivalent to CX, using only ISWAPMax and single-qubit gates"); + library_m.def( + "CX_using_ISWAPMax_and_swap", &CircPool::CX_using_ISWAPMax_and_swap, + "Equivalent to CX, using only ISWAPMax and single-qubit gates, up to a " + "wire swap that is encoded in the implicit qubit permutation of the " + "Circuit"); library_m.def( "CX_using_ZZPhase", &CircPool::CX_using_ZZPhase, "Equivalent to CX, using only ZZPhase, Rx and Rz gates"); @@ -273,6 +278,11 @@ PYBIND11_MODULE(circuit_library, library_m) { library_m.def( "TK2_using_ISWAPMax", &CircPool::TK2_using_ISWAPMax, "Equivalent to TK2, using only ISWAPMax and single-qubit gates."); + library_m.def( + "TK2_using_ISWAPMax_and_swap", &CircPool::TK2_using_ISWAPMax_and_swap, + "Equivalent to TK2, using only ISWAPMax and single-qubit gates, up to a " + "wire swap that is encoded in the implicit qubit permutation of the " + "Circuit."); library_m.def( "XXPhase3_using_TK2", &CircPool::XXPhase3_using_TK2, "Equivalent to XXPhase3, using three TK2 gates"); diff --git a/pytket/pytket/circuit_library/__init__.py b/pytket/pytket/circuit_library/__init__.py index ab789fccd2..dca6d94954 100644 --- a/pytket/pytket/circuit_library/__init__.py +++ b/pytket/pytket/circuit_library/__init__.py @@ -49,6 +49,7 @@ CX_using_ECR, CX_using_flipped_CX, CX_using_ISWAPMax, + CX_using_ISWAPMax_and_swap, CX_using_TK2, CX_using_XXPhase_0, CX_using_XXPhase_1, @@ -88,6 +89,7 @@ TK2_using_CX, TK2_using_CX_and_swap, TK2_using_ISWAPMax, + TK2_using_ISWAPMax_and_swap, TK2_using_normalised_TK2, TK2_using_TK2_or_swap, TK2_using_ZZMax, diff --git a/tket/src/Predicates/PassGenerators.cpp b/tket/src/Predicates/PassGenerators.cpp index af7fb057b4..049a188294 100644 --- a/tket/src/Predicates/PassGenerators.cpp +++ b/tket/src/Predicates/PassGenerators.cpp @@ -163,7 +163,7 @@ find_tk1_replacement(const OpTypeSet& gateset) { throw Unsupported("No known decomposition from TK1 to available gateset."); } -static Circuit find_cx_replacement(const OpTypeSet& gateset) { +static Circuit find_cx_replacement(const OpTypeSet& gateset, bool allow_swaps) { if (gateset.contains(OpType::CX)) { return CircPool::CX(); } @@ -171,7 +171,8 @@ static Circuit find_cx_replacement(const OpTypeSet& gateset) { return CircPool::CX_using_ZZMax(); } if (gateset.contains(OpType::ISWAPMax)) { - return CircPool::CX_using_ISWAPMax(); + return allow_swaps ? CircPool::CX_using_ISWAPMax_and_swap() + : CircPool::CX_using_ISWAPMax(); } if (gateset.contains(OpType::XXPhase)) { return CircPool::CX_using_XXPhase_0(); @@ -190,38 +191,28 @@ static Circuit find_cx_replacement(const OpTypeSet& gateset) { static std::function find_tk2_replacement(const OpTypeSet& gateset, bool allow_swaps) { - if (!allow_swaps) { - if (gateset.contains(OpType::TK2)) { - return CircPool::TK2_using_TK2; - } - if (gateset.contains(OpType::ZZPhase)) { - return CircPool::TK2_using_ZZPhase; - } - if (gateset.contains(OpType::CX)) { - return CircPool::TK2_using_CX; - } - if (gateset.contains(OpType::ZZMax)) { - return CircPool::TK2_using_ZZMax; - } - if (gateset.contains(OpType::ISWAPMax)) { - return CircPool::TK2_using_ISWAPMax; - } - if (gateset.contains(OpType::AAMS)) { - return CircPool::TK2_using_AAMS; - } - } else { - if (gateset.contains(OpType::TK2)) { - return CircPool::TK2_using_TK2_or_swap; - } - if (gateset.contains(OpType::ZZPhase)) { - return CircPool::TK2_using_ZZPhase_and_swap; - } - if (gateset.contains(OpType::CX)) { - return CircPool::TK2_using_CX_and_swap; - } - if (gateset.contains(OpType::ZZMax)) { - return CircPool::TK2_using_ZZMax_and_swap; - } + if (gateset.contains(OpType::TK2)) { + return allow_swaps ? CircPool::TK2_using_TK2_or_swap + : CircPool::TK2_using_TK2; + } + if (gateset.contains(OpType::ZZPhase)) { + return allow_swaps ? CircPool::TK2_using_ZZPhase_and_swap + : CircPool::TK2_using_ZZPhase; + } + if (gateset.contains(OpType::CX)) { + return allow_swaps ? CircPool::TK2_using_CX_and_swap + : CircPool::TK2_using_CX; + } + if (gateset.contains(OpType::ZZMax)) { + return allow_swaps ? CircPool::TK2_using_ZZMax_and_swap + : CircPool::TK2_using_ZZMax; + } + if (gateset.contains(OpType::ISWAPMax)) { + return allow_swaps ? CircPool::TK2_using_ISWAPMax_and_swap + : CircPool::TK2_using_ISWAPMax; + } + if (gateset.contains(OpType::AAMS)) { + return CircPool::TK2_using_AAMS; } throw Unsupported("No known decomposition from TK2 to available gateset."); } @@ -242,7 +233,8 @@ PassPtr gen_auto_rebase_pass(const OpTypeSet& allowed_gates, bool allow_swaps) { } try { return Transforms::rebase_factory( - allowed_gates, find_cx_replacement(allowed_gates), tk1_replacement); + allowed_gates, find_cx_replacement(allowed_gates, allow_swaps), + tk1_replacement); } catch (const Unsupported&) { throw Unsupported( "No known decomposition from CX or TK2 to available gateset."); From 13b9707cae15d4b5584b40c66fa9a1da2db996cb Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Tue, 25 Mar 2025 10:41:43 +0000 Subject: [PATCH 08/12] Add test for AutoRebase with ISWAPMax. --- pytket/tests/predicates_test.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pytket/tests/predicates_test.py b/pytket/tests/predicates_test.py index 33ff66ca1d..7e3c476a49 100644 --- a/pytket/tests/predicates_test.py +++ b/pytket/tests/predicates_test.py @@ -859,6 +859,12 @@ def test_rz_sx_decomp() -> None: assert c == comp +def test_iswapmax_autorebase() -> None: + c = Circuit(2).H(0).CX(0, 1) + assert AutoRebase({OpType.ISWAPMax, OpType.TK1}).apply(c) + assert c.n_gates_of_type(OpType.ISWAPMax) <= 2 + + def test_flatten_relabel_pass() -> None: c = Circuit(3) c.H(1).H(2) From 9de491c4431b9cc45adac59e56a2686800444443 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Tue, 25 Mar 2025 11:20:41 +0000 Subject: [PATCH 09/12] Update changelog. --- pytket/docs/changelog.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 117798c82c..672ad6e984 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -1,6 +1,12 @@ Changelog ========= +Unreleased +---------- + +* Add methods in `circuit_library` to convert `CX` and `TK2` to and from + `ISWAPMax`, and support for `AutoRebase` targeting `ISWAPMax`. + 2.1.0 (March 2025) ------------------ From d92babf7d0ee9c8898257697be2346fefe61ef65 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Tue, 25 Mar 2025 11:26:27 +0000 Subject: [PATCH 10/12] Regenerate stubs. --- pytket/pytket/_tket/circuit_library.pyi | 26 ++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/pytket/pytket/_tket/circuit_library.pyi b/pytket/pytket/_tket/circuit_library.pyi index 261cc52ed9..952f6d4e53 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_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', 'CnX_vchain_decomp', '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_RxRy', 'TK1_to_RzH', 'TK1_to_RzRx', 'TK1_to_RzSX', 'TK1_to_RzXSX', 'TK1_to_TK1', 'TK1_to_U3', 'TK1_using_GPI', 'TK2_using_3xCX', 'TK2_using_AAMS', 'TK2_using_CX', 'TK2_using_CX_and_swap', 'TK2_using_TK2', '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'] +__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_ISWAPMax', 'CX_using_ISWAPMax_and_swap', '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', 'CnX_vchain_decomp', 'ECR_using_CX', 'ESWAP_using_CX', 'ESWAP_using_TK2', 'FSim_using_CX', 'FSim_using_TK2', 'H_CZ_H', 'ISWAPMax_using_CX', 'ISWAPMax_using_TK2', '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_RxRy', 'TK1_to_RzH', 'TK1_to_RzRx', 'TK1_to_RzSX', 'TK1_to_RzXSX', 'TK1_to_TK1', 'TK1_to_U3', 'TK1_using_GPI', 'TK2_using_3xCX', 'TK2_using_AAMS', 'TK2_using_CX', 'TK2_using_CX_and_swap', 'TK2_using_ISWAPMax', 'TK2_using_ISWAPMax_and_swap', 'TK2_using_TK2', '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 @@ -139,6 +139,14 @@ def CX_using_ECR() -> pytket._tket.circuit.Circuit: """ Equivalent to CX, using only ECR, Rx and U3 gates """ +def CX_using_ISWAPMax() -> pytket._tket.circuit.Circuit: + """ + Equivalent to CX, using only ISWAPMax and single-qubit gates + """ +def CX_using_ISWAPMax_and_swap() -> pytket._tket.circuit.Circuit: + """ + Equivalent to CX, using only ISWAPMax and single-qubit gates, up to a wire swap that is encoded in the implicit qubit permutation of the Circuit + """ def CX_using_TK2() -> pytket._tket.circuit.Circuit: """ Equivalent to CX, using a TK2 and single-qubit gates @@ -204,6 +212,14 @@ def H_CZ_H() -> pytket._tket.circuit.Circuit: """ H[1]; CZ[0,1]; H[1] """ +def ISWAPMax_using_CX() -> pytket._tket.circuit.Circuit: + """ + Equivalent to ISWAPMax, using CX, U3 and Rz gates + """ +def ISWAPMax_using_TK2() -> pytket._tket.circuit.Circuit: + """ + Equivalent to ISWAPMax, using a TK2 gate + """ def ISWAP_using_CX(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ Equivalent to ISWAP, using CX, U3 and Rz gates @@ -302,6 +318,14 @@ def TK2_using_CX_and_swap(arg0: sympy.Expr | float, arg1: sympy.Expr | float, ar The decomposition minimizes the number of CX gates. """ +def TK2_using_ISWAPMax(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to TK2, using only ISWAPMax and single-qubit gates. + """ +def TK2_using_ISWAPMax_and_swap(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to TK2, using only ISWAPMax and single-qubit gates, up to a wire swap that is encoded in the implicit qubit permutation of the Circuit. + """ def TK2_using_TK2(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ A circuit of a single TK2 gate with given parameters From 0b7a308fa85fe62ae4879b7748e16390185355d2 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Tue, 25 Mar 2025 13:57:05 +0000 Subject: [PATCH 11/12] Bump tket version. --- pytket/conanfile.py | 2 +- tket/conanfile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 6b87a6f43a..f468dd38a4 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -38,7 +38,7 @@ def requirements(self): self.requires("pybind11_json/tci-0.2.15@tket/stable") self.requires("symengine/tci-0.14.0@tket/stable") self.requires("tkassert/0.3.4@tket/stable") - self.requires("tket/2.1.1@tket/stable") + self.requires("tket/2.1.2@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tktokenswap/0.3.11@tket/stable") diff --git a/tket/conanfile.py b/tket/conanfile.py index 21804c6816..a49265e109 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "2.1.1" + version = "2.1.2" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" From e10c02b9fd293b05a9c5877803e154979bf4a973 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Tue, 25 Mar 2025 14:28:45 +0000 Subject: [PATCH 12/12] Add tests. --- tket/test/src/Circuit/test_CircPool.cpp | 30 +++++++++++++++++++++++++ tket/test/src/test_CompilerPass.cpp | 15 +++++++++++++ 2 files changed, 45 insertions(+) diff --git a/tket/test/src/Circuit/test_CircPool.cpp b/tket/test/src/Circuit/test_CircPool.cpp index ae356e1565..0aae85c8f4 100644 --- a/tket/test/src/Circuit/test_CircPool.cpp +++ b/tket/test/src/Circuit/test_CircPool.cpp @@ -64,6 +64,36 @@ SCENARIO("Simple CircPool identities") { orig.add_op(OpType::TK1, {0.2, 0.3, 0.4}, {0}); res = CircPool::tk1_to_rxry(0.2, 0.3, 0.4); } + GIVEN("CX_using_ISWAPMax") { + orig = Circuit(2); + orig.add_op(OpType::CX, {0, 1}); + res = CircPool::CX_using_ISWAPMax(); + } + GIVEN("CX_using_ISWAPMax_and_swap") { + orig = Circuit(2); + orig.add_op(OpType::CX, {0, 1}); + res = CircPool::CX_using_ISWAPMax_and_swap(); + } + GIVEN("TK2_using_ISWAPMax") { + orig = Circuit(2); + orig.add_op(OpType::TK2, {0.2, 0.3, 0.4}, {0, 1}); + res = CircPool::TK2_using_ISWAPMax(0.2, 0.3, 0.4); + } + GIVEN("TK2_using_ISWAPMax_and_swap") { + orig = Circuit(2); + orig.add_op(OpType::TK2, {0.2, 0.3, 0.4}, {0, 1}); + res = CircPool::TK2_using_ISWAPMax_and_swap(0.2, 0.3, 0.4); + } + GIVEN("ISWAPMax_using_TK2") { + orig = Circuit(2); + orig.add_op(OpType::ISWAPMax, {0, 1}); + res = CircPool::ISWAPMax_using_TK2(); + } + GIVEN("ISWAPMax_using_CX") { + orig = Circuit(2); + orig.add_op(OpType::ISWAPMax, {0, 1}); + res = CircPool::ISWAPMax_using_CX(); + } auto u_orig = tket_sim::get_unitary(orig); auto u_res = tket_sim::get_unitary(res); REQUIRE(u_res.isApprox(u_orig)); diff --git a/tket/test/src/test_CompilerPass.cpp b/tket/test/src/test_CompilerPass.cpp index 27f5db6002..e16a6d8587 100644 --- a/tket/test/src/test_CompilerPass.cpp +++ b/tket/test/src/test_CompilerPass.cpp @@ -2289,6 +2289,21 @@ SCENARIO("AutoRebase") { gen_auto_rebase_pass({OpType::CRz, OpType::TK1}, false), Unsupported); REQUIRE_THROWS_AS(gen_auto_rebase_pass({}, false), Unsupported); } + GIVEN("Rebasing to ISWAPMax") { + Circuit circ(2); + circ.add_op(OpType::CZ, {0, 1}); + Circuit circ1 = circ; + PassPtr p_noswaps = + gen_auto_rebase_pass({OpType::ISWAPMax, OpType::TK1}, false); + PassPtr p_swapsok = + gen_auto_rebase_pass({OpType::ISWAPMax, OpType::TK1}, true); + CompilationUnit cu(circ); + CompilationUnit cu1(circ1); + CHECK(p_noswaps->apply(cu)); + CHECK(p_swapsok->apply(cu1)); + REQUIRE(test_unitary_comparison(circ, cu.get_circ_ref())); + REQUIRE(test_unitary_comparison(circ1, cu1.get_circ_ref())); + } } SCENARIO("AutoSquash") {