From 2cc9e163f4dc65a3e81d10078fbb004ea72b9594 Mon Sep 17 00:00:00 2001 From: seyon Date: Thu, 10 Feb 2022 16:28:17 +0000 Subject: [PATCH 1/6] [feature] simplify custom rebase with one gateset parameter --- pytket/binders/passes.cpp | 12 ++-- pytket/tests/predicates_test.py | 9 +-- schemas/compiler_pass_v1.json | 15 ++--- tket/src/Predicates/PassGenerators.cpp | 25 ++++---- .../include/Predicates/PassGenerators.hpp | 3 +- tket/src/Transformations/Rebase.cpp | 58 +++++++++---------- .../include/Transformations/Rebase.hpp | 5 +- tket/tests/test_CompilerPass.cpp | 7 ++- tket/tests/test_Rebase.cpp | 53 +++++++---------- 9 files changed, 80 insertions(+), 107 deletions(-) diff --git a/pytket/binders/passes.cpp b/pytket/binders/passes.cpp index 1f8b80f266..11df97920c 100644 --- a/pytket/binders/passes.cpp +++ b/pytket/binders/passes.cpp @@ -459,25 +459,23 @@ PYBIND11_MODULE(passes, m) { m.def( "RebaseCustom", &gen_rebase_pass, "Construct a custom rebase pass. This pass:\n(1) decomposes " - "multi-qubit gates not in the set of gate types `multiqs` to CX " - "gates;\n(2) if CX is not in `multiqs`, replaces CX gates with " + "multi-qubit gates not in the set of gate types `gateset` to CX " + "gates;\n(2) if CX is not in `gateset`, replaces CX gates with " "`cx_replacement`;\n(3) converts any single-qubit gates not in the " - "gate type set `singleqs` to the form " + "gate type set to the form " ":math:`\\mathrm{Rz}(a)\\mathrm{Rx}(b)\\mathrm{Rz}(c)` (in " "matrix-multiplication order, i.e. reverse order in the " "circuit);\n(4) applies the `tk1_replacement` function to each of " "these triples :math:`(a,b,c)` to generate replacement circuits." - "\n\n:param multiqs: The allowed multi-qubit operations in the " + "\n\n:param gateset: The allowed multi-qubit operations in the " "rebased circuit." "\n:param cx_replacement: The equivalent circuit to replace a CX " "gate in the desired basis." - "\n:param singleqs: The allowed single-qubit operations in the " - "rebased circuit." "\n:param tk1_replacement: A function which, given the parameters of " "an Rz(a)Rx(b)Rz(c) triple, returns an equivalent circuit in the " "desired basis." "\n:return: a pass that rebases to the given gate set", - py::arg("multiqs"), py::arg("cx_replacement"), py::arg("singleqs"), + py::arg("gateset"), py::arg("cx_replacement"), py::arg("tk1_replacement")); m.def( diff --git a/pytket/tests/predicates_test.py b/pytket/tests/predicates_test.py index f44909447e..624cce6c27 100644 --- a/pytket/tests/predicates_test.py +++ b/pytket/tests/predicates_test.py @@ -132,7 +132,7 @@ def test_rebase_pass_generation() -> None: cx = Circuit(2) cx.CX(0, 1) pz_rebase = RebaseCustom( - {OpType.CX}, cx, {OpType.PhasedX, OpType.Rz}, tk1_to_phasedxrz + {OpType.CX, OpType.PhasedX, OpType.Rz}, cx, tk1_to_phasedxrz ) circ = Circuit(2) circ.X(0).Y(1) @@ -622,14 +622,15 @@ def sq(a: float, b: float, c: float) -> Circuit: cx = Circuit(2) cx.CX(0, 1) pz_rebase = RebaseCustom( - {OpType.CX}, cx, {OpType.PhasedX, OpType.Rz}, tk1_to_phasedxrz + {OpType.CX, OpType.PhasedX, OpType.Rz}, cx, tk1_to_phasedxrz ) assert pz_rebase.to_dict()["StandardPass"]["name"] == "RebaseCustom" - assert pz_rebase.to_dict()["StandardPass"]["basis_multiqs"] == ["CX"] - assert set(pz_rebase.to_dict()["StandardPass"]["basis_singleqs"]) == { + assert set(pz_rebase.to_dict()["StandardPass"]["basis_allowed"]) == { + "CX", "PhasedX", "Rz", } + assert cx.to_dict() == pz_rebase.to_dict()["StandardPass"]["basis_cx_replacement"] # EulerAngleReduction euler_pass = EulerAngleReduction(OpType.Ry, OpType.Rx) diff --git a/schemas/compiler_pass_v1.json b/schemas/compiler_pass_v1.json index 7d4d377fb9..4864df893b 100644 --- a/schemas/compiler_pass_v1.json +++ b/schemas/compiler_pass_v1.json @@ -114,24 +114,18 @@ "type": "string", "description": "The name of the compiler pass. Matches the name of the pytket method used to generate it." }, - "basis_multiqs": { + "basis_allowed": { "type": "array", "items": { "type": "string" }, - "description": "OpTypes of supported multi-qubit gates. Used in \"RebaseCustom\"." + "description": "OpTypes of supported gates. Used in \"RebaseCustom\"." }, "basis_cx_replacement": { "$ref": "file:///circuit_v1.json#", "description": "A circuit implementing a CX gate in a target gate set. Used in \"RebaseCustom\"." }, - "basis_singleqs": { - "type": "array", - "items": { - "type": "string" - }, - "description": "OpTypes of supported single-qubit gates. Used in \"RebaseCustom\" and \"SquashCustom\"." - }, + "basis_tk1_replacement": { "type": "string", "description": "A method for generating optimised single-qubit unitary circuits in a target gate set. This string should be interpreted by Python \"dill\" into a function. Used in \"RebaseCustom\" and \"SquashCustom\"." @@ -247,9 +241,8 @@ }, "then": { "required": [ - "basis_multiqs", + "basis_allowed", "basis_cx_replacement", - "basis_singleqs", "basis_tk1_replacement" ] } diff --git a/tket/src/Predicates/PassGenerators.cpp b/tket/src/Predicates/PassGenerators.cpp index 11f22bd52a..78f12354ee 100644 --- a/tket/src/Predicates/PassGenerators.cpp +++ b/tket/src/Predicates/PassGenerators.cpp @@ -38,16 +38,14 @@ namespace tket { PassPtr gen_rebase_pass( - const OpTypeSet& multiqs, const Circuit& cx_replacement, - const OpTypeSet& singleqs, + const OpTypeSet& allowed_gates, const Circuit& cx_replacement, const std::function& tk1_replacement) { Transform t = Transforms::rebase_factory( - multiqs, cx_replacement, singleqs, tk1_replacement); + allowed_gates, cx_replacement, tk1_replacement); PredicatePtrMap precons; - OpTypeSet all_types(singleqs); - all_types.insert(multiqs.begin(), multiqs.end()); + OpTypeSet all_types(allowed_gates); all_types.insert(OpType::Measure); all_types.insert(OpType::Collapse); all_types.insert(OpType::Reset); @@ -61,9 +59,8 @@ PassPtr gen_rebase_pass( // record pass config nlohmann::json j; j["name"] = "RebaseCustom"; - j["basis_multiqs"] = multiqs; + j["basis_allowed"] = allowed_gates; j["basis_cx_replacement"] = cx_replacement; - j["basis_singleqs"] = singleqs; j["basis_tk1_replacement"] = "SERIALIZATION OF FUNCTIONS IS NOT YET SUPPORTED"; return std::make_shared(precons, t, pc, j); @@ -199,9 +196,10 @@ PassPtr gen_default_mapping_pass(const Architecture& arc, bool delay_measures) { PassPtr gen_cx_mapping_pass( const Architecture& arc, const PlacementPtr& placement_ptr, const RoutingConfig& config, bool directed_cx, bool delay_measures) { - PassPtr rebase_pass = gen_rebase_pass( - {OpType::CX}, CircPool::CX(), all_single_qubit_types(), - Transforms::tk1_to_tk1); + OpTypeSet gate_set = all_single_qubit_types(); + gate_set.insert(OpType::CX); + PassPtr rebase_pass = + gen_rebase_pass(gate_set, CircPool::CX(), Transforms::tk1_to_tk1); PassPtr return_pass = rebase_pass >> gen_full_mapping_pass(arc, placement_ptr, config); @@ -434,10 +432,11 @@ PassPtr gen_full_mapping_pass_phase_poly( PassPtr gen_directed_cx_routing_pass( const Architecture& arc, const RoutingConfig& config) { OpTypeSet multis = {OpType::CX, OpType::BRIDGE, OpType::SWAP}; + OpTypeSet gate_set = all_single_qubit_types(); + gate_set.insert(multis.begin(), multis.end()); + return gen_routing_pass(arc, config) >> - gen_rebase_pass( - multis, CircPool::CX(), all_single_qubit_types(), - Transforms::tk1_to_tk1) >> + gen_rebase_pass(gate_set, CircPool::CX(), Transforms::tk1_to_tk1) >> gen_decompose_routing_gates_to_cxs_pass(arc, true); } diff --git a/tket/src/Predicates/include/Predicates/PassGenerators.hpp b/tket/src/Predicates/include/Predicates/PassGenerators.hpp index ca332668fe..87180862d3 100644 --- a/tket/src/Predicates/include/Predicates/PassGenerators.hpp +++ b/tket/src/Predicates/include/Predicates/PassGenerators.hpp @@ -23,8 +23,7 @@ namespace tket { /* a wrapper method for the rebase_factory in Transforms */ PassPtr gen_rebase_pass( - const OpTypeSet& multiqs, const Circuit& cx_replacement, - const OpTypeSet& singleqs, + const OpTypeSet& allowed_gates, const Circuit& cx_replacement, const std::function& tk1_replacement); diff --git a/tket/src/Transformations/Rebase.cpp b/tket/src/Transformations/Rebase.cpp index 9ede90b192..4a10ad4154 100644 --- a/tket/src/Transformations/Rebase.cpp +++ b/tket/src/Transformations/Rebase.cpp @@ -27,25 +27,23 @@ namespace tket { namespace Transforms { static bool standard_rebase( - Circuit& circ, const OpTypeSet& multiqs, const Circuit& cx_replacement, - const OpTypeSet& singleqs, + Circuit& circ, const OpTypeSet& allowed_gates, const Circuit& cx_replacement, const std::function& tk1_replacement); Transform rebase_factory( - const OpTypeSet& multiqs, const Circuit& cx_replacement, - const OpTypeSet& singleqs, + const OpTypeSet& allowed_gates, const Circuit& cx_replacement, const std::function& tk1_replacement) { return Transform([=](Circuit& circ) { return standard_rebase( - circ, multiqs, cx_replacement, singleqs, tk1_replacement); + circ, allowed_gates, cx_replacement, tk1_replacement); }); } static bool standard_rebase( - Circuit& circ, const OpTypeSet& multiqs, const Circuit& cx_replacement, - const OpTypeSet& singleqs, + Circuit& circ, const OpTypeSet& allowed_gates, + const Circuit& cx_replacement, const std::function& tk1_replacement) { bool success = false; @@ -60,7 +58,7 @@ static bool standard_rebase( op = cond.get_op(); } OpType type = op->get_type(); - if (multiqs.find(type) != multiqs.end() || type == OpType::CX || + if (allowed_gates.find(type) != allowed_gates.end() || type == OpType::CX || type == OpType::Barrier) continue; // need to convert @@ -73,7 +71,7 @@ static bool standard_rebase( bin.push_back(v); success = true; } - if (multiqs.find(OpType::CX) == multiqs.end()) { + if (allowed_gates.find(OpType::CX) == allowed_gates.end()) { const Op_ptr cx_op = get_op_ptr(OpType::CX); success = circ.substitute_all(cx_replacement, cx_op) | success; } @@ -89,7 +87,7 @@ static bool standard_rebase( } OpType type = op->get_type(); if (!is_gate_type(type) || is_projective_type(type) || - singleqs.find(type) != singleqs.end()) + allowed_gates.find(type) != allowed_gates.end()) continue; // need to convert std::vector tk1_angles = as_gate_ptr(op)->get_tk1_angles(); @@ -116,8 +114,7 @@ Transform rebase_tket() { c.add_op(OpType::TK1, {alpha, beta, gamma}, {0}); return c; }; - return rebase_factory( - {OpType::CX}, CircPool::CX(), {OpType::TK1}, tk1_to_tk1); + return rebase_factory({OpType::CX, OpType::TK1}, CircPool::CX(), tk1_to_tk1); } Circuit tk1_to_PhasedXRz( @@ -139,7 +136,7 @@ Circuit tk1_to_PhasedXRz( Transform rebase_cirq() { return rebase_factory( - {OpType::CZ}, CircPool::H_CZ_H(), {OpType::PhasedX, OpType::Rz}, + {OpType::CZ, OpType::PhasedX, OpType::Rz}, CircPool::H_CZ_H(), tk1_to_PhasedXRz); } @@ -256,47 +253,44 @@ Circuit tk1_to_tk1(const Expr& alpha, const Expr& beta, const Expr& gamma) { Transform rebase_HQS() { return rebase_factory( - {OpType::ZZMax}, CircPool::CX_using_ZZMax(), - {OpType::PhasedX, OpType::Rz}, tk1_to_PhasedXRz); + {OpType::ZZMax, OpType::PhasedX, OpType::Rz}, CircPool::CX_using_ZZMax(), + tk1_to_PhasedXRz); } Transform rebase_UMD() { return rebase_factory( - {OpType::XXPhase}, CircPool::CX_using_XXPhase_0(), - {OpType::PhasedX, OpType::Rz}, tk1_to_PhasedXRz); + {OpType::XXPhase, OpType::PhasedX, OpType::Rz}, + CircPool::CX_using_XXPhase_0(), tk1_to_PhasedXRz); } Transform rebase_quil() { return rebase_factory( - {OpType::CZ}, CircPool::H_CZ_H(), {OpType::Rx, OpType::Rz}, tk1_to_rzrx); + {OpType::CZ, OpType::Rx, OpType::Rz}, CircPool::H_CZ_H(), tk1_to_rzrx); } Transform rebase_pyzx() { - OpTypeSet pyzx_multiqs = {OpType::SWAP, OpType::CX, OpType::CZ}; - OpTypeSet pyzx_singleqs = {OpType::H, OpType::X, OpType::Z, OpType::S, - OpType::T, OpType::Rx, OpType::Rz}; - return rebase_factory( - pyzx_multiqs, CircPool::CX(), pyzx_singleqs, tk1_to_rzrx); + OpTypeSet pyzx_gates = {OpType::SWAP, OpType::CX, OpType::CZ, OpType::H, + OpType::X, OpType::Z, OpType::S, OpType::T, + OpType::Rx, OpType::Rz}; + return rebase_factory(pyzx_gates, CircPool::CX(), tk1_to_rzrx); } Transform rebase_projectq() { - OpTypeSet projectq_multiqs = { - OpType::SWAP, OpType::CRz, OpType::CX, OpType::CZ}; - OpTypeSet projectq_singleqs = {OpType::H, OpType::X, OpType::Y, OpType::Z, - OpType::S, OpType::T, OpType::V, OpType::Rx, - OpType::Ry, OpType::Rz}; - return rebase_factory( - projectq_multiqs, CircPool::CX(), projectq_singleqs, tk1_to_rzrx); + OpTypeSet projectq_gates = {OpType::SWAP, OpType::CRz, OpType::CX, OpType::CZ, + OpType::H, OpType::X, OpType::Y, OpType::Z, + OpType::S, OpType::T, OpType::V, OpType::Rx, + OpType::Ry, OpType::Rz}; + return rebase_factory(projectq_gates, CircPool::CX(), tk1_to_rzrx); } Transform rebase_UFR() { return rebase_factory( - {OpType::CX}, CircPool::CX(), {OpType::Rz, OpType::H}, tk1_to_rzh); + {OpType::CX, OpType::Rz, OpType::H}, CircPool::CX(), tk1_to_rzh); } Transform rebase_OQC() { return rebase_factory( - {OpType::ECR}, CircPool::CX_using_ECR(), {OpType::Rz, OpType::SX}, + {OpType::ECR, OpType::Rz, OpType::SX}, CircPool::CX_using_ECR(), tk1_to_rzsx); } diff --git a/tket/src/Transformations/include/Transformations/Rebase.hpp b/tket/src/Transformations/include/Transformations/Rebase.hpp index f5a185bfe7..635d71dca0 100644 --- a/tket/src/Transformations/include/Transformations/Rebase.hpp +++ b/tket/src/Transformations/include/Transformations/Rebase.hpp @@ -23,10 +23,9 @@ namespace Transforms { // decomposes multiq gates not in the gate set to CXs, then replaces CXs with // the replacement (if CX is not allowed) then converts singleq gates no in // the gate set to U3 and replaces them using provided function Expects: any -// gates Produces: gates in multiqs and singleqs +// gates Produces: gates in allowed_gates Transform rebase_factory( - const OpTypeSet& multiqs, const Circuit& cx_replacement, - const OpTypeSet& singleqs, + const OpTypeSet& allowed_gates, const Circuit& cx_replacement, const std::function& tk1_replacement); diff --git a/tket/tests/test_CompilerPass.cpp b/tket/tests/test_CompilerPass.cpp index 52ef767111..10e2c4d2d9 100644 --- a/tket/tests/test_CompilerPass.cpp +++ b/tket/tests/test_CompilerPass.cpp @@ -216,7 +216,7 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { Circuit cx(2); cx.add_op(OpType::CX, {0, 1}); PassPtr pz_rebase = gen_rebase_pass( - {OpType::CX}, cx, {OpType::PhasedX, OpType::Rz}, + {OpType::CX, OpType::PhasedX, OpType::Rz}, cx, Transforms::tk1_to_PhasedXRz); PassPtr all_passes = SynthesiseTket() >> cp_route >> pz_rebase; @@ -1046,8 +1046,9 @@ SCENARIO("CX mapping pass") { PlacementPtr placer = std::make_shared(line); Circuit cx(2); cx.add_op(OpType::CX, {0, 1}); - PassPtr rebase = gen_rebase_pass( - {OpType::CX}, cx, all_single_qubit_types(), Transforms::tk1_to_tk1); + OpTypeSet gateset = all_single_qubit_types(); + gateset.insert(OpType::CX); + PassPtr rebase = gen_rebase_pass(gateset, cx, Transforms::tk1_to_tk1); // Circuit mapping basis states to basis states Circuit c(3); diff --git a/tket/tests/test_Rebase.cpp b/tket/tests/test_Rebase.cpp index 18c0091869..0f496cee9f 100644 --- a/tket/tests/test_Rebase.cpp +++ b/tket/tests/test_Rebase.cpp @@ -41,9 +41,8 @@ SCENARIO("Building rebases with rebase_factory") { auto blanker = [](const Expr&, const Expr&, const Expr&) { return Circuit(1); }; - OpTypeSet multiqs = {OpType::CX}; - OpTypeSet singleqs = {OpType::S, OpType::V, OpType::Rx}; - Transform t = Transforms::rebase_factory(multiqs, blank, singleqs, blanker); + OpTypeSet gates = {OpType::S, OpType::V, OpType::Rx, OpType::CX}; + Transform t = Transforms::rebase_factory(gates, blank, blanker); REQUIRE(!t.apply(c)); REQUIRE(copy == c); } @@ -57,9 +56,8 @@ SCENARIO("Building rebases with rebase_factory") { auto blanker = [](const Expr&, const Expr&, const Expr&) { return Circuit(1); }; - OpTypeSet multiqs = {OpType::CX}; - OpTypeSet singleqs = {OpType::S, OpType::V, OpType::H}; - Transform t = Transforms::rebase_factory(multiqs, blank, singleqs, blanker); + Transform t = Transforms::rebase_factory( + {OpType::S, OpType::V, OpType::H, OpType::CX}, blank, blanker); REQUIRE(t.apply(c)); REQUIRE(c.count_gates(OpType::CZ) == 0); REQUIRE(c.count_gates(OpType::CX) == 1); @@ -79,9 +77,8 @@ SCENARIO("Building rebases with rebase_factory") { auto blanker = [](const Expr&, const Expr&, const Expr&) { return Circuit(1); }; - OpTypeSet multiqs = {OpType::CRz}; - OpTypeSet singleqs = {OpType::S, OpType::V, OpType::H}; - Transform t = Transforms::rebase_factory(multiqs, cx, singleqs, blanker); + Transform t = Transforms::rebase_factory( + {OpType::S, OpType::V, OpType::H, OpType::CRz}, cx, blanker); REQUIRE(t.apply(c)); REQUIRE(c.count_gates(OpType::CZ) == 1); REQUIRE(c.count_gates(OpType::CX) == 0); @@ -101,9 +98,9 @@ SCENARIO("Building rebases with rebase_factory") { auto blanker = [](const Expr&, const Expr&, const Expr&) { return Circuit(1); }; - OpTypeSet multiqs = {OpType::CZ}; - OpTypeSet singleqs = {OpType::S, OpType::X, OpType::H, OpType::Sdg}; - Transform t = Transforms::rebase_factory(multiqs, cx, singleqs, blanker); + OpTypeSet gateset = { + OpType::S, OpType::X, OpType::H, OpType::Sdg, OpType::CZ}; + Transform t = Transforms::rebase_factory(gateset, cx, blanker); REQUIRE(t.apply(c)); REQUIRE(c.count_gates(OpType::CZ) == 1); REQUIRE(c.count_gates(OpType::CX) == 0); @@ -123,9 +120,8 @@ SCENARIO("Building rebases with rebase_factory") { auto blanker = [](const Expr&, const Expr&, const Expr&) { return Circuit(1); }; - OpTypeSet multiqs = {OpType::CX}; - OpTypeSet singleqs = {OpType::S, OpType::V, OpType::H}; - Transform t = Transforms::rebase_factory(multiqs, blank, singleqs, blanker); + OpTypeSet gateset = {OpType::S, OpType::V, OpType::H, OpType::CX}; + Transform t = Transforms::rebase_factory(gateset, blank, blanker); REQUIRE(t.apply(c)); REQUIRE(c.count_gates(OpType::CZ) == 0); REQUIRE(c.count_gates(OpType::CX) == 6); @@ -143,9 +139,8 @@ SCENARIO("Building rebases with rebase_factory") { auto blanker = [](const Expr&, const Expr&, const Expr&) { return Circuit(1); }; - OpTypeSet multiqs = {OpType::CX}; - OpTypeSet singleqs = {OpType::S, OpType::V, OpType::H}; - Transform t = Transforms::rebase_factory(multiqs, blank, singleqs, blanker); + OpTypeSet gateset = {OpType::S, OpType::V, OpType::H, OpType::CX}; + Transform t = Transforms::rebase_factory(gateset, blank, blanker); REQUIRE(t.apply(c)); REQUIRE(c.count_gates(OpType::CX) == 4); const StateVector s1 = tket_sim::get_statevector(c); @@ -163,9 +158,8 @@ SCENARIO("Building rebases with rebase_factory") { auto blanker = [](const Expr&, const Expr&, const Expr&) { return Circuit(1); }; - OpTypeSet multiqs = {OpType::CX}; - OpTypeSet singleqs = {OpType::S, OpType::V, OpType::H}; - Transform t = Transforms::rebase_factory(multiqs, blank, singleqs, blanker); + OpTypeSet gateset = {OpType::S, OpType::V, OpType::H, OpType::CX}; + Transform t = Transforms::rebase_factory(gateset, blank, blanker); REQUIRE(t.apply(c)); REQUIRE(c.count_gates(OpType::CX) == 4); StateVector s1 = tket_sim::get_statevector(c); @@ -187,9 +181,8 @@ SCENARIO("Building rebases with rebase_factory") { auto blanker = [](const Expr&, const Expr&, const Expr&) { return Circuit(1); }; - OpTypeSet multiqs = {OpType::CX}; - OpTypeSet singleqs = {OpType::TK1}; - Transform t = Transforms::rebase_factory(multiqs, blank, singleqs, tk1_map); + OpTypeSet gateset = {OpType::TK1, OpType::CX}; + Transform t = Transforms::rebase_factory(gateset, blank, tk1_map); REQUIRE(t.apply(c)); REQUIRE(c.count_gates(OpType::T) == 0); REQUIRE(c.count_gates(OpType::Rx) == 0); @@ -212,10 +205,8 @@ SCENARIO("Building rebases with rebase_factory") { u.add_op(OpType::Rz, alpha, {0}); return u; }; - OpTypeSet multiqs = {OpType::CX}; - OpTypeSet singleqs = {OpType::Rz, OpType::Rx}; - Transform t = - Transforms::rebase_factory(multiqs, blank, singleqs, rzrx_map); + OpTypeSet gateset = {OpType::Rz, OpType::Rx, OpType::CX}; + Transform t = Transforms::rebase_factory(gateset, blank, rzrx_map); REQUIRE(t.apply(c)); REQUIRE(c.count_gates(OpType::U3) == 0); REQUIRE(c.count_gates(OpType::Rx) == 2); @@ -238,10 +229,8 @@ SCENARIO("Building rebases with rebase_factory") { Transforms::remove_redundancies().apply(u); return u; }; - OpTypeSet multiqs = {OpType::CX}; - OpTypeSet singleqs = {OpType::Rz, OpType::Rx}; - Transform t = - Transforms::rebase_factory(multiqs, blank, singleqs, rzrx_map); + OpTypeSet gateset = {OpType::Rz, OpType::Rx, OpType::CX}; + Transform t = Transforms::rebase_factory(gateset, blank, rzrx_map); REQUIRE(t.apply(c)); REQUIRE(c.count_gates(OpType::T) == 0); REQUIRE(c.count_gates(OpType::U3) == 0); From 550d13c166e090423bc08d1c2965d73ffda82443 Mon Sep 17 00:00:00 2001 From: seyon Date: Fri, 11 Feb 2022 17:28:43 +0000 Subject: [PATCH 2/6] move tk1 circuit functions to CircPool conceptually matches the other parametrised gate circuit definitions --- tket/src/Circuit/CircPool.cpp | 124 ++++++++++++++ tket/src/Circuit/include/Circuit/CircPool.hpp | 12 ++ tket/src/Predicates/PassGenerators.cpp | 4 +- tket/src/Predicates/PassLibrary.cpp | 3 +- tket/src/Transformations/Decomposition.cpp | 3 +- tket/src/Transformations/OptimisationPass.cpp | 6 +- tket/src/Transformations/Rebase.cpp | 156 +++--------------- .../include/Transformations/Rebase.hpp | 12 -- tket/tests/test_CompilerPass.cpp | 5 +- tket/tests/test_Rebase.cpp | 11 +- tket/tests/test_Synthesis.cpp | 30 ++-- 11 files changed, 192 insertions(+), 174 deletions(-) diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index e6b03fc771..93b69699f5 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -786,6 +786,130 @@ Circuit NPhasedX_using_CX( return c; } +static unsigned int_half(const Expr &angle) { + // Assume angle is an even integer + double eval = eval_expr(angle).value(); + return lround(eval / 2); +} + +Circuit tk1_to_rzsx(const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c(1); + Expr correction_phase = 0; + if (equiv_0(beta)) { + // b = 2k, if k is odd, then Rx(b) = -I + c.add_op(OpType::Rz, alpha + gamma, {0}); + correction_phase = int_half(beta); + } else if (equiv_0(beta + 1)) { + // Use Rx(2k-1) = i(-1)^{k}SxSx + correction_phase = -0.5 + int_half(beta - 1); + if (equiv_0(alpha - gamma)) { + // a - c = 2m + // overall operation is (-1)^{m}Rx(2k -1) + c.add_op(OpType::SX, {0}); + c.add_op(OpType::SX, {0}); + correction_phase += int_half(alpha - gamma); + } else { + c.add_op(OpType::Rz, gamma, {0}); + c.add_op(OpType::SX, {0}); + c.add_op(OpType::SX, {0}); + c.add_op(OpType::Rz, alpha, {0}); + } + } else if (equiv_0(beta - 0.5) && equiv_0(alpha) && equiv_0(gamma)) { + // a = 2k, b = 2m+0.5, c = 2n + // Rz(2k)Rx(2m + 0.5)Rz(2n) = (-1)^{k+m+n}e^{-i \pi /4} SX + c.add_op(OpType::SX, {0}); + correction_phase = + int_half(beta - 0.5) + int_half(alpha) + int_half(gamma) - 0.25; + } else if (equiv_0(alpha - 0.5) && equiv_0(gamma - 0.5)) { + // Rz(2k + 0.5)Rx(b)Rz(2m + 0.5) = -i(-1)^{k+m}SX.Rz(1-b).SX + c.add_op(OpType::SX, {0}); + c.add_op(OpType::Rz, 1 - beta, {0}); + c.add_op(OpType::SX, {0}); + correction_phase = int_half(alpha - 0.5) + int_half(gamma - 0.5) - 0.5; + } else { + c.add_op(OpType::Rz, gamma + 0.5, {0}); + c.add_op(OpType::SX, {0}); + c.add_op(OpType::Rz, beta - 1, {0}); + c.add_op(OpType::SX, {0}); + c.add_op(OpType::Rz, alpha + 0.5, {0}); + correction_phase = -0.5; + } + c.add_phase(correction_phase); + return c; +} + +Circuit tk1_to_rzh(const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c(1); + std::optional cliff = equiv_Clifford(beta, 4); + if (cliff) { + switch (*cliff % 4) { + case 0: { + c.add_op(OpType::Rz, gamma + alpha, {0}); + break; + } + case 1: { + c.add_op(OpType::Rz, gamma - 0.5, {0}); + c.add_op(OpType::H, {0}); + c.add_op(OpType::Rz, alpha - 0.5, {0}); + c.add_phase(-0.5); + break; + } + case 2: { + c.add_op(OpType::Rz, gamma - alpha, {0}); + c.add_op(OpType::H, {0}); + c.add_op(OpType::Rz, 1., {0}); + c.add_op(OpType::H, {0}); + break; + } + case 3: { + c.add_op(OpType::Rz, gamma + 0.5, {0}); + c.add_op(OpType::H, {0}); + c.add_op(OpType::Rz, alpha + 0.5, {0}); + c.add_phase(-0.5); + break; + } + } + if (cliff >= 4u) c.add_phase(1.); + } else { + c.add_op(OpType::Rz, gamma, {0}); + c.add_op(OpType::H, {0}); + c.add_op(OpType::Rz, beta, {0}); + c.add_op(OpType::H, {0}); + c.add_op(OpType::Rz, alpha, {0}); + } + return c; +} + +Circuit tk1_to_tk1(const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c(1); + c.add_op(OpType::TK1, {alpha, beta, gamma}, {0}); + return c; +} + +Circuit tk1_to_rzrx(const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c(1); + c.add_op(OpType::Rz, gamma, {0}); + c.add_op(OpType::Rx, beta, {0}); + c.add_op(OpType::Rz, alpha, {0}); + return c; +} + +Circuit tk1_to_PhasedXRz( + const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c(1); + if (equiv_expr(beta, 1)) { + // Angles β ∈ {π, 3π} + c.add_op(OpType::PhasedX, {beta, (alpha - gamma) / 2.}, {0}); + } else if (equiv_expr(beta, 0)) { + // Angle β ∈ {0, 2π} + c.add_op(OpType::Rz, alpha + beta + gamma, {0}); + } else { + c.add_op(OpType::Rz, alpha + gamma, {0}); + c.add_op(OpType::PhasedX, {beta, alpha}, {0}); + } + return c; +} + } // namespace CircPool } // namespace tket diff --git a/tket/src/Circuit/include/Circuit/CircPool.hpp b/tket/src/Circuit/include/Circuit/CircPool.hpp index f07fe6620f..5d874fb130 100644 --- a/tket/src/Circuit/include/Circuit/CircPool.hpp +++ b/tket/src/Circuit/include/Circuit/CircPool.hpp @@ -225,6 +225,18 @@ Circuit PhasedISWAP_using_CX(Expr p, Expr t); /** Unwrap NPhasedX, into number_of_qubits PhasedX gates */ Circuit NPhasedX_using_CX(unsigned int number_of_qubits, Expr alpha, Expr beta); +// converts a TK1 gate to a PhasedXRz gate +Circuit tk1_to_PhasedXRz( + const Expr &alpha, const Expr &beta, const Expr &gamma); + +Circuit tk1_to_rzrx(const Expr &alpha, const Expr &beta, const Expr &gamma); + +Circuit tk1_to_rzh(const Expr &alpha, const Expr &beta, const Expr &gamma); + +Circuit tk1_to_rzsx(const Expr &alpha, const Expr &beta, const Expr &gamma); + +Circuit tk1_to_tk1(const Expr &alpha, const Expr &beta, const Expr &gamma); + } // namespace CircPool } // namespace tket diff --git a/tket/src/Predicates/PassGenerators.cpp b/tket/src/Predicates/PassGenerators.cpp index 78f12354ee..1789943aed 100644 --- a/tket/src/Predicates/PassGenerators.cpp +++ b/tket/src/Predicates/PassGenerators.cpp @@ -199,7 +199,7 @@ PassPtr gen_cx_mapping_pass( OpTypeSet gate_set = all_single_qubit_types(); gate_set.insert(OpType::CX); PassPtr rebase_pass = - gen_rebase_pass(gate_set, CircPool::CX(), Transforms::tk1_to_tk1); + gen_rebase_pass(gate_set, CircPool::CX(), CircPool::tk1_to_tk1); PassPtr return_pass = rebase_pass >> gen_full_mapping_pass(arc, placement_ptr, config); @@ -436,7 +436,7 @@ PassPtr gen_directed_cx_routing_pass( gate_set.insert(multis.begin(), multis.end()); return gen_routing_pass(arc, config) >> - gen_rebase_pass(gate_set, CircPool::CX(), Transforms::tk1_to_tk1) >> + gen_rebase_pass(gate_set, CircPool::CX(), CircPool::tk1_to_tk1) >> gen_decompose_routing_gates_to_cxs_pass(arc, true); } diff --git a/tket/src/Predicates/PassLibrary.cpp b/tket/src/Predicates/PassLibrary.cpp index e964696c57..fc1f0d95d1 100644 --- a/tket/src/Predicates/PassLibrary.cpp +++ b/tket/src/Predicates/PassLibrary.cpp @@ -16,6 +16,7 @@ #include +#include "Circuit/CircPool.hpp" #include "PassGenerators.hpp" #include "Predicates/CompilerPass.hpp" #include "Transformations/BasicOptimisation.hpp" @@ -354,7 +355,7 @@ const PassPtr &SquashTK1() { const PassPtr &SquashHQS() { static const PassPtr pp([]() { return gen_squash_pass( - {OpType::Rz, OpType::PhasedX}, Transforms::tk1_to_PhasedXRz); + {OpType::Rz, OpType::PhasedX}, CircPool::tk1_to_PhasedXRz); }()); return pp; } diff --git a/tket/src/Transformations/Decomposition.cpp b/tket/src/Transformations/Decomposition.cpp index ed63b20317..511bd5caf9 100644 --- a/tket/src/Transformations/Decomposition.cpp +++ b/tket/src/Transformations/Decomposition.cpp @@ -240,7 +240,8 @@ Transform decompose_tk1_to_rzrx() { success = true; const Op_ptr g = circ.get_Op_ptr_from_Vertex(*it); const std::vector ¶ms = g->get_params(); - Circuit newcirc = tk1_to_rzrx(params[0], params[1], params[2]); + Circuit newcirc = + CircPool::tk1_to_rzrx(params[0], params[1], params[2]); Subcircuit sc = { {circ.get_in_edges(*it)}, {circ.get_all_out_edges(*it)}, {*it}}; circ.substitute(newcirc, sc, Circuit::VertexDeletion::Yes); diff --git a/tket/src/Transformations/OptimisationPass.cpp b/tket/src/Transformations/OptimisationPass.cpp index f4bcde28d2..fa9cbe65c0 100644 --- a/tket/src/Transformations/OptimisationPass.cpp +++ b/tket/src/Transformations/OptimisationPass.cpp @@ -15,6 +15,7 @@ #include "OptimisationPass.hpp" #include "BasicOptimisation.hpp" +#include "Circuit/CircPool.hpp" #include "Circuit/CircUtils.hpp" #include "CliffordOptimisation.hpp" #include "CliffordReductionPass.hpp" @@ -136,8 +137,9 @@ Transform synthesise_UMD() { OpType type = op_ptr->get_type(); if (type == OpType::TK1) { std::vector tk1_angles = as_gate_ptr(op_ptr)->get_tk1_angles(); - Circuit in_circ = - tk1_to_PhasedXRz(tk1_angles[0], tk1_angles[1], tk1_angles[2]); + Circuit in_circ = CircPool::tk1_to_PhasedXRz( + tk1_angles[0], tk1_angles[1], tk1_angles[2]); + remove_redundancies().apply(in_circ); Subcircuit sub = { {circ.get_in_edges(v)}, {circ.get_all_out_edges(v)}, {v}}; bin.push_back(v); diff --git a/tket/src/Transformations/Rebase.cpp b/tket/src/Transformations/Rebase.cpp index 4a10ad4154..3276905841 100644 --- a/tket/src/Transformations/Rebase.cpp +++ b/tket/src/Transformations/Rebase.cpp @@ -27,7 +27,8 @@ namespace tket { namespace Transforms { static bool standard_rebase( - Circuit& circ, const OpTypeSet& allowed_gates, const Circuit& cx_replacement, + Circuit& circ, const OpTypeSet& allowed_gates, + const Circuit& cx_replacement, const std::function& tk1_replacement); @@ -117,162 +118,45 @@ Transform rebase_tket() { return rebase_factory({OpType::CX, OpType::TK1}, CircPool::CX(), tk1_to_tk1); } -Circuit tk1_to_PhasedXRz( - const Expr& alpha, const Expr& beta, const Expr& gamma) { - Circuit c(1); - if (equiv_expr(beta, 1)) { - // Angles β ∈ {π, 3π} - c.add_op(OpType::PhasedX, {beta, (alpha - gamma) / 2.}, {0}); - } else if (equiv_expr(beta, 0)) { - // Angle β ∈ {0, 2π} - c.add_op(OpType::Rz, alpha + beta + gamma, {0}); - } else { - c.add_op(OpType::Rz, alpha + gamma, {0}); - c.add_op(OpType::PhasedX, {beta, alpha}, {0}); +// apply redundancy removal to circuit returned by function +#define WRAP_REDUNDANCY(func) \ + [](const Expr& alpha, const Expr& beta, const Expr& gamma) { \ + Circuit c = func(alpha, beta, gamma); \ + remove_redundancies().apply(c); \ + return c; \ } - remove_redundancies().apply(c); - return c; -} Transform rebase_cirq() { return rebase_factory( {OpType::CZ, OpType::PhasedX, OpType::Rz}, CircPool::H_CZ_H(), - tk1_to_PhasedXRz); -} - -Circuit tk1_to_rzrx(const Expr& alpha, const Expr& beta, const Expr& gamma) { - Circuit c(1); - c.add_op(OpType::Rz, gamma, {0}); - c.add_op(OpType::Rx, beta, {0}); - c.add_op(OpType::Rz, alpha, {0}); - remove_redundancies().apply(c); - return c; -} - -Circuit tk1_to_rzh(const Expr& alpha, const Expr& beta, const Expr& gamma) { - Circuit c(1); - std::optional cliff = equiv_Clifford(beta, 4); - if (cliff) { - switch (*cliff % 4) { - case 0: { - c.add_op(OpType::Rz, gamma + alpha, {0}); - break; - } - case 1: { - c.add_op(OpType::Rz, gamma - 0.5, {0}); - c.add_op(OpType::H, {0}); - c.add_op(OpType::Rz, alpha - 0.5, {0}); - c.add_phase(-0.5); - break; - } - case 2: { - c.add_op(OpType::Rz, gamma - alpha, {0}); - c.add_op(OpType::H, {0}); - c.add_op(OpType::Rz, 1., {0}); - c.add_op(OpType::H, {0}); - break; - } - case 3: { - c.add_op(OpType::Rz, gamma + 0.5, {0}); - c.add_op(OpType::H, {0}); - c.add_op(OpType::Rz, alpha + 0.5, {0}); - c.add_phase(-0.5); - break; - } - } - if (cliff >= 4u) c.add_phase(1.); - } else { - c.add_op(OpType::Rz, gamma, {0}); - c.add_op(OpType::H, {0}); - c.add_op(OpType::Rz, beta, {0}); - c.add_op(OpType::H, {0}); - c.add_op(OpType::Rz, alpha, {0}); - } - remove_redundancies().apply(c); - return c; -} - -static unsigned int_half(const Expr& angle) { - // Assume angle is an even integer - double eval = eval_expr(angle).value(); - return lround(eval / 2); -} - -Circuit tk1_to_rzsx(const Expr& alpha, const Expr& beta, const Expr& gamma) { - Circuit c(1); - Expr correction_phase = 0; - if (equiv_0(beta)) { - // b = 2k, if k is odd, then Rx(b) = -I - c.add_op(OpType::Rz, alpha + gamma, {0}); - correction_phase = int_half(beta); - } else if (equiv_0(beta + 1)) { - // Use Rx(2k-1) = i(-1)^{k}SxSx - correction_phase = -0.5 + int_half(beta - 1); - if (equiv_0(alpha - gamma)) { - // a - c = 2m - // overall operation is (-1)^{m}Rx(2k -1) - c.add_op(OpType::SX, {0}); - c.add_op(OpType::SX, {0}); - correction_phase += int_half(alpha - gamma); - } else { - c.add_op(OpType::Rz, gamma, {0}); - c.add_op(OpType::SX, {0}); - c.add_op(OpType::SX, {0}); - c.add_op(OpType::Rz, alpha, {0}); - } - } else if (equiv_0(beta - 0.5) && equiv_0(alpha) && equiv_0(gamma)) { - // a = 2k, b = 2m+0.5, c = 2n - // Rz(2k)Rx(2m + 0.5)Rz(2n) = (-1)^{k+m+n}e^{-i \pi /4} SX - c.add_op(OpType::SX, {0}); - correction_phase = - int_half(beta - 0.5) + int_half(alpha) + int_half(gamma) - 0.25; - } else if (equiv_0(alpha - 0.5) && equiv_0(gamma - 0.5)) { - // Rz(2k + 0.5)Rx(b)Rz(2m + 0.5) = -i(-1)^{k+m}SX.Rz(1-b).SX - c.add_op(OpType::SX, {0}); - c.add_op(OpType::Rz, 1 - beta, {0}); - c.add_op(OpType::SX, {0}); - correction_phase = int_half(alpha - 0.5) + int_half(gamma - 0.5) - 0.5; - } else { - c.add_op(OpType::Rz, gamma + 0.5, {0}); - c.add_op(OpType::SX, {0}); - c.add_op(OpType::Rz, beta - 1, {0}); - c.add_op(OpType::SX, {0}); - c.add_op(OpType::Rz, alpha + 0.5, {0}); - correction_phase = -0.5; - } - c.add_phase(correction_phase); - remove_redundancies().apply(c); - return c; -} - -Circuit tk1_to_tk1(const Expr& alpha, const Expr& beta, const Expr& gamma) { - Circuit c(1); - c.add_op(OpType::TK1, {alpha, beta, gamma}, {0}); - return c; + WRAP_REDUNDANCY(CircPool::tk1_to_PhasedXRz)); } Transform rebase_HQS() { return rebase_factory( {OpType::ZZMax, OpType::PhasedX, OpType::Rz}, CircPool::CX_using_ZZMax(), - tk1_to_PhasedXRz); + WRAP_REDUNDANCY(CircPool::tk1_to_PhasedXRz)); } Transform rebase_UMD() { return rebase_factory( {OpType::XXPhase, OpType::PhasedX, OpType::Rz}, - CircPool::CX_using_XXPhase_0(), tk1_to_PhasedXRz); + CircPool::CX_using_XXPhase_0(), + WRAP_REDUNDANCY(CircPool::tk1_to_PhasedXRz)); } Transform rebase_quil() { return rebase_factory( - {OpType::CZ, OpType::Rx, OpType::Rz}, CircPool::H_CZ_H(), tk1_to_rzrx); + {OpType::CZ, OpType::Rx, OpType::Rz}, CircPool::H_CZ_H(), + WRAP_REDUNDANCY(CircPool::tk1_to_rzrx)); } Transform rebase_pyzx() { OpTypeSet pyzx_gates = {OpType::SWAP, OpType::CX, OpType::CZ, OpType::H, OpType::X, OpType::Z, OpType::S, OpType::T, OpType::Rx, OpType::Rz}; - return rebase_factory(pyzx_gates, CircPool::CX(), tk1_to_rzrx); + return rebase_factory( + pyzx_gates, CircPool::CX(), WRAP_REDUNDANCY(CircPool::tk1_to_rzrx)); } Transform rebase_projectq() { @@ -280,18 +164,20 @@ Transform rebase_projectq() { OpType::H, OpType::X, OpType::Y, OpType::Z, OpType::S, OpType::T, OpType::V, OpType::Rx, OpType::Ry, OpType::Rz}; - return rebase_factory(projectq_gates, CircPool::CX(), tk1_to_rzrx); + return rebase_factory( + projectq_gates, CircPool::CX(), WRAP_REDUNDANCY(CircPool::tk1_to_rzrx)); } Transform rebase_UFR() { return rebase_factory( - {OpType::CX, OpType::Rz, OpType::H}, CircPool::CX(), tk1_to_rzh); + {OpType::CX, OpType::Rz, OpType::H}, CircPool::CX(), + WRAP_REDUNDANCY(CircPool::tk1_to_rzh)); } Transform rebase_OQC() { return rebase_factory( {OpType::ECR, OpType::Rz, OpType::SX}, CircPool::CX_using_ECR(), - tk1_to_rzsx); + WRAP_REDUNDANCY(CircPool::tk1_to_rzsx)); } } // namespace Transforms diff --git a/tket/src/Transformations/include/Transformations/Rebase.hpp b/tket/src/Transformations/include/Transformations/Rebase.hpp index 635d71dca0..ff0bca2095 100644 --- a/tket/src/Transformations/include/Transformations/Rebase.hpp +++ b/tket/src/Transformations/include/Transformations/Rebase.hpp @@ -66,18 +66,6 @@ Transform rebase_UFR(); // Singleqs: Rz, SX Transform rebase_OQC(); -// converts a TK1 gate to a PhasedXRz gate -Circuit tk1_to_PhasedXRz( - const Expr& alpha, const Expr& beta, const Expr& gamma); - -Circuit tk1_to_rzrx(const Expr& alpha, const Expr& beta, const Expr& gamma); - -Circuit tk1_to_rzh(const Expr& alpha, const Expr& beta, const Expr& gamma); - -Circuit tk1_to_rzsx(const Expr& alpha, const Expr& beta, const Expr& gamma); - -Circuit tk1_to_tk1(const Expr& alpha, const Expr& beta, const Expr& gamma); - } // namespace Transforms } // namespace tket diff --git a/tket/tests/test_CompilerPass.cpp b/tket/tests/test_CompilerPass.cpp index 10e2c4d2d9..c7c92d7fab 100644 --- a/tket/tests/test_CompilerPass.cpp +++ b/tket/tests/test_CompilerPass.cpp @@ -15,6 +15,7 @@ #include #include +#include "Circuit/CircPool.hpp" #include "Circuit/Circuit.hpp" #include "OpType/OpType.hpp" #include "OpType/OpTypeFunctions.hpp" @@ -217,7 +218,7 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { cx.add_op(OpType::CX, {0, 1}); PassPtr pz_rebase = gen_rebase_pass( {OpType::CX, OpType::PhasedX, OpType::Rz}, cx, - Transforms::tk1_to_PhasedXRz); + CircPool::tk1_to_PhasedXRz); PassPtr all_passes = SynthesiseTket() >> cp_route >> pz_rebase; REQUIRE(all_passes->apply(cu)); @@ -1048,7 +1049,7 @@ SCENARIO("CX mapping pass") { cx.add_op(OpType::CX, {0, 1}); OpTypeSet gateset = all_single_qubit_types(); gateset.insert(OpType::CX); - PassPtr rebase = gen_rebase_pass(gateset, cx, Transforms::tk1_to_tk1); + PassPtr rebase = gen_rebase_pass(gateset, cx, CircPool::tk1_to_tk1); // Circuit mapping basis states to basis states Circuit c(3); diff --git a/tket/tests/test_Rebase.cpp b/tket/tests/test_Rebase.cpp index 0f496cee9f..be270515ac 100644 --- a/tket/tests/test_Rebase.cpp +++ b/tket/tests/test_Rebase.cpp @@ -16,6 +16,7 @@ #include #include "Circuit/Boxes.hpp" +#include "Circuit/CircPool.hpp" #include "CircuitsForTesting.hpp" #include "Simulation/CircuitSimulator.hpp" #include "Simulation/ComparisonFunctions.hpp" @@ -401,8 +402,7 @@ SCENARIO("Check each Clifford case for tk1_to_rzh") { Circuit correct(1); correct.add_op( OpType::TK1, {test.alpha, test.beta, test.gamma}, {0}); - Circuit result = - Transforms::tk1_to_rzh(test.alpha, test.beta, test.gamma); + Circuit result = CircPool::tk1_to_rzh(test.alpha, test.beta, test.gamma); REQUIRE(result.n_gates() == test.expected_gates); REQUIRE(test_unitary_comparison(correct, result)); } @@ -455,8 +455,8 @@ SCENARIO("Check cases for tk1_to_rzsx") { Circuit correct(1); correct.add_op( OpType::TK1, {test.alpha, test.beta, test.gamma}, {0}); - Circuit result = - Transforms::tk1_to_rzsx(test.alpha, test.beta, test.gamma); + Circuit result = CircPool::tk1_to_rzsx(test.alpha, test.beta, test.gamma); + Transforms::remove_redundancies().apply(result); REQUIRE(result.n_gates() == test.expected_gates); REQUIRE(test_unitary_comparison(correct, result)); } @@ -465,8 +465,7 @@ SCENARIO("Check cases for tk1_to_rzsx") { Circuit correct(1); correct.add_op( OpType::TK1, {test.alpha, test.beta, test.gamma}, {0}); - Circuit result = - Transforms::tk1_to_rzsx(test.alpha, test.beta, test.gamma); + Circuit result = CircPool::tk1_to_rzsx(test.alpha, test.beta, test.gamma); REQUIRE(result.n_gates() == test.expected_gates); } } diff --git a/tket/tests/test_Synthesis.cpp b/tket/tests/test_Synthesis.cpp index 5bc342d74c..a8ec7cb7ac 100644 --- a/tket/tests/test_Synthesis.cpp +++ b/tket/tests/test_Synthesis.cpp @@ -16,6 +16,7 @@ #include #include +#include "Circuit/CircPool.hpp" #include "Circuit/CircUtils.hpp" #include "CircuitsForTesting.hpp" #include "Gate/Rotation.hpp" @@ -705,16 +706,16 @@ SCENARIO("Testing general 1qb squash") { Circuit copy = circ; OpTypeSet singleqs = {OpType::Rz, OpType::PhasedX}; bool success = - Transforms::squash_factory(singleqs, Transforms::tk1_to_PhasedXRz) + Transforms::squash_factory(singleqs, CircPool::tk1_to_PhasedXRz) .apply(circ); REQUIRE_FALSE(success); singleqs.insert(OpType::Rx); - success = Transforms::squash_factory(singleqs, Transforms::tk1_to_PhasedXRz) + success = Transforms::squash_factory(singleqs, CircPool::tk1_to_PhasedXRz) .apply(circ); REQUIRE(success); check_command_types(circ, {OpType::Rz, OpType::PhasedX}); REQUIRE(test_unitary_comparison(circ, copy)); - success = Transforms::squash_factory(singleqs, Transforms::tk1_to_PhasedXRz) + success = Transforms::squash_factory(singleqs, CircPool::tk1_to_PhasedXRz) .apply(circ); REQUIRE_FALSE(success); } @@ -725,12 +726,12 @@ SCENARIO("Testing general 1qb squash") { Circuit copy = circ; OpTypeSet singleqs = {OpType::Rz, OpType::PhasedX}; bool success = - Transforms::squash_factory(singleqs, Transforms::tk1_to_PhasedXRz) + Transforms::squash_factory(singleqs, CircPool::tk1_to_PhasedXRz) .apply(circ); REQUIRE(success); check_command_types(circ, {OpType::PhasedX}); REQUIRE(test_unitary_comparison(circ, copy)); - success = Transforms::squash_factory(singleqs, Transforms::tk1_to_PhasedXRz) + success = Transforms::squash_factory(singleqs, CircPool::tk1_to_PhasedXRz) .apply(circ); REQUIRE_FALSE(success); } @@ -742,12 +743,12 @@ SCENARIO("Testing general 1qb squash") { Circuit copy = circ; OpTypeSet singleqs = {OpType::Rz, OpType::PhasedX}; bool success = - Transforms::squash_factory(singleqs, Transforms::tk1_to_PhasedXRz) + Transforms::squash_factory(singleqs, CircPool::tk1_to_PhasedXRz) .apply(circ); REQUIRE(success); check_command_types(circ, {OpType::Rz}); REQUIRE(test_unitary_comparison(circ, copy)); - success = Transforms::squash_factory(singleqs, Transforms::tk1_to_PhasedXRz) + success = Transforms::squash_factory(singleqs, CircPool::tk1_to_PhasedXRz) .apply(circ); REQUIRE_FALSE(success); } @@ -759,13 +760,15 @@ SCENARIO("Testing general 1qb squash") { bool success = Transforms::rebase_HQS().apply(circ); REQUIRE(success); OpTypeSet singleqs = {OpType::Rz, OpType::PhasedX}; - success = Transforms::squash_factory(singleqs, Transforms::tk1_to_PhasedXRz) + success = Transforms::squash_factory(singleqs, CircPool::tk1_to_PhasedXRz) .apply(circ); REQUIRE(success); + success = Transforms::remove_redundancies().apply(circ); + REQUIRE(success); check_command_types( circ, {OpType::Rz, OpType::PhasedX, OpType::ZZMax, OpType::Rz, OpType::PhasedX}); - success = Transforms::squash_factory(singleqs, Transforms::tk1_to_PhasedXRz) + success = Transforms::squash_factory(singleqs, CircPool::tk1_to_PhasedXRz) .apply(circ); REQUIRE_FALSE(success); } @@ -788,7 +791,7 @@ SCENARIO("Testing general 1qb squash") { circ.add_op(OpType::Rz, 1., {0}); OpTypeSet singleqs = {OpType::Rz, OpType::Rx, OpType::PhasedX}; bool success = - Transforms::squash_factory(singleqs, Transforms::tk1_to_PhasedXRz) + Transforms::squash_factory(singleqs, CircPool::tk1_to_PhasedXRz) .apply(circ); REQUIRE(success); check_command_types( @@ -796,7 +799,7 @@ SCENARIO("Testing general 1qb squash") { {OpType::Rz, OpType::PhasedX, OpType::Conditional, OpType::Conditional, OpType::Conditional, OpType::Conditional, OpType::Conditional, OpType::Conditional, OpType::Rz, OpType::PhasedX}); - success = Transforms::squash_factory(singleqs, Transforms::tk1_to_PhasedXRz) + success = Transforms::squash_factory(singleqs, CircPool::tk1_to_PhasedXRz) .apply(circ); REQUIRE_FALSE(success); } @@ -1400,14 +1403,15 @@ SCENARIO("Test synthesise_UMD") { Expr a = 0.; Expr b = 0.; Expr c = 0.; - Circuit circ = Transforms::tk1_to_PhasedXRz(a, b, c); + Circuit circ = CircPool::tk1_to_PhasedXRz(a, b, c); + Transforms::remove_redundancies().apply(circ); REQUIRE(circ.n_gates() == 0); } GIVEN("An Rz in disguise") { Expr a = 0.3; Expr b = 0.; Expr c = 1.3; - Circuit circ = Transforms::tk1_to_PhasedXRz(a, b, c); + Circuit circ = CircPool::tk1_to_PhasedXRz(a, b, c); REQUIRE(circ.n_gates() == 1); } GIVEN("Y-gate") { From d5457dfae9170b67ebe045b5a3436b6fbf1dc030 Mon Sep 17 00:00:00 2001 From: seyon Date: Fri, 11 Feb 2022 17:40:09 +0000 Subject: [PATCH 3/6] [feature] expose CircPool as circuit.library --- pytket/CMakeLists.txt | 1 + pytket/binders/circuit/library.cpp | 198 +++++++++++++++++++++++++++++ pytket/binders/circuit/main.cpp | 2 + 3 files changed, 201 insertions(+) create mode 100644 pytket/binders/circuit/library.cpp diff --git a/pytket/CMakeLists.txt b/pytket/CMakeLists.txt index 8e1ecc6e74..e1d4b185ab 100644 --- a/pytket/CMakeLists.txt +++ b/pytket/CMakeLists.txt @@ -46,6 +46,7 @@ pybind11_add_module(circuit binders/circuit/unitid.cpp binders/circuit/boxes.cpp binders/circuit/classical.cpp + binders/circuit/library.cpp binders/circuit/Circuit/main.cpp binders/circuit/Circuit/add_op.cpp binders/circuit/Circuit/add_classical_op.cpp) diff --git a/pytket/binders/circuit/library.cpp b/pytket/binders/circuit/library.cpp new file mode 100644 index 0000000000..5f1af03998 --- /dev/null +++ b/pytket/binders/circuit/library.cpp @@ -0,0 +1,198 @@ +// Copyright 2019-2022 Cambridge Quantum Computing +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "Circuit/CircPool.hpp" +#include "binder_utils.hpp" +#include "typecast.hpp" + +namespace py = pybind11; + +namespace tket { + +void init_library(py::module &m) { + /* Circuit library */ + py::module_ library_m = m.def_submodule( + "library", + "Library of reusable circuits and circuit generator functions."); + library_m.def( + "_BRIDGE_using_CX_0", &CircPool::BRIDGE_using_CX_0, + "Equivalent to BRIDGE, using four CX, first CX has control on qubit 0"); + library_m.def( + "_BRIDGE_using_CX_1", &CircPool::BRIDGE_using_CX_1, + "Equivalent to BRIDGE, using four CX, first CX has control on qubit 1"); + library_m.def( + "_CX_using_flipped_CX", &CircPool::CX_using_flipped_CX, + "Equivalent to CX[0,1], using a CX[1,0] and four H gates"); + library_m.def( + "_CX_using_ECR", &CircPool::CX_using_ECR, + "Equivalent to CX, using only ECR, Rx and U3 gates"); + 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_XXPhase_0", &CircPool::CX_using_XXPhase_0, + "Equivalent to CX, using only XXPhase, Rx, Ry and Rz gates"); + + library_m.def( + "_CX_using_XXPhase_1", &CircPool::CX_using_XXPhase_1, + "Equivalent to CX, using only XXPhase, Rx, Ry and Rz gates"); + library_m.def( + "_CX_VS_CX_reduced", &CircPool::CX_VS_CX_reduced, + "CX-reduced form of CX/V,S/CX"); + library_m.def( + "_CX_V_CX_reduced", &CircPool::CX_V_CX_reduced, + "CX-reduced form of CX/V,-/CX"); + library_m.def( + "_CX_S_CX_reduced", &CircPool::CX_S_CX_reduced, + "CX-reduced form of CX/-,S/CX (= ZZMax)"); + library_m.def( + "_CX_V_S_XC_reduced", &CircPool::CX_V_S_XC_reduced, + "CX-reduced form of CX/V,-/S,-/XC"); + library_m.def( + "_CX_S_V_XC_reduced", &CircPool::CX_S_V_XC_reduced, + "CX-reduced form of CX/-,S/-,V/XC"); + library_m.def( + "_CX_XC_reduced", &CircPool::CX_XC_reduced, "CX-reduced form of CX/XC"); + library_m.def( + "_SWAP_using_CX_0", &CircPool::SWAP_using_CX_0, + "Equivalent to SWAP, using three CX, outer CX have control on qubit 0"); + library_m.def( + "_SWAP_using_CX_1", &CircPool::SWAP_using_CX_1, + "Equivalent to SWAP, using three CX, outer CX have control on qubit 1"); + library_m.def( + "_two_Rz1", &CircPool::two_Rz1, + "A two-qubit circuit with an Rz(1) on each qubit"); + library_m.def("_X1_CX", &CircPool::X1_CX, "X[1]; CX[0,1]"); + library_m.def("_Z0_CX", &CircPool::Z0_CX, "Z[0]; CX[0,1] "); + + library_m.def( + "_CCX_modulo_phase_shift", &CircPool::CCX_modulo_phase_shift, + "Equivalent to CCX up to phase shift, using three CX. Warning: this is " + "not equivalent to CCX up to global phase so cannot be used as a direct " + "substitution except when the phase reversal can be cancelled. Its " + "unitary representation is like CCX but with a -1 at the (5,5) " + "position."); + library_m.def( + "_CCX_normal_decomp", &CircPool::CCX_normal_decomp, + "Equivalent to CCX, using five CX"); + library_m.def( + "_C3X_normal_decomp", &CircPool::C3X_normal_decomp, + "Equivalent to CCCX, using 14 CX"); + library_m.def( + "_C4X_normal_decomp", &CircPool::C4X_normal_decomp, + "Equivalent to CCCCX, using 36 CX "); + library_m.def( + "_ladder_down", &CircPool::ladder_down, "CX[0,1]; CX[2,0]; CCX[0,1,2]"); + library_m.def( + "_ladder_down_2", &CircPool::ladder_down_2, + "CX[0,1]; X[0]; X[2]; CCX[0,1,2]"); + library_m.def( + "_ladder_up", &CircPool::ladder_up, "CCX[0,1,2]; CX[2,0]; CX[2,1]"); + library_m.def("_X", &CircPool::X, "Just an X gate"); + library_m.def("_CX", &CircPool::CX, "Just a CX[0,1] gate"); + library_m.def("_CCX", &CircPool::CCX, "Just a CCX[0,1,2] gate"); + library_m.def("_BRIDGE", &CircPool::BRIDGE, "Just a BRIDGE[0,1,2] gate"); + library_m.def("_H_CZ_H", &CircPool::H_CZ_H, "H[1]; CZ[0,1]; H[1] "); + library_m.def( + "_CZ_using_CX", &CircPool::CZ_using_CX, + "Equivalent to CZ, using CX and single-qubit gates"); + library_m.def( + "_CY_using_CX", &CircPool::CY_using_CX, + "Equivalent to CY, using CX and single-qubit gates"); + library_m.def( + "_CH_using_CX", &CircPool::CH_using_CX, + "Equivalent to CH, using CX and single-qubit gates"); + library_m.def( + "_CV_using_CX", &CircPool::CV_using_CX, + "Equivalent to CV, using CX and single-qubit gates "); + library_m.def( + "_CVdg_using_CX", &CircPool::CVdg_using_CX, + "Equivalent to CVdg, using CX and single-qubit gates"); + library_m.def( + "_CSX_using_CX", &CircPool::CSX_using_CX, + "Equivalent to CSX, using CX and single-qubit gates"); + library_m.def( + "_CSXdg_using_CX", &CircPool::CSXdg_using_CX, + "Equivalent to CSXdg, using CX and single-qubit gates"); + library_m.def( + "_CSWAP_using_CX", &CircPool::CSWAP_using_CX, + "Equivalent to CSWAP, using CX and single-qubit gates "); + library_m.def( + "_ECR_using_CX", &CircPool::ECR_using_CX, + "Equivalent to ECR, using CX, Rx and U3 gates "); + library_m.def( + "_ZZMax_using_CX", &CircPool::ZZMax_using_CX, + "Equivalent to ZZMax, using CX, Rz and U3 gates "); + library_m.def( + "_CRz_using_CX", &CircPool::CRz_using_CX, + "Equivalent to CRz, using CX and Rz gates"); + library_m.def( + "_CRx_using_CX", &CircPool::CRx_using_CX, + "Equivalent to CRx, using CX, H and Rx gates"); + library_m.def( + "_CRy_using_CX", &CircPool::CRy_using_CX, + "Equivalent to CRy, using CX and Ry gates"); + library_m.def( + "_CU1_using_CX", &CircPool::CU1_using_CX, + "Equivalent to CU1, using CX and U1 gates"); + library_m.def( + "_CU3_using_CX", &CircPool::CU3_using_CX, + "Equivalent to CU1, using CX, U1 and U3 gates"); + library_m.def( + "_ISWAP_using_CX", &CircPool::ISWAP_using_CX, + "Equivalent to ISWAP, using CX, U3 and Rz gates"); + library_m.def( + "_XXPhase_using_CX", &CircPool::XXPhase_using_CX, + "Equivalent to XXPhase, using CX and U3 gates "); + library_m.def( + "_YYPhase_using_CX", &CircPool::YYPhase_using_CX, + "Equivalent to YYPhase, using CX, Rz and U3 gates"); + library_m.def( + "_ZZPhase_using_CX", &CircPool::ZZPhase_using_CX, + "Equivalent to ZZPhase, using CX and Rz gates"); + library_m.def( + "_XXPhase3_using_CX", &CircPool::XXPhase3_using_CX, + "Equivalent to 3-qubit MS interaction, using CX and U3 gates"); + library_m.def( + "_ESWAP_using_CX", &CircPool::XXPhase3_using_CX, + "Equivalent to ESWAP, using CX, X, S, Ry and U1 gates"); + library_m.def( + "_FSim_using_CX", &CircPool::FSim_using_CX, + "Equivalent to Fsim, using CX, X, S, U1 and U3 gates "); + library_m.def( + "_PhasedISWAP_using_CX", &CircPool::PhasedISWAP_using_CX, + "Equivalent to PhasedISWAP, using CX, U3 and Rz gates"); + library_m.def( + "_NPhasedX_using_CX", &CircPool::NPhasedX_using_CX, + "Unwrap NPhasedX, into number_of_qubits PhasedX gates"); + + library_m.def( + "_TK1_to_PhasedXRz", &CircPool::tk1_to_PhasedXRz, + "A tk1 equivalent circuit given tk1 parameters in terms of PhasedX, Rz"); + library_m.def( + "_TK1_to_RzRx", &CircPool::tk1_to_rzrx, + "A tk1 equivalent circuit given tk1 parameters in terms of Rz, Rx"); + library_m.def( + "_TK1_to_RzH", &CircPool::tk1_to_rzh, + "A tk1 equivalent circuit given tk1 parameters in terms of Rz, H"); + library_m.def( + "_TK1_to_RzSX", &CircPool::tk1_to_rzsx, + "A tk1 equivalent circuit given tk1 parameters in terms of Rz, Sx"); + library_m.def( + "_TK1_to_TK1", &CircPool::tk1_to_tk1, + "A circuit of a single tk1 gate with given parameters"); +} +} // namespace tket diff --git a/pytket/binders/circuit/main.cpp b/pytket/binders/circuit/main.cpp index 352d0aa296..c2ef596959 100644 --- a/pytket/binders/circuit/main.cpp +++ b/pytket/binders/circuit/main.cpp @@ -39,6 +39,7 @@ void init_unitid(py::module &m); void init_circuit(py::module &m); void init_classical(py::module &m); void init_boxes(py::module &m); +void init_library(py::module &m); PYBIND11_MODULE(circuit, m) { init_unitid(m); @@ -500,6 +501,7 @@ PYBIND11_MODULE(circuit, m) { ":return: set of symbolic parameters for the command"); init_circuit(m); + init_library(m); init_boxes(m); init_classical(m); From 3e36c2d6a18068be0f1f3e5c95c7de54812ddb31 Mon Sep 17 00:00:00 2001 From: seyon Date: Mon, 14 Feb 2022 11:48:39 +0000 Subject: [PATCH 4/6] apply remove_redundancies within standard_rebase --- tket/src/Transformations/Rebase.cpp | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/tket/src/Transformations/Rebase.cpp b/tket/src/Transformations/Rebase.cpp index 3276905841..8c75adc9d8 100644 --- a/tket/src/Transformations/Rebase.cpp +++ b/tket/src/Transformations/Rebase.cpp @@ -94,6 +94,7 @@ static bool standard_rebase( std::vector tk1_angles = as_gate_ptr(op)->get_tk1_angles(); Circuit replacement = tk1_replacement(tk1_angles[0], tk1_angles[1], tk1_angles[2]); + remove_redundancies().apply(replacement); if (conditional) { circ.substitute_conditional(replacement, v, Circuit::VertexDeletion::No); } else { @@ -118,45 +119,35 @@ Transform rebase_tket() { return rebase_factory({OpType::CX, OpType::TK1}, CircPool::CX(), tk1_to_tk1); } -// apply redundancy removal to circuit returned by function -#define WRAP_REDUNDANCY(func) \ - [](const Expr& alpha, const Expr& beta, const Expr& gamma) { \ - Circuit c = func(alpha, beta, gamma); \ - remove_redundancies().apply(c); \ - return c; \ - } - Transform rebase_cirq() { return rebase_factory( {OpType::CZ, OpType::PhasedX, OpType::Rz}, CircPool::H_CZ_H(), - WRAP_REDUNDANCY(CircPool::tk1_to_PhasedXRz)); + CircPool::tk1_to_PhasedXRz); } Transform rebase_HQS() { return rebase_factory( {OpType::ZZMax, OpType::PhasedX, OpType::Rz}, CircPool::CX_using_ZZMax(), - WRAP_REDUNDANCY(CircPool::tk1_to_PhasedXRz)); + CircPool::tk1_to_PhasedXRz); } Transform rebase_UMD() { return rebase_factory( {OpType::XXPhase, OpType::PhasedX, OpType::Rz}, - CircPool::CX_using_XXPhase_0(), - WRAP_REDUNDANCY(CircPool::tk1_to_PhasedXRz)); + CircPool::CX_using_XXPhase_0(), CircPool::tk1_to_PhasedXRz); } Transform rebase_quil() { return rebase_factory( {OpType::CZ, OpType::Rx, OpType::Rz}, CircPool::H_CZ_H(), - WRAP_REDUNDANCY(CircPool::tk1_to_rzrx)); + CircPool::tk1_to_rzrx); } Transform rebase_pyzx() { OpTypeSet pyzx_gates = {OpType::SWAP, OpType::CX, OpType::CZ, OpType::H, OpType::X, OpType::Z, OpType::S, OpType::T, OpType::Rx, OpType::Rz}; - return rebase_factory( - pyzx_gates, CircPool::CX(), WRAP_REDUNDANCY(CircPool::tk1_to_rzrx)); + return rebase_factory(pyzx_gates, CircPool::CX(), CircPool::tk1_to_rzrx); } Transform rebase_projectq() { @@ -164,20 +155,19 @@ Transform rebase_projectq() { OpType::H, OpType::X, OpType::Y, OpType::Z, OpType::S, OpType::T, OpType::V, OpType::Rx, OpType::Ry, OpType::Rz}; - return rebase_factory( - projectq_gates, CircPool::CX(), WRAP_REDUNDANCY(CircPool::tk1_to_rzrx)); + return rebase_factory(projectq_gates, CircPool::CX(), CircPool::tk1_to_rzrx); } Transform rebase_UFR() { return rebase_factory( {OpType::CX, OpType::Rz, OpType::H}, CircPool::CX(), - WRAP_REDUNDANCY(CircPool::tk1_to_rzh)); + CircPool::tk1_to_rzh); } Transform rebase_OQC() { return rebase_factory( {OpType::ECR, OpType::Rz, OpType::SX}, CircPool::CX_using_ECR(), - WRAP_REDUNDANCY(CircPool::tk1_to_rzsx)); + CircPool::tk1_to_rzsx); } } // namespace Transforms From 72f21b871fbe26a22dee9be000e4e4a68782ad00 Mon Sep 17 00:00:00 2001 From: seyon Date: Mon, 14 Feb 2022 14:45:08 +0000 Subject: [PATCH 5/6] clarify cx_replacement gate use requirement in passes.cpp --- pytket/binders/passes.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytket/binders/passes.cpp b/pytket/binders/passes.cpp index 11df97920c..2479fc6219 100644 --- a/pytket/binders/passes.cpp +++ b/pytket/binders/passes.cpp @@ -470,7 +470,8 @@ PYBIND11_MODULE(passes, m) { "\n\n:param gateset: The allowed multi-qubit operations in the " "rebased circuit." "\n:param cx_replacement: The equivalent circuit to replace a CX " - "gate in the desired basis." + "gate using two qubit gates from the desired basis (can use any single " + "qubit OpTypes)." "\n:param tk1_replacement: A function which, given the parameters of " "an Rz(a)Rx(b)Rz(c) triple, returns an equivalent circuit in the " "desired basis." From ed6ce0401ed071d6361bc979fee70f038fcde737 Mon Sep 17 00:00:00 2001 From: seyon Date: Mon, 14 Feb 2022 16:31:08 +0000 Subject: [PATCH 6/6] [feature] auto_rebase_pass function for attempting automatic rebase passes --- pytket/binders/circuit/library.cpp | 2 +- pytket/docs/changelog.rst | 2 + pytket/pytket/passes/__init__.py | 1 + pytket/pytket/passes/auto_rebase.py | 97 +++++++++++++++++++++++++++++ pytket/tests/transform_test.py | 53 +++++++++++++++- 5 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 pytket/pytket/passes/auto_rebase.py diff --git a/pytket/binders/circuit/library.cpp b/pytket/binders/circuit/library.cpp index 5f1af03998..e548a4b7a3 100644 --- a/pytket/binders/circuit/library.cpp +++ b/pytket/binders/circuit/library.cpp @@ -25,7 +25,7 @@ namespace tket { void init_library(py::module &m) { /* Circuit library */ py::module_ library_m = m.def_submodule( - "library", + "_library", "Library of reusable circuits and circuit generator functions."); library_m.def( "_BRIDGE_using_CX_0", &CircPool::BRIDGE_using_CX_0, diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 68a6c4f8e9..6ca95cade0 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -10,10 +10,12 @@ API changes: ``map`` property instead.) * The deprecated ``Backend.compile_circuit`` method is removed. (Use ``get_compiled_circuit`` instead.) +* ``RebaseCustom`` takes one allowed gateset parameter rather than separate single qubit and multiqubit gatesets. Minor new features: * Add ``delay_measures`` option to ``DefaultMappingPass``. +* New ``pytket.passes.auto_rebase_pass`` which attempts to construct a rebase pass given a target gate set from known decompositions. 0.19.0 (February 2022) ---------------------- diff --git a/pytket/pytket/passes/__init__.py b/pytket/pytket/passes/__init__.py index 07004a66a4..aa68496e71 100644 --- a/pytket/pytket/passes/__init__.py +++ b/pytket/pytket/passes/__init__.py @@ -17,3 +17,4 @@ from pytket._tket.passes import * # type: ignore from .script import compilation_pass_from_script, compilation_pass_grammar +from .auto_rebase import auto_rebase_pass diff --git a/pytket/pytket/passes/auto_rebase.py b/pytket/pytket/passes/auto_rebase.py new file mode 100644 index 0000000000..f9623a2247 --- /dev/null +++ b/pytket/pytket/passes/auto_rebase.py @@ -0,0 +1,97 @@ +# Copyright 2019-2022 Cambridge Quantum Computing +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Set, Union, Callable, Dict, FrozenSet, TYPE_CHECKING +from pytket.circuit import Circuit, OpType # type: ignore +from pytket._tket.circuit import _library # type: ignore +from pytket.passes import RebaseCustom # type: ignore + +if TYPE_CHECKING: + from sympy import Expr # type: ignore + + +class NoAutoRebase(Exception): + """Automatic rebase could not be found.""" + + +_CX_CIRCS: Dict[OpType, Callable[[], "Circuit"]] = { + OpType.CX: _library._CX, + OpType.ZZMax: _library._CX_using_ZZMax, + OpType.XXPhase: _library._CX_using_XXPhase_0, + OpType.ECR: _library._CX_using_ECR, + OpType.CZ: lambda: Circuit(2).H(1).CZ(0, 1).H(1), +} + + +def get_cx_decomposition(gateset: Set[OpType]) -> Circuit: + """Return a Circuit expressing a CX in terms of a two qubit gate in the + gateset if one is available, raise an error otherwise. + + :param gateset: Target gate set. + :type gateset: Set[OpType] + :raises NoAutoRebase: No suitable CX decomposition found. + :return: Decomposuition circuit. + :rtype: Circuit + """ + if any((matching := k) in gateset for k in _CX_CIRCS): + return _CX_CIRCS[matching]() + raise NoAutoRebase("No known decomposition from CX to available gateset.") + + +Param = Union[str, "Expr"] + +_TK1_circs: Dict[FrozenSet[OpType], Callable[[Param, Param, Param], "Circuit"]] = { + frozenset({OpType.TK1}): _library._TK1_to_TK1, + frozenset({OpType.PhasedX, OpType.Rz}): _library._TK1_to_PhasedXRz, + frozenset({OpType.Rx, OpType.Rz}): _library._TK1_to_RzRx, + frozenset({OpType.Rz, OpType.H}): _library._TK1_to_RzH, + frozenset({OpType.Rz, OpType.SX}): _library._TK1_to_RzSX, +} + + +def get_TK1_decomposition_function( + gateset: Set[OpType], +) -> Callable[[Param, Param, Param], "Circuit"]: + """Return a function for generating TK1 equivalent circuits, which take the + three TK1 parameters as arguments and return a TK1 equivalent single qubit + circuit. If no such function is available, raise an error. + + :raises NoAutoRebase: No suitable TK1 decomposition found. + :return: TK1 decomposition function. + :rtype: Callable[[Param, Param, Param], "Circuit"] + """ + if any((matching := k).issubset(gateset) for k in _TK1_circs): + return _TK1_circs[matching] + raise NoAutoRebase("No known decomposition from TK1 to available gateset.") + + +def auto_rebase_pass(gateset: Set[OpType]) -> RebaseCustom: + """Attempt to generate a rebase pass automatically for the given target + gateset. + + Checks if there are known existing decompositions from CX + to target gateset and TK1 to target gateset and uses those to construct a + custom rebase. + Raises an error if no known decompositions can be found, in which case try + using RebaseCustom with your own decompositions. + + :param gateset: Set of supported OpTypes, target gate set. + :type gateset: FrozenSet[OpType] + :raises NoAutoRebase: No suitable CX or TK1 decomposition found. + :return: Rebase pass. + :rtype: RebaseCustom + """ + return RebaseCustom( + gateset, get_cx_decomposition(gateset), get_TK1_decomposition_function(gateset) + ) diff --git a/pytket/tests/transform_test.py b/pytket/tests/transform_test.py index 14d5d3afd8..32ae7c72f7 100644 --- a/pytket/tests/transform_test.py +++ b/pytket/tests/transform_test.py @@ -14,8 +14,11 @@ from pathlib import Path from pytket.circuit import Circuit, OpType, PauliExpBox # type: ignore +from pytket._tket.circuit import _library # type: ignore from pytket.pauli import Pauli # type: ignore -from pytket.passes import RemoveRedundancies, KAKDecomposition, ThreeQubitSquash, CommuteThroughMultis, PauliSquash, FullPeepholeOptimise, GlobalisePhasedX # type: ignore +from pytket.passes import RemoveRedundancies, KAKDecomposition, ThreeQubitSquash, CommuteThroughMultis, PauliSquash, FullPeepholeOptimise, GlobalisePhasedX, RebaseCustom # type: ignore +from pytket.passes import auto_rebase_pass +from pytket.passes.auto_rebase import _CX_CIRCS, NoAutoRebase from pytket.predicates import CompilationUnit # type: ignore from pytket.transform import Transform, CXConfigType, PauliSynthStrat # type: ignore from pytket.qasm import circuit_from_qasm @@ -729,6 +732,54 @@ def test_full_peephole_optimise() -> None: assert n_cx1 < n_cz +def test_auto_rebase() -> None: + pass_params = [ + ({OpType.CX, OpType.Rz, OpType.Rx}, _library._CX(), _library._TK1_to_RzRx), + ( + {OpType.CZ, OpType.Rz, OpType.SX, OpType.ZZPhase}, + _CX_CIRCS[OpType.CZ](), + _library._TK1_to_RzSX, + ), + ( + {OpType.ZZMax, OpType.T, OpType.Rz, OpType.H}, + _library._CX_using_ZZMax(), + _library._TK1_to_RzH, + ), + ( + {OpType.XXPhase, OpType.T, OpType.Rz, OpType.H}, + _library._CX_using_XXPhase_0(), + _library._TK1_to_RzH, + ), + ( + {OpType.ECR, OpType.PhasedX, OpType.Rz, OpType.CnX}, + _library._CX_using_ECR(), + _library._TK1_to_PhasedXRz, + ), + ( + {OpType.CX, OpType.TK1, OpType.U3, OpType.CnX}, + _library._CX(), + _library._TK1_to_TK1, + ), + ] + + circ = get_test_circuit() + + for gateset, cx_circ, TK1_func in pass_params: + rebase = auto_rebase_pass(gateset) + assert rebase.to_dict() == RebaseCustom(gateset, cx_circ, TK1_func).to_dict() + + c2 = circ.copy() + assert rebase.apply(c2) + + with pytest.raises(NoAutoRebase) as cx_err: + _ = auto_rebase_pass({OpType.ZZPhase, OpType.TK1}) + assert "CX" in str(cx_err.value) + + with pytest.raises(NoAutoRebase) as cx_err: + _ = auto_rebase_pass({OpType.CX, OpType.H, OpType.T}) + assert "TK1" in str(cx_err.value) + + if __name__ == "__main__": test_remove_redundancies() test_reduce_singles()