From 492c7542497bd6fa523029bba51363f853c94a94 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Thu, 20 Jul 2023 16:51:59 +0100 Subject: [PATCH 01/29] Further changes to auto rebase --- pytket/binders/circuit/library.cpp | 9 + pytket/pytket/passes/auto_rebase.py | 23 ++- pytket/tests/transform_test.py | 131 ++++++++++++--- tket/include/tket/Circuit/CircPool.hpp | 20 +++ tket/include/tket/Circuit/Command.hpp | 2 +- .../tket/Predicates/CompilationUnit.hpp | 2 +- tket/src/ArchAwareSynth/Path.cpp | 2 +- tket/src/Circuit/CircPool.cpp | 158 +++++++++++++++++- tket/src/Circuit/ControlledGates.cpp | 18 +- tket/src/Circuit/Multiplexor.cpp | 9 +- tket/src/Mapping/LexiRoute.cpp | 2 +- .../src/Transformations/BasicOptimisation.cpp | 4 +- tket/src/Transformations/Decomposition.cpp | 5 + tket/test/src/Circuit/test_Boxes.cpp | 2 +- tket/test/src/test_ControlDecomp.cpp | 4 +- tket/test/src/test_TwoQubitCanonical.cpp | 6 +- 16 files changed, 343 insertions(+), 54 deletions(-) diff --git a/pytket/binders/circuit/library.cpp b/pytket/binders/circuit/library.cpp index 0ac6224fae..6404ba509e 100644 --- a/pytket/binders/circuit/library.cpp +++ b/pytket/binders/circuit/library.cpp @@ -41,6 +41,12 @@ void init_library(py::module &m) { "Given expressions α, β and γ, return circuit equivalent to " "TK2(α, β, γ) using up to 3 CX and single-qubit gates.\n\n" "The decomposition minimizes the number of CX gates."); + library_m.def( + "_TK2_using_CX_and_swap", &CircPool::TK2_using_CX_and_swap, + "Given expressions α, β and γ, return circuit equivalent to " + "TK2(α, β, γ), up to a wire swap, using up to 3 CX and single-qubit " + "gates.\n\n" + "The decomposition minimizes the number of CX gates."); library_m.def( "_approx_TK2_using_1xCX", &CircPool::approx_TK2_using_1xCX, "Best approximation of TK2 using 1 CX gate and single-qubit gates, using " @@ -227,6 +233,9 @@ void init_library(py::module &m) { library_m.def( "_TK2_using_ZZMax", &CircPool::TK2_using_ZZMax, "Equivalent to TK2, using up to 3 ZZMax gates."); + library_m.def( + "_TK2_using_ZZMax_and_swap", &CircPool::TK2_using_ZZMax_and_swap, + "Equivalent to TK2, up to a wire swap, using up to 3 ZZMax gates."); library_m.def( "_XXPhase3_using_TK2", &CircPool::XXPhase3_using_TK2, "Equivalent to XXPhase3, using three TK2 gates"); diff --git a/pytket/pytket/passes/auto_rebase.py b/pytket/pytket/passes/auto_rebase.py index e32559cd35..b739bc97d7 100644 --- a/pytket/pytket/passes/auto_rebase.py +++ b/pytket/pytket/passes/auto_rebase.py @@ -44,6 +44,12 @@ def _TK2_using_TK2(a: Param, b: Param, c: Param) -> Circuit: OpType.ZZMax: _library._TK2_using_ZZMax, } +_TK2_CIRCS_WIRE_SWAP: Dict[OpType, Callable[[Param, Param, Param], "Circuit"]] = { + OpType.TK2: _TK2_using_TK2, + OpType.ZZPhase: _library._TK2_using_ZZPhase, + OpType.CX: _library._TK2_using_CX_and_swap, + OpType.ZZMax: _library._TK2_using_ZZMax_and_swap, +} def get_cx_decomposition(gateset: Set[OpType]) -> Circuit: """Return a Circuit expressing a CX in terms of a two qubit gate in the @@ -62,6 +68,7 @@ def get_cx_decomposition(gateset: Set[OpType]) -> Circuit: def get_tk2_decomposition( gateset: Set[OpType], + allow_swaps: bool, ) -> Callable[[Param, Param, Param], "Circuit"]: """Return a function to construct a circuit expressing a TK2 in terms of gates in the given gateset, if such a function is available. @@ -70,9 +77,15 @@ def get_tk2_decomposition( :raises NoAutoRebase: no suitable TK2 decomposition found :return: function to decompose TK2 gates """ - for k, fn in _TK2_CIRCS.items(): - if k in gateset: - return fn + if allow_swaps: + for k, fn in _TK2_CIRCS_WIRE_SWAP.items(): + if k in gateset: + return fn + else: + for k, fn in _TK2_CIRCS.items(): + if k in gateset: + return fn + raise NoAutoRebase("No known decomposition from TK2 to given gateset") @@ -109,7 +122,7 @@ def get_TK1_decomposition_function( raise NoAutoRebase("No known decomposition from TK1 to available gateset.") -def auto_rebase_pass(gateset: Set[OpType]) -> RebaseCustom: +def auto_rebase_pass(gateset: Set[OpType], allow_swaps: bool = False) -> RebaseCustom: """Attempt to generate a rebase pass automatically for the given target gateset. @@ -132,7 +145,7 @@ def auto_rebase_pass(gateset: Set[OpType]) -> RebaseCustom: return RebaseCustom(gateset, _library._CX(), tk1) # in other cases, try to rebase via TK2 first try: - return RebaseCustom(gateset, get_tk2_decomposition(gateset), tk1) + return RebaseCustom(gateset, get_tk2_decomposition(gateset, allow_swaps), tk1) except NoAutoRebase: pass try: diff --git a/pytket/tests/transform_test.py b/pytket/tests/transform_test.py index e721fe8269..2959096f7f 100644 --- a/pytket/tests/transform_test.py +++ b/pytket/tests/transform_test.py @@ -1215,30 +1215,111 @@ def test_round_angles() -> None: assert Transform.round_angles(8).apply(circ0) assert circ0 == circ1 +from pytket.passes import NormaliseTK2, DecomposeTK2 +def test_auto_rebase_with_swap() -> None: + swap_pass = auto_rebase_pass({OpType.ZZMax, OpType.PhasedX, OpType.Rz}, True) + no_swap_pass = auto_rebase_pass({OpType.ZZMax, OpType.PhasedX, OpType.Rz}, False) + + c_swap = Circuit(2).ISWAPMax(0, 1) + swap_pass.apply(c_swap) + + # print("\n",c_swap.get_commands()) + + + # c_swap = Circuit(2).ISWAPMax(0,1) + # auto_rebase_pass({OpType.TK2, OpType.PhasedX, OpType.Rz}).apply(c_swap) + # NormaliseTK2().apply(c_swap) + # DecomposeTK2().apply(c_swap) + # print("\n",c_swap.get_commands()) + + + assert c_swap.n_gates_of_type(OpType.ZZMax) == 1 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(1) + assert iqp[Qubit(1)] == Qubit(0) + c_no_swap = Circuit(2).ISWAPMax(0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 2 + + print("\nISWAPMax:", c_swap.n_gates, c_no_swap.n_gates) + + c_swap = Circuit(2).Sycamore(0, 1) + swap_pass.apply(c_swap) + print(c_swap.get_commands()) + print(c_swap.n_gates_of_type(OpType.ZZMax)) + assert c_swap.n_gates_of_type(OpType.ZZMax) == 2 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(1) + assert iqp[Qubit(1)] == Qubit(0) + c_no_swap = Circuit(2).Sycamore(0, 1) + no_swap_pass.apply(c_swap) + assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 3 + print("\nSycamore:", c_swap.n_gates, c_no_swap.n_gates) + + c_swap = Circuit(2).ISWAP(0.3, 0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.ZZMax) == 2 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(0) + assert iqp[Qubit(1)] == Qubit(1) + c_no_swap = Circuit(2).ISWAP(0.3, 0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 2 + + print("\nISWAP:", c_swap.n_gates, c.n_gates) + c_swap = Circuit(2).ISWAPMax(0, 1).ISWAPMax(1, 0) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.ZZMax) == 2 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(0) + assert iqp[Qubit(1)] == Qubit(1) + c_no_swap = Circuit(2).ISWAPMax(0, 1).ISWAPMax(1, 0) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 4 + + c_swap = Circuit(2).SWAP(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.ZZMax) == 0 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(1) + assert iqp[Qubit(1)] == Qubit(0) + c_no_swap = Circuit(2).SWAP(0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 3 + + print("\nSWAP:", c_swap.n_gates, c.n_gates) + + c_swap = Circuit(2).ZZMax(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates == 1 + + print("\nZZMax:", c_swap.n_gates, c.n_gates) + if __name__ == "__main__": - test_remove_redundancies() - test_reduce_singles() - test_commute() - test_KAK() - test_basic_rebases() - test_post_routing() - test_phase_gadget() - test_Cliffords() - test_Pauli_gadget() - test_cons_sequencing() - test_list_sequencing() - test_basic_repeat() - test_while_repeat() - test_implicit_swaps_1() - test_implicit_swaps_2() - test_implicit_swaps_3() - test_decompose_swap_to_cx() - test_noncontiguous_DefaultMappingPass_arc() - test_RoutingPass() - test_DefaultMappingPass() - test_CXMappingPass() - test_CXMappingPass_correctness() - test_CXMappingPass_terminates() - test_FullMappingPass() - test_KAK_with_ClassicalExpBox() + # test_remove_redundancies() + # test_reduce_singles() + # test_commute() + # test_KAK() + # test_basic_rebases() + # test_post_routing() + # test_phase_gadget() + # test_Cliffords() + # test_Pauli_gadget() + # test_cons_sequencing() + # test_list_sequencing() + # test_basic_repeat() + # test_while_repeat() + # test_implicit_swaps_1() + # test_implicit_swaps_2() + # test_implicit_swaps_3() + # test_decompose_swap_to_cx() + # test_noncontiguous_DefaultMappingPass_arc() + # test_RoutingPass() + # test_DefaultMappingPass() + # test_CXMappingPass() + # test_CXMappingPass_correctness() + # test_CXMappingPass_terminates() + # test_FullMappingPass() + # test_KAK_with_ClassicalExpBox() + test_auto_rebase_with_swap() diff --git a/tket/include/tket/Circuit/CircPool.hpp b/tket/include/tket/Circuit/CircPool.hpp index a9218017ed..5eff734ece 100644 --- a/tket/include/tket/Circuit/CircPool.hpp +++ b/tket/include/tket/Circuit/CircPool.hpp @@ -316,6 +316,17 @@ Circuit normalised_TK2_using_CX( */ Circuit TK2_using_CX(const Expr &alpha, const Expr &beta, const Expr &gamma); +/** + * @brief Equivalent to TK2(α, β, γ) up to a wire swap, with minimal number of + * CX gates. + * + * A TK2-equivalent circuit with as few CX gates as possible (0, 1, 2 or 3 CX). + * + * @return Circuit Equivalent circuit, up to a wire swap, to TK2(α, β, γ). + */ +Circuit TK2_using_CX_and_swap( + const Expr &alpha, const Expr &beta, const Expr &gamma); + /** * @brief Equivalent to TK2(α, 0, 0), using 1 ZZPhase gate. * @@ -367,6 +378,15 @@ Circuit TK2_using_ZZPhase( */ Circuit TK2_using_ZZMax(const Expr &alpha, const Expr &beta, const Expr &gamma); +/** + * @brief Equivalent to TK2(α, β, γ), up to a wire swap, using up to 3 ZZMax + * gates. + * + * @return Circuit equivalent to TK2(α, β, γ) up to a wire swap. + */ +Circuit TK2_using_ZZMax_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/include/tket/Circuit/Command.hpp b/tket/include/tket/Circuit/Command.hpp index 85f2875223..9cc5540c59 100644 --- a/tket/include/tket/Circuit/Command.hpp +++ b/tket/include/tket/Circuit/Command.hpp @@ -86,7 +86,7 @@ class Command { Op_ptr op_ptr; unit_vector_t args; // indexed by port numbering std::optional opgroup; - Vertex vert; // vertex in the DAG + Vertex vert; // vertex in the DAG }; JSON_DECL(Command) diff --git a/tket/include/tket/Predicates/CompilationUnit.hpp b/tket/include/tket/Predicates/CompilationUnit.hpp index 2714f187d0..bf9f824267 100644 --- a/tket/include/tket/Predicates/CompilationUnit.hpp +++ b/tket/include/tket/Predicates/CompilationUnit.hpp @@ -58,7 +58,7 @@ class CompilationUnit { void empty_cache() const; void initialize_cache() const; void initialize_maps(); - Circuit circ_; // modified continuously + Circuit circ_; // modified continuously PredicatePtrMap target_preds; // these are the predicates you WANT your circuit to // satisfy by the end of your Compiler Passes diff --git a/tket/src/ArchAwareSynth/Path.cpp b/tket/src/ArchAwareSynth/Path.cpp index dad2700494..3a9de1f2f7 100644 --- a/tket/src/ArchAwareSynth/Path.cpp +++ b/tket/src/ArchAwareSynth/Path.cpp @@ -102,7 +102,7 @@ PathHandler PathHandler::construct_acyclic_handler() const { std::list current_layer_vertices{centre_node}; std::list next_layer_vertices; std::vector> parents_neighbours( - n); // pair(num_neighbours, parent vertex) + n); // pair(num_neighbours, parent vertex) std::vector vertices_in_tree( n, 0); // track which vertices are in the acyclic graph vertices_in_tree[centre_node] = 1; diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index 3e3f151895..7ddc249077 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -20,6 +20,7 @@ #include "tket/Circuit/Circuit.hpp" #include "tket/Gate/Rotation.hpp" #include "tket/OpType/OpType.hpp" +#include "tket/Transformations/BasicOptimisation.hpp" #include "tket/Utils/Expression.hpp" #include "tket/Utils/MatrixAnalysis.hpp" @@ -906,7 +907,6 @@ Circuit TK2_using_3xCX(const Expr &alpha, const Expr &beta, const Expr &gamma) { Circuit normalised_TK2_using_CX( const Expr &alpha, const Expr &beta, const Expr &gamma) { - // only handle TK2 if normalised to Weyl chamber if (equiv_0(alpha, 4) && equiv_0(beta, 4) && equiv_0(gamma, 4)) { return Circuit(2); } else if ( @@ -935,6 +935,145 @@ Circuit TK2_using_CX(const Expr &alpha, const Expr &beta, const Expr &gamma) { return c; } +/** + * @brief TK2 expressed as CX and optional wire swap + * + * Decomposes a TK2 gate into CX gates + * + * Symbolic parameters are supported. In that case, decompositions are exact. + * + * @param angles The TK2 parameters + * @return Circuit TK2-equivalent up to wire swap circuit + */ +static Circuit TK2_swap_replacement(std::array angles) { + std::cout << " In TK2 SWAP replacement." << std::endl; + if (!in_weyl_chamber(angles)) { + throw std::domain_error("TK2 params are not normalised to Weyl chamber."); + } + unsigned n_gates = 3; // default to 3x CX + bool implicit_swap = false; // default to no implicit swap + + // Generate to support allowing swap gates + Circuit pre, post; + std::array angles_swapped; + // Swapped circuit + Circuit swap_circ(2); + angles_swapped = angles; + for (unsigned i = 0; i < 3; ++i) { + angles_swapped[i] += 0.5; + } + + std::tie(pre, angles_swapped, post) = normalise_TK2_angles( + angles_swapped[0], angles_swapped[1], angles_swapped[2]); + pre.add_phase(0.25); + + // Try to evaluate exprs to doubles. + std::array angles_eval; + std::array angles_eval_swapped; + unsigned last_angle = 0; + for (; last_angle < 3; ++last_angle) { + std::optional eval = eval_expr_mod(angles[last_angle]); + if (eval) { + angles_eval[last_angle] = *eval; + } else { + break; + } + eval = eval_expr_mod(angles_swapped[last_angle]); + TKET_ASSERT(eval); + angles_eval_swapped[last_angle] = *eval; + } + + // Check if fewer gates can be used. + + if (last_angle <= 2) { + if (equiv_0(angles[2], 4) && equiv_0(angles[1], 4)) { + n_gates = 1; + } else if (equiv_0(angles_swapped[2], 4) && equiv_0(angles_swapped[1], 4)) + { + implicit_swap = true; + n_gates = 1; + } else if (equiv_0(angles[2], 4)) { + n_gates = 2; + } else if (equiv_0(angles_swapped[2], 4)) { + implicit_swap = true; + n_gates = 2; + } + } else { + double max_fid = 0.; + double ncx_fid; + for (unsigned n_cx = 0; n_cx <= 3; ++n_cx) { + switch (n_cx) { + case 0: + ncx_fid = + trace_fidelity(angles_eval[0], angles_eval[1], angles_eval[2]); + case 1: + ncx_fid = trace_fidelity( + 0.5 - angles_eval[0], angles_eval[1], angles_eval[2]); + case 2: + ncx_fid = trace_fidelity(0, 0, angles_eval[2]); + default: + ncx_fid = 1.; + } + if (ncx_fid > max_fid) { + max_fid = ncx_fid; + n_gates = n_cx; + } + } + } + + + // Build circuit for substitution. + Circuit sub(2); + switch (n_gates) { + case 0: + break; + case 1: { + sub.append(CircPool::approx_TK2_using_1xCX()); + break; + } + case 2: { + sub.append(CircPool::approx_TK2_using_2xCX(angles[0], angles[1])); + break; + } + case 3: { + sub.append(CircPool::TK2_using_3xCX(angles[0], angles[1], angles[2])); + break; + } + default: + TKET_ASSERT(!"Number of CX invalid in decompose_TK2"); + } + + if (implicit_swap) { + Circuit swap(2); + swap.add_op(OpType::SWAP, {0, 1}); + sub = pre >> sub >> post >> swap; + sub.replace_SWAPs(); + } + // This decomposition can leave many extraneous single qubits gates: squash + // them into TK1 that can be resynthesised + Transforms::squash_1qb_to_tk1().apply(sub); + return sub; +} + +Circuit TK2_using_CX_and_swap( + const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c = TK2_using_normalised_TK2(alpha, beta, gamma); + // Find the TK2 vertex and replace it. + BGL_FORALL_VERTICES(v, c.dag, DAG) { + Op_ptr op = c.get_Op_ptr_from_Vertex(v); + if (op->get_type() == OpType::TK2) { + std::vector params = op->get_params(); + TKET_ASSERT(params.size() == 3); + std::array arr_params = {params[0], params[1], params[2]}; + Circuit rep = TK2_swap_replacement(arr_params); + + c.substitute(rep, v, Circuit::VertexDeletion::Yes); + break; + } + } + return c; +} + Circuit approx_TK2_using_1xZZPhase(const Expr &alpha) { return XXPhase_using_ZZPhase(alpha); } @@ -972,6 +1111,23 @@ Circuit TK2_using_ZZMax( return c; } +Circuit TK2_using_ZZMax_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 ZZMax. + 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_ZZMax(), 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}); diff --git a/tket/src/Circuit/ControlledGates.cpp b/tket/src/Circuit/ControlledGates.cpp index 1395182f63..3f243080fb 100644 --- a/tket/src/Circuit/ControlledGates.cpp +++ b/tket/src/Circuit/ControlledGates.cpp @@ -39,7 +39,7 @@ typedef std::vector> candidate_t; // each CnX candidate to decompose needs a spare wire to put // some extra controls on -static Circuit lemma72(unsigned control_m); // rule lemma 7.2 +static Circuit lemma72(unsigned control_m); // rule lemma 7.2 static void lemma73( Circuit& circ, const std::pair& pairy); // rule lemma 7.3 @@ -87,9 +87,9 @@ Circuit incrementer_borrow_1_qubit(unsigned n) { cnx_top = C4X_normal_decomp(); cnx1_qbs = {0, 1, 2, 3, n}; } else { - cnx_top = lemma72(k); // k controls on cnx + cnx_top = lemma72(k); // k controls on cnx cnx1_qbs.resize( - 2 * k - 2); // size of replacement using borrowed qbs = 2*k-1 + 2 * k - 2); // size of replacement using borrowed qbs = 2*k-1 std::iota(cnx1_qbs.begin(), cnx1_qbs.end(), 0); cnx1_qbs.push_back(n); // target is last qubit } @@ -104,11 +104,11 @@ Circuit incrementer_borrow_1_qubit(unsigned n) { if (i != 0) bot_qbs[2 * i + 1] = i + j - - 1; // 3,5...n //other qbs we are actually trying to increment + 1; // 3,5...n //other qbs we are actually trying to increment } bot_qbs[1] = n; // incremented qubit 0 in incrementer is bottom one } else { - if (j == 4) { // code is unreachable if j<4 + if (j == 4) { // code is unreachable if j<4 bottom_incrementer.add_blank_wires(4); bottom_incrementer.append_qubits(C3X_normal_decomp(), {0, 1, 2, 3}); bottom_incrementer.add_op(OpType::CCX, {0, 1, 2}); @@ -528,9 +528,9 @@ static void lemma73(Circuit& circ, const std::pair& pairy) { EdgeVec cut2(b_qubits); for (unsigned i = N - m2 - 1; i < N - 1; ++i) cut2[i - (N - m2 - 1)] = - frontier[i]; // N-1 - (N-m2-1) = m2 (all the controls) + frontier[i]; // N-1 - (N-m2-1) = m2 (all the controls) for (unsigned i = 0; i < b_qubits - (m2 + 1); ++i) - cut2[i + m2] = frontier[i]; // empty wires + cut2[i + m2] = frontier[i]; // empty wires cut2[b_qubits - 1] = frontier[N - 1]; // target new_circ.cut_insert(b_replacement, cut2); @@ -570,9 +570,9 @@ static void lemma73(Circuit& circ, const std::pair& pairy) { EdgeVec cut4(b_qubits); for (unsigned i = N - m2 - 1; i < N - 1; ++i) cut4[i - (N - m2 - 1)] = - frontier[i]; // N-1 - (N-m2-1) = m2 (all the controls) + frontier[i]; // N-1 - (N-m2-1) = m2 (all the controls) for (unsigned i = 0; i < b_qubits - (m2 + 1); ++i) - cut4[i + m2] = frontier[i]; // empty wires + cut4[i + m2] = frontier[i]; // empty wires cut4[b_qubits - 1] = frontier[N - 1]; // target new_circ.cut_insert(b_replacement, cut4); diff --git a/tket/src/Circuit/Multiplexor.cpp b/tket/src/Circuit/Multiplexor.cpp index af9cbfcdb0..7e23b6718c 100644 --- a/tket/src/Circuit/Multiplexor.cpp +++ b/tket/src/Circuit/Multiplexor.cpp @@ -847,13 +847,18 @@ void MultiplexedTensoredU2Box::generate_circuit() const { } } - circ.append(diag_circ); + PhasePolyBox ppb(diag_circ); + // ppb.get_circuit() + circ.append(ppb.get_circuit()); + // circ.append(diag_circ); if ((diag_vec - Eigen::VectorXcd::Constant(1ULL << n_controls_, 1)) .cwiseAbs() .sum() > EPS) { std::vector args(n_controls_); std::iota(std::begin(args), std::end(args), 0); - circ.add_box(DiagonalBox(diag_vec), args); + PhasePolyBox ppb_end_diagonal(DiagonalBox(diag_vec).get_circuit()); + circ.append(ppb_end_diagonal.get_circuit()); + // circ.add_box(DiagonalBox(diag_vec), args); } circ_ = std::make_shared(circ); diff --git a/tket/src/Mapping/LexiRoute.cpp b/tket/src/Mapping/LexiRoute.cpp index 62e6cd9e17..d8706d6f4f 100644 --- a/tket/src/Mapping/LexiRoute.cpp +++ b/tket/src/Mapping/LexiRoute.cpp @@ -645,7 +645,7 @@ std::pair LexiRoute::check_bridge( auto it = this->interacting_uids_.find(swap.first); if (it != this->interacting_uids_.end()) { // => in interaction if (this->architecture_->get_distance(swap.first, Node(it->second)) == - 2) { // => could be bridge + 2) { // => could be bridge // below should always return correct object given prior checks VertPort vp = (*this->mapping_frontier_->linear_boundary->find(swap.first)).second; diff --git a/tket/src/Transformations/BasicOptimisation.cpp b/tket/src/Transformations/BasicOptimisation.cpp index b0dab5d032..6b31f5d5bc 100644 --- a/tket/src/Transformations/BasicOptimisation.cpp +++ b/tket/src/Transformations/BasicOptimisation.cpp @@ -111,9 +111,9 @@ static bool commute_singles_to_front(Circuit &circ) { // helper class subcircuits representing 2qb interactions struct Interaction { Interaction(const Qubit &_q0, const Qubit &_q1) : q0(_q0), q1(_q1) {} - Qubit q0; // Qubit numbers + Qubit q0; // Qubit numbers Qubit q1; - Edge e0; // In edges starting interaction + Edge e0; // In edges starting interaction Edge e1; unsigned count; // Number of two qubit gates in interaction VertexSet vertices; // Vertices in interaction subcircuit diff --git a/tket/src/Transformations/Decomposition.cpp b/tket/src/Transformations/Decomposition.cpp index d008478da9..d1d302cbed 100644 --- a/tket/src/Transformations/Decomposition.cpp +++ b/tket/src/Transformations/Decomposition.cpp @@ -606,6 +606,7 @@ static double best_noise_aware_decomposition( double cx_fid = std::max( fid.CX_fidelity ? fid.CX_fidelity.value() : 0., fid.ZZMax_fidelity ? fid.ZZMax_fidelity.value() : 0.); + std::cout << "CX FID: " << cx_fid << std::endl; bool zzmax_is_better = false; if (cx_fid < EPS) { if (!fid.ZZPhase_fidelity) { @@ -617,8 +618,12 @@ static double best_noise_aware_decomposition( } if (cx_fid > EPS) { for (unsigned n_cx = 0; n_cx <= 3; ++n_cx) { + std::cout << "Cx fid: " << cx_fid << std::endl; + std::cout << "thing: " << pow(cx_fid, n_cx) << std::endl; double ncx_fid = get_CX_fidelity(angles, n_cx) * pow(cx_fid, n_cx); + std::cout << "NCX FID:" << ncx_fid << " ncx: " << n_cx << std::endl; if (ncx_fid > max_fid) { + std::cout << " poggies " << std::endl; max_fid = ncx_fid; best_optype = zzmax_is_better ? OpType::ZZMax : OpType::CX; n_gates = n_cx; diff --git a/tket/test/src/Circuit/test_Boxes.cpp b/tket/test/src/Circuit/test_Boxes.cpp index 587fe74f65..e41998e5bf 100644 --- a/tket/test/src/Circuit/test_Boxes.cpp +++ b/tket/test/src/Circuit/test_Boxes.cpp @@ -203,7 +203,7 @@ SCENARIO("Using Boxes", "[boxes]") { c.add_box(ebox, {0, 1}); Eigen::Matrix4cd U = (+0.5 * i_ * A).exp(); // should be the inverse Unitary2qBox ubox(U); - c.add_box(ubox, {0, 1}); // should act as the identity + c.add_box(ubox, {0, 1}); // should act as the identity Eigen::MatrixXcd uc = tket_sim::get_unitary(c); REQUIRE((uc - Eigen::Matrix4cd::Identity()).cwiseAbs().sum() < ERR_EPS); } diff --git a/tket/test/src/test_ControlDecomp.cpp b/tket/test/src/test_ControlDecomp.cpp index 09f745c672..9a8a50ca2f 100644 --- a/tket/test/src/test_ControlDecomp.cpp +++ b/tket/test/src/test_ControlDecomp.cpp @@ -235,7 +235,7 @@ SCENARIO("Test switch statement") { WHEN("Vertex with 1 edge") { circ.add_blank_wires(1); circ.add_op( - OpType::CnRy, p, {0}); // automatically converted to Ry + OpType::CnRy, p, {0}); // automatically converted to Ry REQUIRE(!Transforms::decomp_controlled_Rys().apply(circ)); REQUIRE(circ.n_vertices() == 3); // 1 in, 1 out, 1 Ry REQUIRE(circ.n_gates() == 1); @@ -415,7 +415,7 @@ SCENARIO("Test incrementer using 1 borrowed qubit") { if (i != 0) bot_qbs[2 * i + 1] = i + k - - 1; // 3,5...n //other qbs we are actually trying to increment + 1; // 3,5...n //other qbs we are actually trying to increment } bot_qbs[1] = n; // incremented qubit 0 in incrementer is bottom one inc.append_qubits(bottom_incrementer, bot_qbs); diff --git a/tket/test/src/test_TwoQubitCanonical.cpp b/tket/test/src/test_TwoQubitCanonical.cpp index e0f3a6aa0e..d321cddd59 100644 --- a/tket/test/src/test_TwoQubitCanonical.cpp +++ b/tket/test/src/test_TwoQubitCanonical.cpp @@ -212,7 +212,7 @@ SCENARIO("Testing two-qubit canonical forms") { 6. + 7. * i_, 7. + 8. * i_, 8. + 9. * i_, 9. + 1. * i_, 1. + 2. * i_, 2. + 3. * i_, 3. + 4. * i_, 4. + 5. * i_, 5. + 6. * i_, 6. + 7. * i_, 7. + 8. * i_; - Eigen::Matrix4cd A = B + B.adjoint(); // hermitian + Eigen::Matrix4cd A = B + B.adjoint(); // hermitian Eigen::Matrix4cd I = Eigen::Matrix4cd::Identity(); Eigen::Matrix4cd U = (I - i_ * A).inverse() * (I + i_ * A); // unitary @@ -272,7 +272,7 @@ SCENARIO("Testing two-qubit canonical forms") { 6. + 7. * i_, 7. + 8. * i_, 8. + 9. * i_, 9. + 1. * i_, 1. + 2. * i_, 2. + 3. * i_, 3. + 4. * i_, 4. + 5. * i_, 5. + 6. * i_, 6. + 7. * i_, 7. + 8. * i_; - Eigen::Matrix4cd A = B + B.adjoint(); // hermitian + Eigen::Matrix4cd A = B + B.adjoint(); // hermitian Eigen::Matrix4cd I = Eigen::Matrix4cd::Identity(); Eigen::Matrix4cd U = (I - i_ * A).inverse() * (I + i_ * A); // unitary Circuit result = two_qubit_canonical(U); @@ -443,7 +443,7 @@ SCENARIO("Testing two qubit decomposition with fidelity tradeoff") { 6. + 7. * i_, 7. + 8. * i_, 8. + 9. * i_, 9. + 1. * i_, 1. + 2. * i_, 2. + 3. * i_, 3. + 4. * i_, 4. + 5. * i_, 5. + 6. * i_, 6. + 7. * i_, 7. + 8. * i_; - Eigen::Matrix4cd A = B + B.adjoint(); // hermitian + Eigen::Matrix4cd A = B + B.adjoint(); // hermitian Eigen::Matrix4cd I = Eigen::Matrix4cd::Identity(); Eigen::Matrix4cd U = (I - i_ * A).inverse() * (I + i_ * A); // unitary auto get_fid = [&U](const Eigen::Matrix4cd &Up) { From 95d49028b840022ebcec54eae02d24256706edf5 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Mon, 24 Jul 2023 09:55:16 +0100 Subject: [PATCH 02/29] Undo accidental commited changes --- tket/src/Circuit/Multiplexor.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tket/src/Circuit/Multiplexor.cpp b/tket/src/Circuit/Multiplexor.cpp index 7e23b6718c..af9cbfcdb0 100644 --- a/tket/src/Circuit/Multiplexor.cpp +++ b/tket/src/Circuit/Multiplexor.cpp @@ -847,18 +847,13 @@ void MultiplexedTensoredU2Box::generate_circuit() const { } } - PhasePolyBox ppb(diag_circ); - // ppb.get_circuit() - circ.append(ppb.get_circuit()); - // circ.append(diag_circ); + circ.append(diag_circ); if ((diag_vec - Eigen::VectorXcd::Constant(1ULL << n_controls_, 1)) .cwiseAbs() .sum() > EPS) { std::vector args(n_controls_); std::iota(std::begin(args), std::end(args), 0); - PhasePolyBox ppb_end_diagonal(DiagonalBox(diag_vec).get_circuit()); - circ.append(ppb_end_diagonal.get_circuit()); - // circ.add_box(DiagonalBox(diag_vec), args); + circ.add_box(DiagonalBox(diag_vec), args); } circ_ = std::make_shared(circ); From 63de41ff3e8419e9404b676e7a9ef5be0ccaa08c Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Mon, 24 Jul 2023 16:23:25 +0100 Subject: [PATCH 03/29] Remove comments, working up to phase --- tket/src/Circuit/CircPool.cpp | 72 ++++++++++++++-------- tket/src/Transformations/Decomposition.cpp | 6 -- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index 7ddc249077..bd2cc0d90e 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -946,7 +946,6 @@ Circuit TK2_using_CX(const Expr &alpha, const Expr &beta, const Expr &gamma) { * @return Circuit TK2-equivalent up to wire swap circuit */ static Circuit TK2_swap_replacement(std::array angles) { - std::cout << " In TK2 SWAP replacement." << std::endl; if (!in_weyl_chamber(angles)) { throw std::domain_error("TK2 params are not normalised to Weyl chamber."); } @@ -954,7 +953,7 @@ static Circuit TK2_swap_replacement(std::array angles) { bool implicit_swap = false; // default to no implicit swap // Generate to support allowing swap gates - Circuit pre, post; + // Circuit pre, post; std::array angles_swapped; // Swapped circuit Circuit swap_circ(2); @@ -984,12 +983,10 @@ static Circuit TK2_swap_replacement(std::array angles) { } // Check if fewer gates can be used. - if (last_angle <= 2) { if (equiv_0(angles[2], 4) && equiv_0(angles[1], 4)) { n_gates = 1; - } else if (equiv_0(angles_swapped[2], 4) && equiv_0(angles_swapped[1], 4)) - { + } else if (equiv_0(angles_swapped[2], 4) && equiv_0(angles_swapped[1], 4)) { implicit_swap = true; n_gates = 1; } else if (equiv_0(angles[2], 4)) { @@ -1000,28 +997,53 @@ static Circuit TK2_swap_replacement(std::array angles) { } } else { double max_fid = 0.; - double ncx_fid; - for (unsigned n_cx = 0; n_cx <= 3; ++n_cx) { - switch (n_cx) { - case 0: - ncx_fid = - trace_fidelity(angles_eval[0], angles_eval[1], angles_eval[2]); - case 1: - ncx_fid = trace_fidelity( - 0.5 - angles_eval[0], angles_eval[1], angles_eval[2]); - case 2: - ncx_fid = trace_fidelity(0, 0, angles_eval[2]); - default: - ncx_fid = 1.; - } - if (ncx_fid > max_fid) { - max_fid = ncx_fid; - n_gates = n_cx; - } + + auto [a, b, c] = angles_eval; + auto [as, bs, cs] = angles_eval_swapped; + + double ncx_fid = trace_fidelity(a, b, c); + if (ncx_fid > max_fid) { + max_fid = ncx_fid; + n_gates = 0; + implicit_swap = false; + } + ncx_fid = trace_fidelity(as, bs, cs); + if (ncx_fid > max_fid) { + max_fid = ncx_fid; + n_gates = 0; + implicit_swap = true; + } + ncx_fid = trace_fidelity(0.5 - a, b, c); + if (ncx_fid > max_fid) { + max_fid = ncx_fid; + n_gates = 1; + implicit_swap = false; + } + ncx_fid = trace_fidelity(0.5 - as, bs, cs); + if (ncx_fid > max_fid) { + max_fid = ncx_fid; + n_gates = 1; + implicit_swap = true; + } + ncx_fid = trace_fidelity(0, 0, c); + if (ncx_fid > max_fid) { + max_fid = ncx_fid; + n_gates = 2; + implicit_swap = false; + } + ncx_fid = trace_fidelity(0, 0, cs); + if (ncx_fid > max_fid) { + max_fid = ncx_fid; + n_gates = 2; + implicit_swap = true; + } + if (1 > max_fid) { + max_fid = 1; + n_gates = 3; + implicit_swap = false; } } - // Build circuit for substitution. Circuit sub(2); switch (n_gates) { @@ -1052,6 +1074,7 @@ static Circuit TK2_swap_replacement(std::array angles) { // This decomposition can leave many extraneous single qubits gates: squash // them into TK1 that can be resynthesised Transforms::squash_1qb_to_tk1().apply(sub); + std::cout << "replacement circuit: " << sub << std::endl; return sub; } @@ -1066,7 +1089,6 @@ Circuit TK2_using_CX_and_swap( TKET_ASSERT(params.size() == 3); std::array arr_params = {params[0], params[1], params[2]}; Circuit rep = TK2_swap_replacement(arr_params); - c.substitute(rep, v, Circuit::VertexDeletion::Yes); break; } diff --git a/tket/src/Transformations/Decomposition.cpp b/tket/src/Transformations/Decomposition.cpp index d1d302cbed..d2e08f2e4b 100644 --- a/tket/src/Transformations/Decomposition.cpp +++ b/tket/src/Transformations/Decomposition.cpp @@ -578,7 +578,6 @@ static double get_ZZPhase_fidelity( static double get_CX_fidelity(const std::array &k, unsigned nb_cx) { TKET_ASSERT(nb_cx < 4); auto [a, b, c] = k; - // gate fidelity achievable with 0,...,3 cnots // this is fully determined by the information content k and is optimal // see PhysRevA 71.062331 (2005) for more details on this @@ -606,7 +605,6 @@ static double best_noise_aware_decomposition( double cx_fid = std::max( fid.CX_fidelity ? fid.CX_fidelity.value() : 0., fid.ZZMax_fidelity ? fid.ZZMax_fidelity.value() : 0.); - std::cout << "CX FID: " << cx_fid << std::endl; bool zzmax_is_better = false; if (cx_fid < EPS) { if (!fid.ZZPhase_fidelity) { @@ -618,12 +616,8 @@ static double best_noise_aware_decomposition( } if (cx_fid > EPS) { for (unsigned n_cx = 0; n_cx <= 3; ++n_cx) { - std::cout << "Cx fid: " << cx_fid << std::endl; - std::cout << "thing: " << pow(cx_fid, n_cx) << std::endl; double ncx_fid = get_CX_fidelity(angles, n_cx) * pow(cx_fid, n_cx); - std::cout << "NCX FID:" << ncx_fid << " ncx: " << n_cx << std::endl; if (ncx_fid > max_fid) { - std::cout << " poggies " << std::endl; max_fid = ncx_fid; best_optype = zzmax_is_better ? OpType::ZZMax : OpType::CX; n_gates = n_cx; From ca6cfc8a02fc09b3bf57b7cafb4017f1afd29af8 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Wed, 26 Jul 2023 13:23:11 +0100 Subject: [PATCH 04/29] Update CircPool.cpp --- tket/src/Circuit/CircPool.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index bd2cc0d90e..4cb98ab7ae 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -953,7 +953,7 @@ static Circuit TK2_swap_replacement(std::array angles) { bool implicit_swap = false; // default to no implicit swap // Generate to support allowing swap gates - // Circuit pre, post; + Circuit pre, post; std::array angles_swapped; // Swapped circuit Circuit swap_circ(2); @@ -1069,12 +1069,13 @@ static Circuit TK2_swap_replacement(std::array angles) { Circuit swap(2); swap.add_op(OpType::SWAP, {0, 1}); sub = pre >> sub >> post >> swap; + // Transforms::squash_1qb_to_tk1().apply(sub); + // sub = sub >> swap; sub.replace_SWAPs(); } // This decomposition can leave many extraneous single qubits gates: squash // them into TK1 that can be resynthesised Transforms::squash_1qb_to_tk1().apply(sub); - std::cout << "replacement circuit: " << sub << std::endl; return sub; } From a3af378b50f48eae2237988a1ea351134888671f Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Wed, 26 Jul 2023 13:23:31 +0100 Subject: [PATCH 05/29] Update Rebase.cpp --- tket/src/Transformations/Rebase.cpp | 50 +++++++++++++---------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/tket/src/Transformations/Rebase.cpp b/tket/src/Transformations/Rebase.cpp index 2886afaaf5..a4e302bcc9 100644 --- a/tket/src/Transformations/Rebase.cpp +++ b/tket/src/Transformations/Rebase.cpp @@ -122,7 +122,7 @@ static bool standard_rebase_via_tk2( tk2_replacement) { bool success = false; VertexSet bin; - + bool TK2_allowed = allowed_gates.contains(OpType::TK2); // 1. Replace all multi-qubit gates outside the target gateset to TK2. for (const Vertex& v : circ.all_vertices()) { Op_ptr op = circ.get_Op_ptr_from_Vertex(v); @@ -139,6 +139,27 @@ static bool standard_rebase_via_tk2( continue; // need to convert Circuit replacement = TK2_circ_from_multiq(op); + // 2. If TK2 gates are not allowed in the target gateset we find a + // replacement circuit by decomposing them + if (!TK2_allowed) { + VertexSet TK2_bin; + for (const Vertex& u : replacement.all_vertices()) { + Op_ptr op = circ.get_Op_ptr_from_Vertex(u); + TKET_ASSERT(op->get_type() != OpType::Conditional); + if (op->get_type() == OpType::TK2) { + std::vector params = op->get_params(); + TKET_ASSERT(params.size() == 3); + Circuit u_replacement = + tk2_replacement(params[0], params[1], params[2]); + replacement.substitute(u_replacement, u, Circuit::VertexDeletion::No); + TK2_bin.insert(u); + } + } + Transforms::squash_1qb_to_tk1().apply(replacement); + remove_redundancies().apply(replacement); + replacement.remove_vertices( + TK2_bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes); + } if (conditional) { circ.substitute_conditional(replacement, v, Circuit::VertexDeletion::No); } else { @@ -148,32 +169,6 @@ static bool standard_rebase_via_tk2( success = true; } - // 2. If TK2 is not in the target gateset, decompose TK2 gates. - if (!allowed_gates.contains(OpType::TK2)) { - for (const Vertex& v : circ.all_vertices()) { - Op_ptr op = circ.get_Op_ptr_from_Vertex(v); - bool conditional = op->get_type() == OpType::Conditional; - if (conditional) { - const Conditional& cond = static_cast(*op); - op = cond.get_op(); - } - if (op->get_type() == OpType::TK2) { - std::vector params = op->get_params(); - TKET_ASSERT(params.size() == 3); - Circuit replacement = tk2_replacement(params[0], params[1], params[2]); - remove_redundancies().apply(replacement); - if (conditional) { - circ.substitute_conditional( - replacement, v, Circuit::VertexDeletion::No); - } else { - circ.substitute(replacement, v, Circuit::VertexDeletion::No); - } - bin.insert(v); - success = true; - } - } - } - // 3. Replace 0- and 1-qubit gates by converting to TK1 and replacing. for (const Vertex& v : circ.all_vertices()) { if (bin.contains(v)) continue; @@ -198,7 +193,6 @@ static bool standard_rebase_via_tk2( bin.insert(v); success = true; } - circ.remove_vertices( bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes); return success; From 0543bbc29549be05ce878278d7099daafb0e35d1 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Wed, 26 Jul 2023 13:58:22 +0100 Subject: [PATCH 06/29] Remove comments from transform_test and add gate count assertions --- pytket/tests/transform_test.py | 88 +++++++++++++++------------------- 1 file changed, 39 insertions(+), 49 deletions(-) diff --git a/pytket/tests/transform_test.py b/pytket/tests/transform_test.py index 2959096f7f..08c4ade301 100644 --- a/pytket/tests/transform_test.py +++ b/pytket/tests/transform_test.py @@ -436,7 +436,7 @@ def test_pauli_graph_synth() -> None: num_cxs = c.n_gates_of_type(OpType.CX) cx_counts.append(num_cxs) - for (i, count) in enumerate(cx_counts): + for i, count in enumerate(cx_counts): if i == 0: continue assert count < cx_counts[i - 1] @@ -1215,111 +1215,101 @@ def test_round_angles() -> None: assert Transform.round_angles(8).apply(circ0) assert circ0 == circ1 -from pytket.passes import NormaliseTK2, DecomposeTK2 + def test_auto_rebase_with_swap() -> None: swap_pass = auto_rebase_pass({OpType.ZZMax, OpType.PhasedX, OpType.Rz}, True) no_swap_pass = auto_rebase_pass({OpType.ZZMax, OpType.PhasedX, OpType.Rz}, False) c_swap = Circuit(2).ISWAPMax(0, 1) swap_pass.apply(c_swap) - - # print("\n",c_swap.get_commands()) - - - # c_swap = Circuit(2).ISWAPMax(0,1) - # auto_rebase_pass({OpType.TK2, OpType.PhasedX, OpType.Rz}).apply(c_swap) - # NormaliseTK2().apply(c_swap) - # DecomposeTK2().apply(c_swap) - # print("\n",c_swap.get_commands()) - - assert c_swap.n_gates_of_type(OpType.ZZMax) == 1 + assert c_swap.n_gates == 4 iqp = c_swap.implicit_qubit_permutation() assert iqp[Qubit(0)] == Qubit(1) assert iqp[Qubit(1)] == Qubit(0) c_no_swap = Circuit(2).ISWAPMax(0, 1) no_swap_pass.apply(c_no_swap) assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 2 - - print("\nISWAPMax:", c_swap.n_gates, c_no_swap.n_gates) + assert c_no_swap.n_gates == 13 c_swap = Circuit(2).Sycamore(0, 1) swap_pass.apply(c_swap) - print(c_swap.get_commands()) - print(c_swap.n_gates_of_type(OpType.ZZMax)) assert c_swap.n_gates_of_type(OpType.ZZMax) == 2 + assert c_swap.n_gates == 12 iqp = c_swap.implicit_qubit_permutation() assert iqp[Qubit(0)] == Qubit(1) assert iqp[Qubit(1)] == Qubit(0) + c_no_swap = Circuit(2).Sycamore(0, 1) - no_swap_pass.apply(c_swap) + no_swap_pass.apply(c_no_swap) assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 3 - print("\nSycamore:", c_swap.n_gates, c_no_swap.n_gates) + assert c_no_swap.n_gates == 16 c_swap = Circuit(2).ISWAP(0.3, 0, 1) swap_pass.apply(c_swap) assert c_swap.n_gates_of_type(OpType.ZZMax) == 2 + assert c_swap.n_gates == 13 iqp = c_swap.implicit_qubit_permutation() assert iqp[Qubit(0)] == Qubit(0) assert iqp[Qubit(1)] == Qubit(1) c_no_swap = Circuit(2).ISWAP(0.3, 0, 1) no_swap_pass.apply(c_no_swap) assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 2 + assert c_no_swap.n_gates == 13 - print("\nISWAP:", c_swap.n_gates, c.n_gates) c_swap = Circuit(2).ISWAPMax(0, 1).ISWAPMax(1, 0) swap_pass.apply(c_swap) assert c_swap.n_gates_of_type(OpType.ZZMax) == 2 + assert c_swap.n_gates == 8 iqp = c_swap.implicit_qubit_permutation() assert iqp[Qubit(0)] == Qubit(0) assert iqp[Qubit(1)] == Qubit(1) c_no_swap = Circuit(2).ISWAPMax(0, 1).ISWAPMax(1, 0) no_swap_pass.apply(c_no_swap) assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 4 + assert c_no_swap.n_gates == 26 c_swap = Circuit(2).SWAP(0, 1) swap_pass.apply(c_swap) assert c_swap.n_gates_of_type(OpType.ZZMax) == 0 + assert c_swap.n_gates == 0 iqp = c_swap.implicit_qubit_permutation() assert iqp[Qubit(0)] == Qubit(1) assert iqp[Qubit(1)] == Qubit(0) c_no_swap = Circuit(2).SWAP(0, 1) no_swap_pass.apply(c_no_swap) assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 3 - - print("\nSWAP:", c_swap.n_gates, c.n_gates) + assert c_no_swap.n_gates == 16 c_swap = Circuit(2).ZZMax(0, 1) swap_pass.apply(c_swap) assert c_swap.n_gates == 1 - print("\nZZMax:", c_swap.n_gates, c.n_gates) - if __name__ == "__main__": - # test_remove_redundancies() - # test_reduce_singles() - # test_commute() - # test_KAK() - # test_basic_rebases() - # test_post_routing() - # test_phase_gadget() - # test_Cliffords() - # test_Pauli_gadget() - # test_cons_sequencing() - # test_list_sequencing() - # test_basic_repeat() - # test_while_repeat() - # test_implicit_swaps_1() - # test_implicit_swaps_2() - # test_implicit_swaps_3() - # test_decompose_swap_to_cx() - # test_noncontiguous_DefaultMappingPass_arc() - # test_RoutingPass() - # test_DefaultMappingPass() - # test_CXMappingPass() - # test_CXMappingPass_correctness() - # test_CXMappingPass_terminates() - # test_FullMappingPass() - # test_KAK_with_ClassicalExpBox() + test_remove_redundancies() + test_reduce_singles() + test_commute() + test_KAK() + test_basic_rebases() + test_post_routing() + test_phase_gadget() + test_Cliffords() + test_Pauli_gadget() + test_cons_sequencing() + test_list_sequencing() + test_basic_repeat() + test_while_repeat() + test_implicit_swaps_1() + test_implicit_swaps_2() + test_implicit_swaps_3() + test_decompose_swap_to_cx() + test_noncontiguous_DefaultMappingPass_arc() + test_RoutingPass() + test_DefaultMappingPass() + test_CXMappingPass() + test_CXMappingPass_correctness() + test_CXMappingPass_terminates() + test_FullMappingPass() + test_KAK_with_ClassicalExpBox() test_auto_rebase_with_swap() From f30734ab07376b413a255da7edb36977ce3a60fe Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Wed, 26 Jul 2023 14:06:51 +0100 Subject: [PATCH 07/29] Remove redundant commented out code --- tket/src/Circuit/CircPool.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index 4cb98ab7ae..4bf9f4607c 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -1069,8 +1069,6 @@ static Circuit TK2_swap_replacement(std::array angles) { Circuit swap(2); swap.add_op(OpType::SWAP, {0, 1}); sub = pre >> sub >> post >> swap; - // Transforms::squash_1qb_to_tk1().apply(sub); - // sub = sub >> swap; sub.replace_SWAPs(); } // This decomposition can leave many extraneous single qubits gates: squash From 8e64eb085854cf4605f6d3cd2c0e3d633144b618 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Wed, 26 Jul 2023 14:07:13 +0100 Subject: [PATCH 08/29] bump --- 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 61ec1b7f37..b7e650b36c 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.2.28@tket/stable") + self.requires("tket/1.2.29@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.3@tket/stable") diff --git a/tket/conanfile.py b/tket/conanfile.py index b7d40a7b05..5e0343c3cd 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.28" + version = "1.2.29" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" From 493ce0a4232fc61f727243b0a0a2dba3a6bd64c9 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Wed, 26 Jul 2023 16:31:43 +0100 Subject: [PATCH 09/29] add c++ tests --- tket/test/src/test_CompilerPass.cpp | 109 ++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/tket/test/src/test_CompilerPass.cpp b/tket/test/src/test_CompilerPass.cpp index eac98903bd..73c334997b 100644 --- a/tket/test/src/test_CompilerPass.cpp +++ b/tket/test/src/test_CompilerPass.cpp @@ -1722,5 +1722,114 @@ SCENARIO("Flatten and relabel registers") { } } +SCENARIO("Custom rebase pass with implicit wire swaps.") { + OpTypeSet allowed_gates_cx = {OpType::PhasedX, OpType::Rz, OpType::CX}; + PassPtr pp_rebase_cx = gen_rebase_pass_via_tk2( + allowed_gates_cx, CircPool::TK2_using_CX_and_swap, + CircPool::tk1_to_PhasedXRz); + OpTypeSet allowed_gates_zzmax = {OpType::PhasedX, OpType::Rz, OpType::ZZMax}; + PassPtr pp_rebase_zzmax = gen_rebase_pass_via_tk2( + allowed_gates_zzmax, CircPool::TK2_using_ZZMax_and_swap, + CircPool::tk1_to_PhasedXRz); + + GIVEN("Targeting CX gates, ISWAPMax gate.") { + Circuit c(2); + c.add_op(OpType::ISWAPMax, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_cx->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 1); + } + GIVEN("Targeting CX gates, Sycamore gate.") { + Circuit c(2); + c.add_op(OpType::Sycamore, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_cx->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 2); + } + GIVEN("Targeting CX gates, ISWAP gate.") { + Circuit c(2); + c.add_op(OpType::ISWAP, 0.3, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_cx->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 2); + } + GIVEN("Targeting CX gates, SWAP gate.") { + Circuit c(2); + c.add_op(OpType::SWAP, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_cx->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 0); + } + GIVEN("Targeting CX gates, CX gate.") { + Circuit c(2); + c.add_op(OpType::CX, {0, 1}); + CompilationUnit cu(c); + CHECK(!pp_rebase_cx->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 1); + } + GIVEN("Targeting CX gates, ZZMAX gate.") { + Circuit c(2); + c.add_op(OpType::ZZMax, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_cx->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 1); + } + GIVEN("Targeting CX gates, ZZPhasegate.") { + Circuit c(2); + c.add_op(OpType::ZZPhase, 0.3, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_cx->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 2); + } + GIVEN("Targeting ZZMax gates, ISWAPMax gate.") { + Circuit c(2); + c.add_op(OpType::ISWAPMax, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_zzmax->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 1); + } + GIVEN("Targeting ZZMax gates, ISWAP gate.") { + Circuit c(2); + c.add_op(OpType::ISWAP, 0.3, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_zzmax->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 2); + } + GIVEN("Targeting ZZMax gates, Sycamore gate.") { + Circuit c(2); + c.add_op(OpType::Sycamore, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_zzmax->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 2); + } + GIVEN("Targeting ZZMax gates, SWAP gate.") { + Circuit c(2); + c.add_op(OpType::SWAP, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_zzmax->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 0); + } + GIVEN("Targeting ZZMax gates, CX gate.") { + Circuit c(2); + c.add_op(OpType::CX, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_zzmax->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 1); + } + GIVEN("Targeting ZZMax gates, ZZMAX gate.") { + Circuit c(2); + c.add_op(OpType::ZZMax, {0, 1}); + CompilationUnit cu(c); + CHECK(!pp_rebase_zzmax->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 1); + } + GIVEN("Targeting ZZMax gates, ZZPhasegate.") { + Circuit c(2); + c.add_op(OpType::ZZPhase, 0.3, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_zzmax->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 2); + } +} } // namespace test_CompilerPass } // namespace tket From f3726c242b1cbd41e00ab05a1c428fcd29d09626 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Wed, 26 Jul 2023 16:31:55 +0100 Subject: [PATCH 10/29] update auto rebase and add tests for CX case --- pytket/pytket/passes/auto_rebase.py | 10 ++++- pytket/tests/transform_test.py | 68 ++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/pytket/pytket/passes/auto_rebase.py b/pytket/pytket/passes/auto_rebase.py index b739bc97d7..1eb2693ce4 100644 --- a/pytket/pytket/passes/auto_rebase.py +++ b/pytket/pytket/passes/auto_rebase.py @@ -51,6 +51,7 @@ def _TK2_using_TK2(a: Param, b: Param, c: Param) -> Circuit: OpType.ZZMax: _library._TK2_using_ZZMax_and_swap, } + 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. @@ -85,7 +86,7 @@ def get_tk2_decomposition( for k, fn in _TK2_CIRCS.items(): if k in gateset: return fn - + raise NoAutoRebase("No known decomposition from TK2 to given gateset") @@ -140,6 +141,13 @@ def auto_rebase_pass(gateset: Set[OpType], allow_swaps: bool = False) -> RebaseC """ tk1 = get_TK1_decomposition_function(gateset) + if allow_swaps: + try: + return RebaseCustom( + gateset, get_tk2_decomposition(gateset, allow_swaps), tk1 + ) + except NoAutoRebase: + pass # if the gateset has CX but not TK2, rebase via CX if OpType.CX in gateset and OpType.TK2 not in gateset: return RebaseCustom(gateset, _library._CX(), tk1) diff --git a/pytket/tests/transform_test.py b/pytket/tests/transform_test.py index 08c4ade301..a23a696a68 100644 --- a/pytket/tests/transform_test.py +++ b/pytket/tests/transform_test.py @@ -1216,7 +1216,70 @@ def test_round_angles() -> None: assert circ0 == circ1 -def test_auto_rebase_with_swap() -> None: +def test_auto_rebase_with_swap_cx() -> None: + swap_pass = auto_rebase_pass({OpType.CX, OpType.PhasedX, OpType.Rz}, True) + no_swap_pass = auto_rebase_pass({OpType.CX, OpType.PhasedX, OpType.Rz}, False) + + c_swap = Circuit(2).ISWAPMax(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.CX) == 1 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(1) + assert iqp[Qubit(1)] == Qubit(0) + c_no_swap = Circuit(2).ISWAPMax(0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.CX) == 2 + + c_swap = Circuit(2).Sycamore(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.CX) == 2 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(1) + assert iqp[Qubit(1)] == Qubit(0) + c_no_swap = Circuit(2).Sycamore(0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.CX) == 3 + + c_swap = Circuit(2).ISWAP(0.3, 0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.CX) == 2 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(0) + assert iqp[Qubit(1)] == Qubit(1) + c_no_swap = Circuit(2).ISWAP(0.3, 0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.CX) == 2 + + c_swap = Circuit(2).ISWAPMax(0, 1).ISWAPMax(1, 0) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.CX) == 2 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(0) + assert iqp[Qubit(1)] == Qubit(1) + c_no_swap = Circuit(2).ISWAPMax(0, 1).ISWAPMax(1, 0) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.CX) == 4 + + c_swap = Circuit(2).SWAP(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.CX) == 0 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(1) + assert iqp[Qubit(1)] == Qubit(0) + c_no_swap = Circuit(2).SWAP(0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.CX) == 3 + + c_swap = Circuit(2).ZZMax(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.CX) == 1 + + c_swap = Circuit(2).CX(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates == 1 + + +def test_auto_rebase_with_swap_zzmax() -> None: swap_pass = auto_rebase_pass({OpType.ZZMax, OpType.PhasedX, OpType.Rz}, True) no_swap_pass = auto_rebase_pass({OpType.ZZMax, OpType.PhasedX, OpType.Rz}, False) @@ -1312,4 +1375,5 @@ def test_auto_rebase_with_swap() -> None: test_CXMappingPass_terminates() test_FullMappingPass() test_KAK_with_ClassicalExpBox() - test_auto_rebase_with_swap() + test_auto_rebase_with_swap_cx() + test_auto_rebase_with_swap_zzmax() From 387fbbed40eeec49e3a611d6d151f6a2c408bcfc Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Wed, 26 Jul 2023 17:12:33 +0100 Subject: [PATCH 11/29] Update CircPool.cpp --- tket/src/Circuit/CircPool.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index 4bf9f4607c..8a3775fbce 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -1043,7 +1043,6 @@ static Circuit TK2_swap_replacement(std::array angles) { implicit_swap = false; } } - // Build circuit for substitution. Circuit sub(2); switch (n_gates) { From 5ef2b9b8ee05e0a8ee9b770c1ed016efbbb0b2d3 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Thu, 27 Jul 2023 09:22:55 +0100 Subject: [PATCH 12/29] clang format update --- tket/include/tket/Circuit/Command.hpp | 2 +- .../tket/Predicates/CompilationUnit.hpp | 2 +- tket/src/ArchAwareSynth/Path.cpp | 2 +- tket/src/Circuit/ControlledGates.cpp | 18 +++++++++--------- tket/src/Mapping/LexiRoute.cpp | 2 +- tket/src/Transformations/BasicOptimisation.cpp | 4 ++-- tket/test/src/Circuit/test_Boxes.cpp | 2 +- tket/test/src/test_ControlDecomp.cpp | 4 ++-- tket/test/src/test_TwoQubitCanonical.cpp | 6 +++--- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/tket/include/tket/Circuit/Command.hpp b/tket/include/tket/Circuit/Command.hpp index 9cc5540c59..85f2875223 100644 --- a/tket/include/tket/Circuit/Command.hpp +++ b/tket/include/tket/Circuit/Command.hpp @@ -86,7 +86,7 @@ class Command { Op_ptr op_ptr; unit_vector_t args; // indexed by port numbering std::optional opgroup; - Vertex vert; // vertex in the DAG + Vertex vert; // vertex in the DAG }; JSON_DECL(Command) diff --git a/tket/include/tket/Predicates/CompilationUnit.hpp b/tket/include/tket/Predicates/CompilationUnit.hpp index bf9f824267..2714f187d0 100644 --- a/tket/include/tket/Predicates/CompilationUnit.hpp +++ b/tket/include/tket/Predicates/CompilationUnit.hpp @@ -58,7 +58,7 @@ class CompilationUnit { void empty_cache() const; void initialize_cache() const; void initialize_maps(); - Circuit circ_; // modified continuously + Circuit circ_; // modified continuously PredicatePtrMap target_preds; // these are the predicates you WANT your circuit to // satisfy by the end of your Compiler Passes diff --git a/tket/src/ArchAwareSynth/Path.cpp b/tket/src/ArchAwareSynth/Path.cpp index 3a9de1f2f7..dad2700494 100644 --- a/tket/src/ArchAwareSynth/Path.cpp +++ b/tket/src/ArchAwareSynth/Path.cpp @@ -102,7 +102,7 @@ PathHandler PathHandler::construct_acyclic_handler() const { std::list current_layer_vertices{centre_node}; std::list next_layer_vertices; std::vector> parents_neighbours( - n); // pair(num_neighbours, parent vertex) + n); // pair(num_neighbours, parent vertex) std::vector vertices_in_tree( n, 0); // track which vertices are in the acyclic graph vertices_in_tree[centre_node] = 1; diff --git a/tket/src/Circuit/ControlledGates.cpp b/tket/src/Circuit/ControlledGates.cpp index 3f243080fb..1395182f63 100644 --- a/tket/src/Circuit/ControlledGates.cpp +++ b/tket/src/Circuit/ControlledGates.cpp @@ -39,7 +39,7 @@ typedef std::vector> candidate_t; // each CnX candidate to decompose needs a spare wire to put // some extra controls on -static Circuit lemma72(unsigned control_m); // rule lemma 7.2 +static Circuit lemma72(unsigned control_m); // rule lemma 7.2 static void lemma73( Circuit& circ, const std::pair& pairy); // rule lemma 7.3 @@ -87,9 +87,9 @@ Circuit incrementer_borrow_1_qubit(unsigned n) { cnx_top = C4X_normal_decomp(); cnx1_qbs = {0, 1, 2, 3, n}; } else { - cnx_top = lemma72(k); // k controls on cnx + cnx_top = lemma72(k); // k controls on cnx cnx1_qbs.resize( - 2 * k - 2); // size of replacement using borrowed qbs = 2*k-1 + 2 * k - 2); // size of replacement using borrowed qbs = 2*k-1 std::iota(cnx1_qbs.begin(), cnx1_qbs.end(), 0); cnx1_qbs.push_back(n); // target is last qubit } @@ -104,11 +104,11 @@ Circuit incrementer_borrow_1_qubit(unsigned n) { if (i != 0) bot_qbs[2 * i + 1] = i + j - - 1; // 3,5...n //other qbs we are actually trying to increment + 1; // 3,5...n //other qbs we are actually trying to increment } bot_qbs[1] = n; // incremented qubit 0 in incrementer is bottom one } else { - if (j == 4) { // code is unreachable if j<4 + if (j == 4) { // code is unreachable if j<4 bottom_incrementer.add_blank_wires(4); bottom_incrementer.append_qubits(C3X_normal_decomp(), {0, 1, 2, 3}); bottom_incrementer.add_op(OpType::CCX, {0, 1, 2}); @@ -528,9 +528,9 @@ static void lemma73(Circuit& circ, const std::pair& pairy) { EdgeVec cut2(b_qubits); for (unsigned i = N - m2 - 1; i < N - 1; ++i) cut2[i - (N - m2 - 1)] = - frontier[i]; // N-1 - (N-m2-1) = m2 (all the controls) + frontier[i]; // N-1 - (N-m2-1) = m2 (all the controls) for (unsigned i = 0; i < b_qubits - (m2 + 1); ++i) - cut2[i + m2] = frontier[i]; // empty wires + cut2[i + m2] = frontier[i]; // empty wires cut2[b_qubits - 1] = frontier[N - 1]; // target new_circ.cut_insert(b_replacement, cut2); @@ -570,9 +570,9 @@ static void lemma73(Circuit& circ, const std::pair& pairy) { EdgeVec cut4(b_qubits); for (unsigned i = N - m2 - 1; i < N - 1; ++i) cut4[i - (N - m2 - 1)] = - frontier[i]; // N-1 - (N-m2-1) = m2 (all the controls) + frontier[i]; // N-1 - (N-m2-1) = m2 (all the controls) for (unsigned i = 0; i < b_qubits - (m2 + 1); ++i) - cut4[i + m2] = frontier[i]; // empty wires + cut4[i + m2] = frontier[i]; // empty wires cut4[b_qubits - 1] = frontier[N - 1]; // target new_circ.cut_insert(b_replacement, cut4); diff --git a/tket/src/Mapping/LexiRoute.cpp b/tket/src/Mapping/LexiRoute.cpp index d8706d6f4f..62e6cd9e17 100644 --- a/tket/src/Mapping/LexiRoute.cpp +++ b/tket/src/Mapping/LexiRoute.cpp @@ -645,7 +645,7 @@ std::pair LexiRoute::check_bridge( auto it = this->interacting_uids_.find(swap.first); if (it != this->interacting_uids_.end()) { // => in interaction if (this->architecture_->get_distance(swap.first, Node(it->second)) == - 2) { // => could be bridge + 2) { // => could be bridge // below should always return correct object given prior checks VertPort vp = (*this->mapping_frontier_->linear_boundary->find(swap.first)).second; diff --git a/tket/src/Transformations/BasicOptimisation.cpp b/tket/src/Transformations/BasicOptimisation.cpp index 6b31f5d5bc..b0dab5d032 100644 --- a/tket/src/Transformations/BasicOptimisation.cpp +++ b/tket/src/Transformations/BasicOptimisation.cpp @@ -111,9 +111,9 @@ static bool commute_singles_to_front(Circuit &circ) { // helper class subcircuits representing 2qb interactions struct Interaction { Interaction(const Qubit &_q0, const Qubit &_q1) : q0(_q0), q1(_q1) {} - Qubit q0; // Qubit numbers + Qubit q0; // Qubit numbers Qubit q1; - Edge e0; // In edges starting interaction + Edge e0; // In edges starting interaction Edge e1; unsigned count; // Number of two qubit gates in interaction VertexSet vertices; // Vertices in interaction subcircuit diff --git a/tket/test/src/Circuit/test_Boxes.cpp b/tket/test/src/Circuit/test_Boxes.cpp index e41998e5bf..587fe74f65 100644 --- a/tket/test/src/Circuit/test_Boxes.cpp +++ b/tket/test/src/Circuit/test_Boxes.cpp @@ -203,7 +203,7 @@ SCENARIO("Using Boxes", "[boxes]") { c.add_box(ebox, {0, 1}); Eigen::Matrix4cd U = (+0.5 * i_ * A).exp(); // should be the inverse Unitary2qBox ubox(U); - c.add_box(ubox, {0, 1}); // should act as the identity + c.add_box(ubox, {0, 1}); // should act as the identity Eigen::MatrixXcd uc = tket_sim::get_unitary(c); REQUIRE((uc - Eigen::Matrix4cd::Identity()).cwiseAbs().sum() < ERR_EPS); } diff --git a/tket/test/src/test_ControlDecomp.cpp b/tket/test/src/test_ControlDecomp.cpp index 9a8a50ca2f..09f745c672 100644 --- a/tket/test/src/test_ControlDecomp.cpp +++ b/tket/test/src/test_ControlDecomp.cpp @@ -235,7 +235,7 @@ SCENARIO("Test switch statement") { WHEN("Vertex with 1 edge") { circ.add_blank_wires(1); circ.add_op( - OpType::CnRy, p, {0}); // automatically converted to Ry + OpType::CnRy, p, {0}); // automatically converted to Ry REQUIRE(!Transforms::decomp_controlled_Rys().apply(circ)); REQUIRE(circ.n_vertices() == 3); // 1 in, 1 out, 1 Ry REQUIRE(circ.n_gates() == 1); @@ -415,7 +415,7 @@ SCENARIO("Test incrementer using 1 borrowed qubit") { if (i != 0) bot_qbs[2 * i + 1] = i + k - - 1; // 3,5...n //other qbs we are actually trying to increment + 1; // 3,5...n //other qbs we are actually trying to increment } bot_qbs[1] = n; // incremented qubit 0 in incrementer is bottom one inc.append_qubits(bottom_incrementer, bot_qbs); diff --git a/tket/test/src/test_TwoQubitCanonical.cpp b/tket/test/src/test_TwoQubitCanonical.cpp index d321cddd59..e0f3a6aa0e 100644 --- a/tket/test/src/test_TwoQubitCanonical.cpp +++ b/tket/test/src/test_TwoQubitCanonical.cpp @@ -212,7 +212,7 @@ SCENARIO("Testing two-qubit canonical forms") { 6. + 7. * i_, 7. + 8. * i_, 8. + 9. * i_, 9. + 1. * i_, 1. + 2. * i_, 2. + 3. * i_, 3. + 4. * i_, 4. + 5. * i_, 5. + 6. * i_, 6. + 7. * i_, 7. + 8. * i_; - Eigen::Matrix4cd A = B + B.adjoint(); // hermitian + Eigen::Matrix4cd A = B + B.adjoint(); // hermitian Eigen::Matrix4cd I = Eigen::Matrix4cd::Identity(); Eigen::Matrix4cd U = (I - i_ * A).inverse() * (I + i_ * A); // unitary @@ -272,7 +272,7 @@ SCENARIO("Testing two-qubit canonical forms") { 6. + 7. * i_, 7. + 8. * i_, 8. + 9. * i_, 9. + 1. * i_, 1. + 2. * i_, 2. + 3. * i_, 3. + 4. * i_, 4. + 5. * i_, 5. + 6. * i_, 6. + 7. * i_, 7. + 8. * i_; - Eigen::Matrix4cd A = B + B.adjoint(); // hermitian + Eigen::Matrix4cd A = B + B.adjoint(); // hermitian Eigen::Matrix4cd I = Eigen::Matrix4cd::Identity(); Eigen::Matrix4cd U = (I - i_ * A).inverse() * (I + i_ * A); // unitary Circuit result = two_qubit_canonical(U); @@ -443,7 +443,7 @@ SCENARIO("Testing two qubit decomposition with fidelity tradeoff") { 6. + 7. * i_, 7. + 8. * i_, 8. + 9. * i_, 9. + 1. * i_, 1. + 2. * i_, 2. + 3. * i_, 3. + 4. * i_, 4. + 5. * i_, 5. + 6. * i_, 6. + 7. * i_, 7. + 8. * i_; - Eigen::Matrix4cd A = B + B.adjoint(); // hermitian + Eigen::Matrix4cd A = B + B.adjoint(); // hermitian Eigen::Matrix4cd I = Eigen::Matrix4cd::Identity(); Eigen::Matrix4cd U = (I - i_ * A).inverse() * (I + i_ * A); // unitary auto get_fid = [&U](const Eigen::Matrix4cd &Up) { From b98b9ccfba548fee2a7fa7f8cc183442a2f2b265 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Tue, 1 Aug 2023 16:28:02 +0100 Subject: [PATCH 13/29] update changes for sycamore gate --- pytket/binders/circuit/library.cpp | 13 +- pytket/pytket/passes/auto_rebase.py | 20 +-- tket/include/tket/Circuit/CircPool.hpp | 30 +++- tket/src/Circuit/CircPool.cpp | 89 +++++++--- tket/src/Transformations/Rebase.cpp | 6 +- tket/test/src/test_CompilerPass.cpp | 220 ++++++++++++++++++++----- 6 files changed, 294 insertions(+), 84 deletions(-) diff --git a/pytket/binders/circuit/library.cpp b/pytket/binders/circuit/library.cpp index 6404ba509e..17380a72ce 100644 --- a/pytket/binders/circuit/library.cpp +++ b/pytket/binders/circuit/library.cpp @@ -44,7 +44,8 @@ void init_library(py::module &m) { library_m.def( "_TK2_using_CX_and_swap", &CircPool::TK2_using_CX_and_swap, "Given expressions α, β and γ, return circuit equivalent to " - "TK2(α, β, γ), up to a wire swap, using up to 3 CX and single-qubit " + "TK2(α, β, γ), up to a wire swap that is encoded in the implicit " + " qubit permutation of the Circuit, using up to 3 CX and single-qubit " "gates.\n\n" "The decomposition minimizes the number of CX gates."); library_m.def( @@ -74,6 +75,9 @@ void init_library(py::module &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_ZZPhase", &CircPool::CX_using_ZZPhase, + "Equivalent to CX, using only ZZPhase, 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"); @@ -222,6 +226,10 @@ void init_library(py::module &m) { library_m.def( "_TK2_using_ZZPhase", &CircPool::TK2_using_ZZPhase, "Equivalent to TK2, using 3 ZZPhase gates"); + library_m.def( + "_TK2_using_ZZPhase_and_swap", &CircPool::TK2_using_ZZPhase_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 ZZPhase gates."); library_m.def( "_approx_TK2_using_1xZZPhase", &CircPool::approx_TK2_using_1xZZPhase, "Approximate equivalent to TK2, using 1 ZZPhase gate and single-qubit " @@ -235,7 +243,8 @@ void init_library(py::module &m) { "Equivalent to TK2, using up to 3 ZZMax gates."); library_m.def( "_TK2_using_ZZMax_and_swap", &CircPool::TK2_using_ZZMax_and_swap, - "Equivalent to TK2, up to a wire swap, using up to 3 ZZMax gates."); + "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( "_XXPhase3_using_TK2", &CircPool::XXPhase3_using_TK2, "Equivalent to XXPhase3, using three TK2 gates"); diff --git a/pytket/pytket/passes/auto_rebase.py b/pytket/pytket/passes/auto_rebase.py index 1eb2693ce4..f5fe389481 100644 --- a/pytket/pytket/passes/auto_rebase.py +++ b/pytket/pytket/passes/auto_rebase.py @@ -36,6 +36,12 @@ class NoAutoRebase(Exception): def _TK2_using_TK2(a: Param, b: Param, c: Param) -> Circuit: return Circuit(2).TK2(a, b, c, 0, 1) +def _TK2_using_TK2_or_swap(a: Param, b: Param, c: Param) -> Circuit: + if a == b == c == 0.5: + c = Circuit(2).SWAP(0,1) + c.replace_SWAPs() + return c + return Circuit(2).TK2(a, b, c, 0, 1) _TK2_CIRCS: Dict[OpType, Callable[[Param, Param, Param], "Circuit"]] = { OpType.TK2: _TK2_using_TK2, @@ -45,7 +51,7 @@ def _TK2_using_TK2(a: Param, b: Param, c: Param) -> Circuit: } _TK2_CIRCS_WIRE_SWAP: Dict[OpType, Callable[[Param, Param, Param], "Circuit"]] = { - OpType.TK2: _TK2_using_TK2, + OpType.TK2: _TK2_using_TK2_or_swap, OpType.ZZPhase: _library._TK2_using_ZZPhase, OpType.CX: _library._TK2_using_CX_and_swap, OpType.ZZMax: _library._TK2_using_ZZMax_and_swap, @@ -140,16 +146,8 @@ def auto_rebase_pass(gateset: Set[OpType], allow_swaps: bool = False) -> RebaseC :rtype: RebaseCustom """ tk1 = get_TK1_decomposition_function(gateset) - - if allow_swaps: - try: - return RebaseCustom( - gateset, get_tk2_decomposition(gateset, allow_swaps), tk1 - ) - except NoAutoRebase: - pass - # if the gateset has CX but not TK2, rebase via CX - if OpType.CX in gateset and OpType.TK2 not in gateset: + # if the gateset has CX but not TK2, and implicit wire swaps not allowed, rebase via CX + if OpType.CX in gateset and OpType.TK2 not in gateset and not allow_swaps: return RebaseCustom(gateset, _library._CX(), tk1) # in other cases, try to rebase via TK2 first try: diff --git a/tket/include/tket/Circuit/CircPool.hpp b/tket/include/tket/Circuit/CircPool.hpp index 5eff734ece..e80a8c8b04 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 ZZPhase, Rx and Rz gates */ +const Circuit &CX_using_ZZPhase(); + /** Equivalent to CX, using only XXPhase, Rx, Ry and Rz gates */ const Circuit &CX_using_XXPhase_0(); @@ -317,7 +320,8 @@ Circuit normalised_TK2_using_CX( Circuit TK2_using_CX(const Expr &alpha, const Expr &beta, const Expr &gamma); /** - * @brief Equivalent to TK2(α, β, γ) up to a wire swap, with minimal number of + * @brief Equivalent to TK2(α, β, γ) up to a wire swap that is encoded in + * the implicit qubit permutation of the Circuit with minimal number of * CX gates. * * A TK2-equivalent circuit with as few CX gates as possible (0, 1, 2 or 3 CX). @@ -347,10 +351,10 @@ Circuit approx_TK2_using_1xZZPhase(const Expr &alpha); * This is the optimal 2-ZZPhase approximation for any TK2(α, β, γ), with * respect to the squared trace fidelity metric. * - * Warning: in practice, we would not expect this decomposition to be attractive - * on real hardware, as the same approximation fidelity can be obtained using - * 2 ZZMax gates, which would typically have (same or) higher fidelity than - * variable angle ZZPhase gates. + * Warning: in practice, we would not expect this decomposition to be + * attractive on real hardware, as the same approximation fidelity can be + * obtained using 2 ZZMax gates, which would typically have (same or) higher + * fidelity than variable angle ZZPhase gates. * * @return Circuit Equivalent circuit to TK2(α, β, 0). */ @@ -370,6 +374,18 @@ Circuit approx_TK2_using_2xZZPhase(const Expr &alpha, const Expr &beta); */ Circuit TK2_using_ZZPhase( const Expr &alpha, const Expr &beta, const Expr &gamma); +/** + * @brief Equivalent to TK2(α, β, γ) up to a wire swap that is encoded in + * the implicit qubit permutation of the Circuit with minimal number of + * ZZPhase gates. + * + * A TK2-equivalent circuit with as few ZZPhase gates as possible: + * (0, 1, 2 or 3 ZZphase). + * + * @return Circuit Equivalent circuit, up to a wire swap, to TK2(α, β, γ). + */ +Circuit TK2_using_ZZPhase_and_swap( + const Expr &alpha, const Expr &beta, const Expr &gamma); /** * @brief Equivalent to TK2(α, β, γ), using up to 3 ZZMax gates. @@ -379,8 +395,8 @@ Circuit TK2_using_ZZPhase( Circuit TK2_using_ZZMax(const Expr &alpha, const Expr &beta, const Expr &gamma); /** - * @brief Equivalent to TK2(α, β, γ), up to a wire swap, using up to 3 ZZMax - * gates. + * @brief 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. * * @return Circuit equivalent to TK2(α, β, γ) up to a wire swap. */ diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index 8a3775fbce..9915f1a346 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -107,6 +107,22 @@ const Circuit &CX_using_ZZMax() { return *C; } +const Circuit &CX_using_ZZPhase() { + static std::unique_ptr C = std::make_unique([]() { + Circuit c(2); + c.add_op(OpType::Rz, 1.5, {0}); + c.add_op(OpType::Rx, 0.5, {1}); + c.add_op(OpType::Rz, 1.5, {1}); + c.add_op(OpType::Rx, 1.5, {1}); + c.add_op(OpType::ZZPhase, 0.5, {0, 1}); + c.add_op(OpType::Rx, 1.5, {1}); + c.add_op(OpType::Rz, 1.5, {1}); + c.add_phase(0.75); + return c; + }()); + return *C; +} + const Circuit &CX_using_XXPhase_0() { static std::unique_ptr C = std::make_unique([]() { Circuit c(2); @@ -955,8 +971,6 @@ static Circuit TK2_swap_replacement(std::array angles) { // Generate to support allowing swap gates Circuit pre, post; std::array angles_swapped; - // Swapped circuit - Circuit swap_circ(2); angles_swapped = angles; for (unsigned i = 0; i < 3; ++i) { angles_swapped[i] += 0.5; @@ -972,25 +986,35 @@ static Circuit TK2_swap_replacement(std::array angles) { unsigned last_angle = 0; for (; last_angle < 3; ++last_angle) { std::optional eval = eval_expr_mod(angles[last_angle]); - if (eval) { + if (eval && *eval > 0) { angles_eval[last_angle] = *eval; } else { break; } - eval = eval_expr_mod(angles_swapped[last_angle]); - TKET_ASSERT(eval); - angles_eval_swapped[last_angle] = *eval; + } + + unsigned last_angle_swapped = 0; + for (; last_angle_swapped < 3; ++last_angle_swapped) { + std::optional eval = + eval_expr_mod(angles_swapped[last_angle_swapped]); + if (eval && *eval > 0) { + angles_eval_swapped[last_angle_swapped] = *eval; + } else { + break; + } } // Check if fewer gates can be used. if (last_angle <= 2) { if (equiv_0(angles[2], 4) && equiv_0(angles[1], 4)) { n_gates = 1; - } else if (equiv_0(angles_swapped[2], 4) && equiv_0(angles_swapped[1], 4)) { - implicit_swap = true; - n_gates = 1; } else if (equiv_0(angles[2], 4)) { n_gates = 2; + } + } else if (last_angle_swapped <= 2) { + if (equiv_0(angles_swapped[2], 4) && equiv_0(angles_swapped[1], 4)) { + implicit_swap = true; + n_gates = 1; } else if (equiv_0(angles_swapped[2], 4)) { implicit_swap = true; n_gates = 2; @@ -1043,6 +1067,7 @@ static Circuit TK2_swap_replacement(std::array angles) { implicit_swap = false; } } + // Build circuit for substitution. Circuit sub(2); switch (n_gates) { @@ -1114,6 +1139,26 @@ Circuit TK2_using_ZZPhase( return c; } +Circuit TK2_using_ZZPhase_and_swap( + const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c = TK2_using_CX_and_swap(alpha, beta, gamma); + if (c.count_gates(OpType::CX) < 3) { + // Find the CX gates and replace them with ZZMax. + 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_ZZPhase(), v, Circuit::VertexDeletion::No); + bin.insert(v); + } + } + c.remove_vertices( + bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes); + return c; + } + return TK2_using_ZZPhase(alpha, beta, gamma); +} + Circuit TK2_using_ZZMax( const Expr &alpha, const Expr &beta, const Expr &gamma) { Circuit c = TK2_using_CX(alpha, beta, gamma); @@ -1134,18 +1179,23 @@ Circuit TK2_using_ZZMax( Circuit TK2_using_ZZMax_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 ZZMax. - 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_ZZMax(), v, Circuit::VertexDeletion::No); - bin.insert(v); + + if (c.count_gates(OpType::CX) < 3) { + // Find the CX gates and replace them with ZZMax. + 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_ZZMax(), v, Circuit::VertexDeletion::No); + bin.insert(v); + } } + c.remove_vertices( + bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes); + return c; } - c.remove_vertices( - bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes); - return c; + + return TK2_using_ZZMax(alpha, beta, gamma); } Circuit XXPhase3_using_TK2(const Expr &alpha) { @@ -1258,7 +1308,6 @@ Circuit TK2_using_normalised_TK2( const Expr &alpha, const Expr &beta, const Expr &gamma) { auto [pre, normalised_exprs, post] = normalise_TK2_angles(alpha, beta, gamma); auto [alpha_norm, beta_norm, gamma_norm] = normalised_exprs; - Circuit res(2); res.append(pre); res.add_op( diff --git a/tket/src/Transformations/Rebase.cpp b/tket/src/Transformations/Rebase.cpp index a4e302bcc9..14a060b2da 100644 --- a/tket/src/Transformations/Rebase.cpp +++ b/tket/src/Transformations/Rebase.cpp @@ -122,7 +122,7 @@ static bool standard_rebase_via_tk2( tk2_replacement) { bool success = false; VertexSet bin; - bool TK2_allowed = allowed_gates.contains(OpType::TK2); + const bool TK2_allowed = allowed_gates.contains(OpType::TK2); // 1. Replace all multi-qubit gates outside the target gateset to TK2. for (const Vertex& v : circ.all_vertices()) { Op_ptr op = circ.get_Op_ptr_from_Vertex(v); @@ -139,7 +139,7 @@ static bool standard_rebase_via_tk2( continue; // need to convert Circuit replacement = TK2_circ_from_multiq(op); - // 2. If TK2 gates are not allowed in the target gateset we find a + // If TK2 gates are not allowed in the target gateset we find a // replacement circuit by decomposing them if (!TK2_allowed) { VertexSet TK2_bin; @@ -169,7 +169,7 @@ static bool standard_rebase_via_tk2( success = true; } - // 3. Replace 0- and 1-qubit gates by converting to TK1 and replacing. + // 2. Replace 0- and 1-qubit gates by converting to TK1 and replacing. for (const Vertex& v : circ.all_vertices()) { if (bin.contains(v)) continue; if (circ.n_in_edges_of_type(v, EdgeType::Quantum) > 1) continue; diff --git a/tket/test/src/test_CompilerPass.cpp b/tket/test/src/test_CompilerPass.cpp index 73c334997b..ff540463ed 100644 --- a/tket/test/src/test_CompilerPass.cpp +++ b/tket/test/src/test_CompilerPass.cpp @@ -162,7 +162,8 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { PassPtr cp_route = gen_default_mapping_pass(grid, false); Circuit circ(5); - add_2qb_gates(circ, OpType::CX, {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {3, 4}}); + add_2qb_gates(circ, OpType::CX, {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {3, + 4}}); PredicatePtr routed_correctly = std::make_shared(grid); @@ -183,7 +184,8 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { PassPtr cp_route = gen_default_mapping_pass(grid, false); Circuit circ(6); - add_2qb_gates(circ, OpType::CX, {{0, 1}, {0, 5}, {0, 3}, {1, 2}, {3, 4}}); + add_2qb_gates(circ, OpType::CX, {{0, 1}, {0, 5}, {0, 3}, {1, 2}, {3, + 4}}); SquareGrid grid2(1, 6); PredicatePtr routed_correctly = @@ -197,7 +199,8 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { } WHEN("Ran in safe mode") { REQUIRE( - cp_route->apply(cu, SafetyMode::Audit)); // warning should be logged + cp_route->apply(cu, SafetyMode::Audit)); // warning should be + logged REQUIRE(!cu.check_all_predicates()); } } @@ -396,7 +399,8 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { GIVEN("Full compilation sequence") { SquareGrid grid(1, 5); std::vector passes = { - DecomposeBoxes(), RebaseTket(), gen_default_mapping_pass(grid, true)}; + DecomposeBoxes(), RebaseTket(), gen_default_mapping_pass(grid, + true)}; REQUIRE_NOTHROW(SequencePass(passes)); } GIVEN("TK1 and TK2 replacement functions") { @@ -457,7 +461,8 @@ SCENARIO("Construct invalid sequence passes from vector") { PassPtr compass = std::make_shared( ppm, Transforms::id, pc, nlohmann::json{}); passes.push_back(compass); - REQUIRE_THROWS_AS((void)SequencePass(passes), IncompatibleCompilerPasses); + REQUIRE_THROWS_AS((void)SequencePass(passes), + IncompatibleCompilerPasses); } } @@ -466,7 +471,8 @@ SCENARIO("Construct invalid sequence of loops") { PredicatePtrMap ppm{CompilationUnit::make_type_pair(pp1)}; PostConditions pc{{}, {}, Guarantee::Preserve}; PassPtr pass1 = - std::make_shared(ppm, Transforms::id, pc, nlohmann::json{}); + std::make_shared(ppm, Transforms::id, pc, + nlohmann::json{}); PassPtr loop1 = std::make_shared(pass1); PostConditions pc2{{}, {}, Guarantee::Clear}; PredicatePtrMap empty_ppm{}; @@ -476,7 +482,8 @@ SCENARIO("Construct invalid sequence of loops") { std::vector good_passes{loop1, loop2}; std::vector bad_passes{loop2, loop1}; REQUIRE_NOTHROW((void)SequencePass(good_passes)); - REQUIRE_THROWS_AS((void)SequencePass(bad_passes), IncompatibleCompilerPasses); + REQUIRE_THROWS_AS((void)SequencePass(bad_passes), + IncompatibleCompilerPasses); } SCENARIO("Test RepeatWithMetricPass") { @@ -679,9 +686,11 @@ SCENARIO("gen_placement_pass test") { REQUIRE(graph_cu.get_final_map_ref() != line_cu.get_final_map_ref()); REQUIRE(noise_cu.get_final_map_ref() != line_cu.get_final_map_ref()); REQUIRE( - graph_fall_back_cu.get_final_map_ref() == line_cu.get_final_map_ref()); + graph_fall_back_cu.get_final_map_ref() == + line_cu.get_final_map_ref()); REQUIRE( - noise_fall_back_cu.get_final_map_ref() == line_cu.get_final_map_ref()); + noise_fall_back_cu.get_final_map_ref() == + line_cu.get_final_map_ref()); } } @@ -836,12 +845,14 @@ SCENARIO("FullPeepholeOptimise with various options") { Circuit compiled_circ_noswaps_tk2 = cu_noswaps_tk2.get_circ_ref(); unsigned n_gates_swaps_cx = compiled_circ_swaps_cx.n_gates(); unsigned n_cx_swaps_cx = compiled_circ_swaps_cx.count_gates(OpType::CX); - unsigned n_tk1_swaps_cx = compiled_circ_swaps_cx.count_gates(OpType::TK1); - unsigned n_gates_swaps_tk2 = compiled_circ_swaps_tk2.n_gates(); - unsigned n_tk2_swaps_tk2 = compiled_circ_swaps_tk2.count_gates(OpType::TK2); - unsigned n_tk1_swaps_tk2 = compiled_circ_swaps_tk2.count_gates(OpType::TK1); - unsigned n_gates_noswaps_cx = compiled_circ_noswaps_cx.n_gates(); - unsigned n_cx_noswaps_cx = compiled_circ_noswaps_cx.count_gates(OpType::CX); + unsigned n_tk1_swaps_cx = + compiled_circ_swaps_cx.count_gates(OpType::TK1); unsigned + n_gates_swaps_tk2 = compiled_circ_swaps_tk2.n_gates(); unsigned + n_tk2_swaps_tk2 = compiled_circ_swaps_tk2.count_gates(OpType::TK2); + unsigned n_tk1_swaps_tk2 = + compiled_circ_swaps_tk2.count_gates(OpType::TK1); unsigned + n_gates_noswaps_cx = compiled_circ_noswaps_cx.n_gates(); unsigned + n_cx_noswaps_cx = compiled_circ_noswaps_cx.count_gates(OpType::CX); unsigned n_tk1_noswaps_cx = compiled_circ_noswaps_cx.count_gates(OpType::TK1); unsigned n_gates_noswaps_tk2 = compiled_circ_noswaps_tk2.n_gates(); @@ -928,7 +939,8 @@ SCENARIO("rebase and decompose PhasePolyBox test") { Circuit c(1, 1); c.add_conditional_gate(OpType::H, {}, {0}, {0}, 1); CompilationUnit cu(c); - REQUIRE_THROWS_AS(ComposePhasePolyBoxes()->apply(cu), UnsatisfiedPredicate); + REQUIRE_THROWS_AS(ComposePhasePolyBoxes()->apply(cu), + UnsatisfiedPredicate); } GIVEN("NoWireSwapsPredicate for ComposePhasePolyBoxes") { Circuit circ(5); @@ -1275,7 +1287,8 @@ SCENARIO("Commute measurements to the end of a circuit") { CompilationUnit cu(c); REQUIRE_THROWS_AS(delay_pass->apply(cu), UnsatisfiedPredicate); } - GIVEN("Measure blocked by classical operation, using a partial delay pass") { + GIVEN("Measure blocked by classical operation, using a partial delay pass") + { Circuit c(2, 1); add_2qb_gates(c, OpType::Measure, {{0, 0}, {1, 0}}); c.add_op(OpType::Measure, {0, 0}); @@ -1307,13 +1320,15 @@ SCENARIO("Commute measurements to the end of a circuit") { c.add_op(OpType::CZ, {0, 1}); c.add_op(OpType::Measure, {0, 0}); c.add_conditional_gate(OpType::Z, {}, {1}, {0}, 1); - REQUIRE_THROWS_AS(Transforms::delay_measures().apply(c), CircuitInvalidity); + REQUIRE_THROWS_AS(Transforms::delay_measures().apply(c), + CircuitInvalidity); } GIVEN( - "Call on invalid nested circuit without checking the predicate throws") { + "Call on invalid nested circuit without checking the predicate throws") + { Circuit inner1(1, 2); - inner1.add_conditional_gate(OpType::Measure, {}, {0, 0}, {1}, 1); - CircBox cbox1(inner1); + inner1.add_conditional_gate(OpType::Measure, {}, {0, 0}, {1}, + 1); CircBox cbox1(inner1); Circuit inner2(1, 2); inner2.add_box(cbox1, {0, 0, 1}); @@ -1322,7 +1337,8 @@ SCENARIO("Commute measurements to the end of a circuit") { Circuit c(1, 2); c.add_box(cbox2, {0, 0, 1}); c.add_op(OpType::X, {0}); - REQUIRE_THROWS_AS(Transforms::delay_measures().apply(c), CircuitInvalidity); + REQUIRE_THROWS_AS(Transforms::delay_measures().apply(c), + CircuitInvalidity); } GIVEN("Combined with routing") { Circuit test(3, 1); @@ -1518,8 +1534,8 @@ SCENARIO("CX mapping pass") { } GIVEN("A circuit with measurements inside boxes.") { Circuit inner1(1, 2); - inner1.add_conditional_gate(OpType::Measure, {}, {0, 0}, {1}, 1); - CircBox cbox1(inner1); + inner1.add_conditional_gate(OpType::Measure, {}, {0, 0}, {1}, + 1); CircBox cbox1(inner1); Circuit inner2(1, 2); inner2.add_box(cbox1, {0, 0, 1}); @@ -1731,27 +1747,46 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { PassPtr pp_rebase_zzmax = gen_rebase_pass_via_tk2( allowed_gates_zzmax, CircPool::TK2_using_ZZMax_and_swap, CircPool::tk1_to_PhasedXRz); - + OpTypeSet allowed_gates_zzphase = { + OpType::PhasedX, OpType::Rz, OpType::ZZPhase}; + PassPtr pp_rebase_zzphase = gen_rebase_pass_via_tk2( + allowed_gates_zzphase, CircPool::TK2_using_ZZPhase_and_swap, + CircPool::tk1_to_PhasedXRz); GIVEN("Targeting CX gates, ISWAPMax gate.") { Circuit c(2); c.add_op(OpType::ISWAPMax, {0, 1}); CompilationUnit cu(c); CHECK(pp_rebase_cx->apply(cu)); REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 1); - } - GIVEN("Targeting CX gates, Sycamore gate.") { - Circuit c(2); - c.add_op(OpType::Sycamore, {0, 1}); - CompilationUnit cu(c); - CHECK(pp_rebase_cx->apply(cu)); - REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 2); - } + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); + } + // GIVEN("Targeting CX gates, Sycamore gate.") { + // Circuit c(2); + // c.add_op(OpType::Sycamore, {0, 1}); + // CompilationUnit cu(c); + // CHECK(pp_rebase_cx->apply(cu)); + // REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 1); + // auto u1 = tket_sim::get_unitary(c); + // auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + // CompilationUnit cu0(c); + // gen_rebase_pass_via_tk2( + // allowed_gates_cx, CircPool::TK2_using_CX, + // CircPool::tk1_to_PhasedXRz)->apply(cu0); + // auto u3 = tket_sim::get_unitary(cu.get_circ_ref()); + // REQUIRE(u1.isApprox(u3)); + // REQUIRE(u1.isApprox(u2)); + // } GIVEN("Targeting CX gates, ISWAP gate.") { Circuit c(2); c.add_op(OpType::ISWAP, 0.3, {0, 1}); CompilationUnit cu(c); CHECK(pp_rebase_cx->apply(cu)); REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 2); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); } GIVEN("Targeting CX gates, SWAP gate.") { Circuit c(2); @@ -1759,6 +1794,9 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { CompilationUnit cu(c); CHECK(pp_rebase_cx->apply(cu)); REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 0); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); } GIVEN("Targeting CX gates, CX gate.") { Circuit c(2); @@ -1766,6 +1804,9 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { CompilationUnit cu(c); CHECK(!pp_rebase_cx->apply(cu)); REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 1); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); } GIVEN("Targeting CX gates, ZZMAX gate.") { Circuit c(2); @@ -1773,6 +1814,9 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { CompilationUnit cu(c); CHECK(pp_rebase_cx->apply(cu)); REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 1); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); } GIVEN("Targeting CX gates, ZZPhasegate.") { Circuit c(2); @@ -1780,6 +1824,9 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { CompilationUnit cu(c); CHECK(pp_rebase_cx->apply(cu)); REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 2); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); } GIVEN("Targeting ZZMax gates, ISWAPMax gate.") { Circuit c(2); @@ -1787,6 +1834,9 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { CompilationUnit cu(c); CHECK(pp_rebase_zzmax->apply(cu)); REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 1); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); } GIVEN("Targeting ZZMax gates, ISWAP gate.") { Circuit c(2); @@ -1794,20 +1844,29 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { CompilationUnit cu(c); CHECK(pp_rebase_zzmax->apply(cu)); REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 2); - } - GIVEN("Targeting ZZMax gates, Sycamore gate.") { - Circuit c(2); - c.add_op(OpType::Sycamore, {0, 1}); - CompilationUnit cu(c); - CHECK(pp_rebase_zzmax->apply(cu)); - REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 2); - } + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); + } + // GIVEN("Targeting ZZMax gates, Sycamore gate.") { + // Circuit c(2); + // c.add_op(OpType::Sycamore, {0, 1}); + // CompilationUnit cu(c); + // CHECK(pp_rebase_zzmax->apply(cu)); + // REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 1); + // auto u1 = tket_sim::get_unitary(c); + // auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + // REQUIRE(u1.isApprox(u2)); + // } GIVEN("Targeting ZZMax gates, SWAP gate.") { Circuit c(2); c.add_op(OpType::SWAP, {0, 1}); CompilationUnit cu(c); CHECK(pp_rebase_zzmax->apply(cu)); REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 0); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); } GIVEN("Targeting ZZMax gates, CX gate.") { Circuit c(2); @@ -1815,6 +1874,9 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { CompilationUnit cu(c); CHECK(pp_rebase_zzmax->apply(cu)); REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 1); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); } GIVEN("Targeting ZZMax gates, ZZMAX gate.") { Circuit c(2); @@ -1822,6 +1884,9 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { CompilationUnit cu(c); CHECK(!pp_rebase_zzmax->apply(cu)); REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 1); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); } GIVEN("Targeting ZZMax gates, ZZPhasegate.") { Circuit c(2); @@ -1829,6 +1894,79 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { CompilationUnit cu(c); CHECK(pp_rebase_zzmax->apply(cu)); REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 2); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); + } + GIVEN("Targeting ZZPhase gates, ISWAPMax gate.") { + Circuit c(2); + c.add_op(OpType::ISWAPMax, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_zzphase->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZPhase) == 1); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); + } + GIVEN("Targeting ZZPhase gates, ISWAP gate.") { + Circuit c(2); + c.add_op(OpType::ISWAP, 0.3, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_zzphase->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZPhase) == 2); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); + } + // GIVEN("Targeting ZZPhase gates, Sycamore gate.") { + // Circuit c(2); + // c.add_op(OpType::Sycamore, {0, 1}); + // CompilationUnit cu(c); + // CHECK(pp_rebase_zzphase->apply(cu)); + // REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZPhase) == 1); + // auto u1 = tket_sim::get_unitary(c); + // auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + // REQUIRE(u1.isApprox(u2)); + // } + GIVEN("Targeting ZZPhase gates, SWAP gate.") { + Circuit c(2); + c.add_op(OpType::SWAP, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_zzphase->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZPhase) == 0); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); + } + GIVEN("Targeting ZZPhase gates, CX gate.") { + Circuit c(2); + c.add_op(OpType::CX, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_zzphase->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZPhase) == 1); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); + } + GIVEN("Targeting ZZPhase gates, ZZMax gate.") { + Circuit c(2); + c.add_op(OpType::ZZMax, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_zzphase->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZPhase) == 1); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); + } + GIVEN("Targeting ZZPhase gates, ZZPhasegate.") { + Circuit c(2); + c.add_op(OpType::ZZPhase, 0.3, {0, 1}); + CompilationUnit cu(c); + CHECK(!pp_rebase_zzphase->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZPhase) == 1); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); } } } // namespace test_CompilerPass From a315e95d901dfae19d5596f8761cd70b1de02d7e Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Tue, 1 Aug 2023 16:44:05 +0100 Subject: [PATCH 14/29] Add tests for new zzphase and tk2 sswap cases --- pytket/pytket/passes/auto_rebase.py | 4 +- pytket/tests/transform_test.py | 91 ++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 3 deletions(-) diff --git a/pytket/pytket/passes/auto_rebase.py b/pytket/pytket/passes/auto_rebase.py index f5fe389481..2caea94e74 100644 --- a/pytket/pytket/passes/auto_rebase.py +++ b/pytket/pytket/passes/auto_rebase.py @@ -36,13 +36,15 @@ class NoAutoRebase(Exception): def _TK2_using_TK2(a: Param, b: Param, c: Param) -> Circuit: return Circuit(2).TK2(a, b, c, 0, 1) + def _TK2_using_TK2_or_swap(a: Param, b: Param, c: Param) -> Circuit: if a == b == c == 0.5: - c = Circuit(2).SWAP(0,1) + c = Circuit(2).SWAP(0, 1) c.replace_SWAPs() return c return Circuit(2).TK2(a, b, c, 0, 1) + _TK2_CIRCS: Dict[OpType, Callable[[Param, Param, Param], "Circuit"]] = { OpType.TK2: _TK2_using_TK2, OpType.ZZPhase: _library._TK2_using_ZZPhase, diff --git a/pytket/tests/transform_test.py b/pytket/tests/transform_test.py index a23a696a68..decc7953ea 100644 --- a/pytket/tests/transform_test.py +++ b/pytket/tests/transform_test.py @@ -1232,7 +1232,7 @@ def test_auto_rebase_with_swap_cx() -> None: c_swap = Circuit(2).Sycamore(0, 1) swap_pass.apply(c_swap) - assert c_swap.n_gates_of_type(OpType.CX) == 2 + assert c_swap.n_gates_of_type(OpType.CX) == 1 iqp = c_swap.implicit_qubit_permutation() assert iqp[Qubit(0)] == Qubit(1) assert iqp[Qubit(1)] == Qubit(0) @@ -1297,7 +1297,7 @@ def test_auto_rebase_with_swap_zzmax() -> None: c_swap = Circuit(2).Sycamore(0, 1) swap_pass.apply(c_swap) - assert c_swap.n_gates_of_type(OpType.ZZMax) == 2 + assert c_swap.n_gates_of_type(OpType.ZZMax) == 1 assert c_swap.n_gates == 12 iqp = c_swap.implicit_qubit_permutation() assert iqp[Qubit(0)] == Qubit(1) @@ -1349,6 +1349,91 @@ def test_auto_rebase_with_swap_zzmax() -> None: assert c_swap.n_gates == 1 +def test_auto_rebase_with_swap_zzphase() -> None: + swap_pass = auto_rebase_pass({OpType.ZZPhase, OpType.PhasedX, OpType.Rz}, True) + no_swap_pass = auto_rebase_pass({OpType.ZZPhase, OpType.PhasedX, OpType.Rz}, False) + + c_swap = Circuit(2).ISWAPMax(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.ZZPhase) == 1 + assert c_swap.n_gates == 4 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(1) + assert iqp[Qubit(1)] == Qubit(0) + c_no_swap = Circuit(2).ISWAPMax(0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 2 + assert c_no_swap.n_gates == 13 + + c_swap = Circuit(2).Sycamore(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.ZZPhase) == 1 + assert c_swap.n_gates == 12 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(1) + assert iqp[Qubit(1)] == Qubit(0) + + c_no_swap = Circuit(2).Sycamore(0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 3 + assert c_no_swap.n_gates == 16 + + c_swap = Circuit(2).ISWAP(0.3, 0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.ZZPhase) == 2 + assert c_swap.n_gates == 13 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(0) + assert iqp[Qubit(1)] == Qubit(1) + c_no_swap = Circuit(2).ISWAP(0.3, 0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 2 + assert c_no_swap.n_gates == 13 + + c_swap = Circuit(2).ISWAPMax(0, 1).ISWAPMax(1, 0) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.ZZPhase) == 2 + assert c_swap.n_gates == 8 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(0) + assert iqp[Qubit(1)] == Qubit(1) + c_no_swap = Circuit(2).ISWAPMax(0, 1).ISWAPMax(1, 0) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 4 + assert c_no_swap.n_gates == 26 + + c_swap = Circuit(2).SWAP(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.ZZPhase) == 0 + assert c_swap.n_gates == 0 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(1) + assert iqp[Qubit(1)] == Qubit(0) + c_no_swap = Circuit(2).SWAP(0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 3 + assert c_no_swap.n_gates == 16 + + c_swap = Circuit(2).ZZPhase(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates == 1 + + c_swap = Circuit(2).ZZMax(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.ZZPhase) == 1 + + +def test_auto_rebase_with_swap_tk2() -> None: + swap_pass = auto_rebase_pass({OpType.TK2, OpType.PhasedX, OpType.Rz}, True) + no_swap_pass = auto_rebase_pass({OpType.TK2, OpType.PhasedX, OpType.Rz}, False) + c_swap = Circuit(2).SWAP(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates == 0 + c_no_swap = Circuit(2).SWAP(0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates > 0 + + if __name__ == "__main__": test_remove_redundancies() test_reduce_singles() @@ -1377,3 +1462,5 @@ def test_auto_rebase_with_swap_zzmax() -> None: test_KAK_with_ClassicalExpBox() test_auto_rebase_with_swap_cx() test_auto_rebase_with_swap_zzmax() + test_auto_rebase_with_swap_zzphase() + test_auto_rebase_with_swap_tk2() From da721c91a3e088e196dfaf9b58916bab6ca82ba9 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Tue, 1 Aug 2023 16:50:01 +0100 Subject: [PATCH 15/29] Update auto_rebase.py --- pytket/pytket/passes/auto_rebase.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytket/pytket/passes/auto_rebase.py b/pytket/pytket/passes/auto_rebase.py index 2caea94e74..d22b9b23bc 100644 --- a/pytket/pytket/passes/auto_rebase.py +++ b/pytket/pytket/passes/auto_rebase.py @@ -148,7 +148,8 @@ def auto_rebase_pass(gateset: Set[OpType], allow_swaps: bool = False) -> RebaseC :rtype: RebaseCustom """ tk1 = get_TK1_decomposition_function(gateset) - # if the gateset has CX but not TK2, and implicit wire swaps not allowed, rebase via CX + # if the gateset has CX but not TK2, and implicit wire swaps not allowed: + # rebase via CX if OpType.CX in gateset and OpType.TK2 not in gateset and not allow_swaps: return RebaseCustom(gateset, _library._CX(), tk1) # in other cases, try to rebase via TK2 first From 28df6fb7bdb00d8766997bc5ac2b6051049c7fe5 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Tue, 1 Aug 2023 16:50:33 +0100 Subject: [PATCH 16/29] Update changelog.rst --- pytket/docs/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 0beccadd3c..43bb7afa14 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -7,6 +7,7 @@ Unreleased Minor new features: * Add circuit method ``depth_2q``. +* Add ``allow_swaps`` parameter to ``auto_rebase_pass``. Fixes: From b9b626da758dafa88cf4637092b51f110602fef8 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Tue, 1 Aug 2023 16:53:09 +0100 Subject: [PATCH 17/29] Update test_CompilerPass.cpp --- tket/test/src/test_CompilerPass.cpp | 67 +++++++++++------------------ 1 file changed, 25 insertions(+), 42 deletions(-) diff --git a/tket/test/src/test_CompilerPass.cpp b/tket/test/src/test_CompilerPass.cpp index ff540463ed..e41e79afdb 100644 --- a/tket/test/src/test_CompilerPass.cpp +++ b/tket/test/src/test_CompilerPass.cpp @@ -162,8 +162,7 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { PassPtr cp_route = gen_default_mapping_pass(grid, false); Circuit circ(5); - add_2qb_gates(circ, OpType::CX, {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {3, - 4}}); + add_2qb_gates(circ, OpType::CX, {{0, 1}, {0, 2}, {0, 3}, {1, 2}, {3, 4}}); PredicatePtr routed_correctly = std::make_shared(grid); @@ -184,8 +183,7 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { PassPtr cp_route = gen_default_mapping_pass(grid, false); Circuit circ(6); - add_2qb_gates(circ, OpType::CX, {{0, 1}, {0, 5}, {0, 3}, {1, 2}, {3, - 4}}); + add_2qb_gates(circ, OpType::CX, {{0, 1}, {0, 5}, {0, 3}, {1, 2}, {3, 4}}); SquareGrid grid2(1, 6); PredicatePtr routed_correctly = @@ -198,10 +196,8 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { REQUIRE(!cu.check_all_predicates()); } WHEN("Ran in safe mode") { - REQUIRE( - cp_route->apply(cu, SafetyMode::Audit)); // warning should be - logged - REQUIRE(!cu.check_all_predicates()); + REQUIRE(cp_route->apply(cu, SafetyMode::Audit)); // warning should be + logged REQUIRE(!cu.check_all_predicates()); } } GIVEN("Correct sequence of Synthesis, Routing and Rebasing") { @@ -399,8 +395,7 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { GIVEN("Full compilation sequence") { SquareGrid grid(1, 5); std::vector passes = { - DecomposeBoxes(), RebaseTket(), gen_default_mapping_pass(grid, - true)}; + DecomposeBoxes(), RebaseTket(), gen_default_mapping_pass(grid, true)}; REQUIRE_NOTHROW(SequencePass(passes)); } GIVEN("TK1 and TK2 replacement functions") { @@ -461,8 +456,7 @@ SCENARIO("Construct invalid sequence passes from vector") { PassPtr compass = std::make_shared( ppm, Transforms::id, pc, nlohmann::json{}); passes.push_back(compass); - REQUIRE_THROWS_AS((void)SequencePass(passes), - IncompatibleCompilerPasses); + REQUIRE_THROWS_AS((void)SequencePass(passes), IncompatibleCompilerPasses); } } @@ -471,8 +465,7 @@ SCENARIO("Construct invalid sequence of loops") { PredicatePtrMap ppm{CompilationUnit::make_type_pair(pp1)}; PostConditions pc{{}, {}, Guarantee::Preserve}; PassPtr pass1 = - std::make_shared(ppm, Transforms::id, pc, - nlohmann::json{}); + std::make_shared(ppm, Transforms::id, pc, nlohmann::json{}); PassPtr loop1 = std::make_shared(pass1); PostConditions pc2{{}, {}, Guarantee::Clear}; PredicatePtrMap empty_ppm{}; @@ -482,8 +475,7 @@ SCENARIO("Construct invalid sequence of loops") { std::vector good_passes{loop1, loop2}; std::vector bad_passes{loop2, loop1}; REQUIRE_NOTHROW((void)SequencePass(good_passes)); - REQUIRE_THROWS_AS((void)SequencePass(bad_passes), - IncompatibleCompilerPasses); + REQUIRE_THROWS_AS((void)SequencePass(bad_passes), IncompatibleCompilerPasses); } SCENARIO("Test RepeatWithMetricPass") { @@ -686,11 +678,9 @@ SCENARIO("gen_placement_pass test") { REQUIRE(graph_cu.get_final_map_ref() != line_cu.get_final_map_ref()); REQUIRE(noise_cu.get_final_map_ref() != line_cu.get_final_map_ref()); REQUIRE( - graph_fall_back_cu.get_final_map_ref() == - line_cu.get_final_map_ref()); + graph_fall_back_cu.get_final_map_ref() == line_cu.get_final_map_ref()); REQUIRE( - noise_fall_back_cu.get_final_map_ref() == - line_cu.get_final_map_ref()); + noise_fall_back_cu.get_final_map_ref() == line_cu.get_final_map_ref()); } } @@ -845,14 +835,12 @@ SCENARIO("FullPeepholeOptimise with various options") { Circuit compiled_circ_noswaps_tk2 = cu_noswaps_tk2.get_circ_ref(); unsigned n_gates_swaps_cx = compiled_circ_swaps_cx.n_gates(); unsigned n_cx_swaps_cx = compiled_circ_swaps_cx.count_gates(OpType::CX); - unsigned n_tk1_swaps_cx = - compiled_circ_swaps_cx.count_gates(OpType::TK1); unsigned - n_gates_swaps_tk2 = compiled_circ_swaps_tk2.n_gates(); unsigned - n_tk2_swaps_tk2 = compiled_circ_swaps_tk2.count_gates(OpType::TK2); - unsigned n_tk1_swaps_tk2 = - compiled_circ_swaps_tk2.count_gates(OpType::TK1); unsigned - n_gates_noswaps_cx = compiled_circ_noswaps_cx.n_gates(); unsigned - n_cx_noswaps_cx = compiled_circ_noswaps_cx.count_gates(OpType::CX); + unsigned n_tk1_swaps_cx = compiled_circ_swaps_cx.count_gates(OpType::TK1); + unsigned n_gates_swaps_tk2 = compiled_circ_swaps_tk2.n_gates(); + unsigned n_tk2_swaps_tk2 = compiled_circ_swaps_tk2.count_gates(OpType::TK2); + unsigned n_tk1_swaps_tk2 = compiled_circ_swaps_tk2.count_gates(OpType::TK1); + unsigned n_gates_noswaps_cx = compiled_circ_noswaps_cx.n_gates(); + unsigned n_cx_noswaps_cx = compiled_circ_noswaps_cx.count_gates(OpType::CX); unsigned n_tk1_noswaps_cx = compiled_circ_noswaps_cx.count_gates(OpType::TK1); unsigned n_gates_noswaps_tk2 = compiled_circ_noswaps_tk2.n_gates(); @@ -939,8 +927,7 @@ SCENARIO("rebase and decompose PhasePolyBox test") { Circuit c(1, 1); c.add_conditional_gate(OpType::H, {}, {0}, {0}, 1); CompilationUnit cu(c); - REQUIRE_THROWS_AS(ComposePhasePolyBoxes()->apply(cu), - UnsatisfiedPredicate); + REQUIRE_THROWS_AS(ComposePhasePolyBoxes()->apply(cu), UnsatisfiedPredicate); } GIVEN("NoWireSwapsPredicate for ComposePhasePolyBoxes") { Circuit circ(5); @@ -1287,8 +1274,7 @@ SCENARIO("Commute measurements to the end of a circuit") { CompilationUnit cu(c); REQUIRE_THROWS_AS(delay_pass->apply(cu), UnsatisfiedPredicate); } - GIVEN("Measure blocked by classical operation, using a partial delay pass") - { + GIVEN("Measure blocked by classical operation, using a partial delay pass") { Circuit c(2, 1); add_2qb_gates(c, OpType::Measure, {{0, 0}, {1, 0}}); c.add_op(OpType::Measure, {0, 0}); @@ -1320,15 +1306,13 @@ SCENARIO("Commute measurements to the end of a circuit") { c.add_op(OpType::CZ, {0, 1}); c.add_op(OpType::Measure, {0, 0}); c.add_conditional_gate(OpType::Z, {}, {1}, {0}, 1); - REQUIRE_THROWS_AS(Transforms::delay_measures().apply(c), - CircuitInvalidity); + REQUIRE_THROWS_AS(Transforms::delay_measures().apply(c), CircuitInvalidity); } GIVEN( - "Call on invalid nested circuit without checking the predicate throws") - { + "Call on invalid nested circuit without checking the predicate throws") { Circuit inner1(1, 2); - inner1.add_conditional_gate(OpType::Measure, {}, {0, 0}, {1}, - 1); CircBox cbox1(inner1); + inner1.add_conditional_gate(OpType::Measure, {}, {0, 0}, {1}, 1); + CircBox cbox1(inner1); Circuit inner2(1, 2); inner2.add_box(cbox1, {0, 0, 1}); @@ -1337,8 +1321,7 @@ SCENARIO("Commute measurements to the end of a circuit") { Circuit c(1, 2); c.add_box(cbox2, {0, 0, 1}); c.add_op(OpType::X, {0}); - REQUIRE_THROWS_AS(Transforms::delay_measures().apply(c), - CircuitInvalidity); + REQUIRE_THROWS_AS(Transforms::delay_measures().apply(c), CircuitInvalidity); } GIVEN("Combined with routing") { Circuit test(3, 1); @@ -1534,8 +1517,8 @@ SCENARIO("CX mapping pass") { } GIVEN("A circuit with measurements inside boxes.") { Circuit inner1(1, 2); - inner1.add_conditional_gate(OpType::Measure, {}, {0, 0}, {1}, - 1); CircBox cbox1(inner1); + inner1.add_conditional_gate(OpType::Measure, {}, {0, 0}, {1}, 1); + CircBox cbox1(inner1); Circuit inner2(1, 2); inner2.add_box(cbox1, {0, 0, 1}); From c376908aed830a19e5aefc25c798e591d678ed3a Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Tue, 1 Aug 2023 16:55:20 +0100 Subject: [PATCH 18/29] Update test_CompilerPass.cpp --- tket/test/src/test_CompilerPass.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tket/test/src/test_CompilerPass.cpp b/tket/test/src/test_CompilerPass.cpp index e41e79afdb..2addcca9e2 100644 --- a/tket/test/src/test_CompilerPass.cpp +++ b/tket/test/src/test_CompilerPass.cpp @@ -196,8 +196,8 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { REQUIRE(!cu.check_all_predicates()); } WHEN("Ran in safe mode") { - REQUIRE(cp_route->apply(cu, SafetyMode::Audit)); // warning should be - logged REQUIRE(!cu.check_all_predicates()); + REQUIRE(cp_route->apply(cu, SafetyMode::Audit)); // warning should be logged + REQUIRE(!cu.check_all_predicates()); } } GIVEN("Correct sequence of Synthesis, Routing and Rebasing") { From a6e70ec930c5173555c8d95d4185def2047aef9b Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Tue, 1 Aug 2023 16:55:38 +0100 Subject: [PATCH 19/29] Update test_CompilerPass.cpp --- tket/test/src/test_CompilerPass.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tket/test/src/test_CompilerPass.cpp b/tket/test/src/test_CompilerPass.cpp index 2addcca9e2..4b44715869 100644 --- a/tket/test/src/test_CompilerPass.cpp +++ b/tket/test/src/test_CompilerPass.cpp @@ -196,7 +196,8 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { REQUIRE(!cu.check_all_predicates()); } WHEN("Ran in safe mode") { - REQUIRE(cp_route->apply(cu, SafetyMode::Audit)); // warning should be logged + REQUIRE( + cp_route->apply(cu, SafetyMode::Audit)); // warning should be logged REQUIRE(!cu.check_all_predicates()); } } From 73723c7f5ca0a3c1da98619af62c6525b44d7fb7 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Tue, 1 Aug 2023 17:01:22 +0100 Subject: [PATCH 20/29] Update CircPool.cpp --- tket/src/Circuit/CircPool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index 9915f1a346..1f5eae9e25 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -986,7 +986,7 @@ static Circuit TK2_swap_replacement(std::array angles) { unsigned last_angle = 0; for (; last_angle < 3; ++last_angle) { std::optional eval = eval_expr_mod(angles[last_angle]); - if (eval && *eval > 0) { + if (eval) { angles_eval[last_angle] = *eval; } else { break; @@ -997,7 +997,7 @@ static Circuit TK2_swap_replacement(std::array angles) { for (; last_angle_swapped < 3; ++last_angle_swapped) { std::optional eval = eval_expr_mod(angles_swapped[last_angle_swapped]); - if (eval && *eval > 0) { + if (eval) { angles_eval_swapped[last_angle_swapped] = *eval; } else { break; From cf7198d6a2c365a7431417284c59533741699a30 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Wed, 2 Aug 2023 10:25:39 +0100 Subject: [PATCH 21/29] Remove sycamore tests --- pytket/tests/transform_test.py | 64 +++++++++++++++++----------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/pytket/tests/transform_test.py b/pytket/tests/transform_test.py index decc7953ea..de33143763 100644 --- a/pytket/tests/transform_test.py +++ b/pytket/tests/transform_test.py @@ -1230,15 +1230,15 @@ def test_auto_rebase_with_swap_cx() -> None: no_swap_pass.apply(c_no_swap) assert c_no_swap.n_gates_of_type(OpType.CX) == 2 - c_swap = Circuit(2).Sycamore(0, 1) - swap_pass.apply(c_swap) - assert c_swap.n_gates_of_type(OpType.CX) == 1 - iqp = c_swap.implicit_qubit_permutation() - assert iqp[Qubit(0)] == Qubit(1) - assert iqp[Qubit(1)] == Qubit(0) - c_no_swap = Circuit(2).Sycamore(0, 1) - no_swap_pass.apply(c_no_swap) - assert c_no_swap.n_gates_of_type(OpType.CX) == 3 + # c_swap = Circuit(2).Sycamore(0, 1) + # swap_pass.apply(c_swap) + # assert c_swap.n_gates_of_type(OpType.CX) == 1 + # iqp = c_swap.implicit_qubit_permutation() + # assert iqp[Qubit(0)] == Qubit(1) + # assert iqp[Qubit(1)] == Qubit(0) + # c_no_swap = Circuit(2).Sycamore(0, 1) + # no_swap_pass.apply(c_no_swap) + # assert c_no_swap.n_gates_of_type(OpType.CX) == 3 c_swap = Circuit(2).ISWAP(0.3, 0, 1) swap_pass.apply(c_swap) @@ -1295,18 +1295,18 @@ def test_auto_rebase_with_swap_zzmax() -> None: assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 2 assert c_no_swap.n_gates == 13 - c_swap = Circuit(2).Sycamore(0, 1) - swap_pass.apply(c_swap) - assert c_swap.n_gates_of_type(OpType.ZZMax) == 1 - assert c_swap.n_gates == 12 - iqp = c_swap.implicit_qubit_permutation() - assert iqp[Qubit(0)] == Qubit(1) - assert iqp[Qubit(1)] == Qubit(0) + # c_swap = Circuit(2).Sycamore(0, 1) + # swap_pass.apply(c_swap) + # assert c_swap.n_gates_of_type(OpType.ZZMax) == 1 + # assert c_swap.n_gates == 12 + # iqp = c_swap.implicit_qubit_permutation() + # assert iqp[Qubit(0)] == Qubit(1) + # assert iqp[Qubit(1)] == Qubit(0) - c_no_swap = Circuit(2).Sycamore(0, 1) - no_swap_pass.apply(c_no_swap) - assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 3 - assert c_no_swap.n_gates == 16 + # c_no_swap = Circuit(2).Sycamore(0, 1) + # no_swap_pass.apply(c_no_swap) + # assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 3 + # assert c_no_swap.n_gates == 16 c_swap = Circuit(2).ISWAP(0.3, 0, 1) swap_pass.apply(c_swap) @@ -1365,18 +1365,18 @@ def test_auto_rebase_with_swap_zzphase() -> None: assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 2 assert c_no_swap.n_gates == 13 - c_swap = Circuit(2).Sycamore(0, 1) - swap_pass.apply(c_swap) - assert c_swap.n_gates_of_type(OpType.ZZPhase) == 1 - assert c_swap.n_gates == 12 - iqp = c_swap.implicit_qubit_permutation() - assert iqp[Qubit(0)] == Qubit(1) - assert iqp[Qubit(1)] == Qubit(0) - - c_no_swap = Circuit(2).Sycamore(0, 1) - no_swap_pass.apply(c_no_swap) - assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 3 - assert c_no_swap.n_gates == 16 + # c_swap = Circuit(2).Sycamore(0, 1) + # swap_pass.apply(c_swap) + # assert c_swap.n_gates_of_type(OpType.ZZPhase) == 1 + # assert c_swap.n_gates == 12 + # iqp = c_swap.implicit_qubit_permutation() + # assert iqp[Qubit(0)] == Qubit(1) + # assert iqp[Qubit(1)] == Qubit(0) + + # c_no_swap = Circuit(2).Sycamore(0, 1) + # no_swap_pass.apply(c_no_swap) + # assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 3 + # assert c_no_swap.n_gates == 16 c_swap = Circuit(2).ISWAP(0.3, 0, 1) swap_pass.apply(c_swap) From 41bfb1c3d6a0464ad18abccce9ac45e83f4d6c25 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Wed, 2 Aug 2023 10:25:45 +0100 Subject: [PATCH 22/29] Update CircPool.cpp --- tket/src/Circuit/CircPool.cpp | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index 1f5eae9e25..daed91887c 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -983,7 +983,6 @@ static Circuit TK2_swap_replacement(std::array angles) { // Try to evaluate exprs to doubles. std::array angles_eval; std::array angles_eval_swapped; - unsigned last_angle = 0; for (; last_angle < 3; ++last_angle) { std::optional eval = eval_expr_mod(angles[last_angle]); if (eval) { @@ -991,16 +990,10 @@ static Circuit TK2_swap_replacement(std::array angles) { } else { break; } - } - - unsigned last_angle_swapped = 0; - for (; last_angle_swapped < 3; ++last_angle_swapped) { - std::optional eval = - eval_expr_mod(angles_swapped[last_angle_swapped]); - if (eval) { - angles_eval_swapped[last_angle_swapped] = *eval; - } else { - break; + if (allow_swaps) { + eval = eval_expr_mod(angles_swapped[last_angle]); + TKET_ASSERT(eval); + angles_eval_swapped[last_angle] = *eval; } } @@ -1008,13 +1001,11 @@ static Circuit TK2_swap_replacement(std::array angles) { if (last_angle <= 2) { if (equiv_0(angles[2], 4) && equiv_0(angles[1], 4)) { n_gates = 1; - } else if (equiv_0(angles[2], 4)) { - n_gates = 2; - } - } else if (last_angle_swapped <= 2) { - if (equiv_0(angles_swapped[2], 4) && equiv_0(angles_swapped[1], 4)) { + } else if (equiv_0(angles_swapped[2], 4) && equiv_0(angles_swapped[1], 4)) { implicit_swap = true; n_gates = 1; + } else if (equiv_0(angles[2], 4)) { + n_gates = 2; } else if (equiv_0(angles_swapped[2], 4)) { implicit_swap = true; n_gates = 2; From 27ab14ebc19d4dc1d5827e1d3c1df99f6079cd5e Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Wed, 2 Aug 2023 10:49:57 +0100 Subject: [PATCH 23/29] Update CircPool.cpp --- tket/src/Circuit/CircPool.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index daed91887c..2ab20fab98 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -983,6 +983,7 @@ static Circuit TK2_swap_replacement(std::array angles) { // Try to evaluate exprs to doubles. std::array angles_eval; std::array angles_eval_swapped; + unsigned last_angle = 0; for (; last_angle < 3; ++last_angle) { std::optional eval = eval_expr_mod(angles[last_angle]); if (eval) { @@ -990,11 +991,9 @@ static Circuit TK2_swap_replacement(std::array angles) { } else { break; } - if (allow_swaps) { - eval = eval_expr_mod(angles_swapped[last_angle]); - TKET_ASSERT(eval); - angles_eval_swapped[last_angle] = *eval; - } + eval = eval_expr_mod(angles_swapped[last_angle]); + TKET_ASSERT(eval); + angles_eval_swapped[last_angle] = *eval; } // Check if fewer gates can be used. From b6f942e96ff559a962948a7662bcb21b85244539 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Wed, 2 Aug 2023 16:33:37 +0100 Subject: [PATCH 24/29] Move TK2 SWAP decomp into c++, update overall method --- pytket/binders/circuit/library.cpp | 4 +++ pytket/pytket/passes/auto_rebase.py | 12 ++------- pytket/tests/transform_test.py | 7 ++--- tket/include/tket/Circuit/CircPool.hpp | 9 +++++++ tket/src/Circuit/CircPool.cpp | 12 ++++++++- tket/src/Transformations/Rebase.cpp | 36 ++++++++++++-------------- tket/test/src/test_CompilerPass.cpp | 21 ++++++++++----- 7 files changed, 60 insertions(+), 41 deletions(-) diff --git a/pytket/binders/circuit/library.cpp b/pytket/binders/circuit/library.cpp index 17380a72ce..7fd391391c 100644 --- a/pytket/binders/circuit/library.cpp +++ b/pytket/binders/circuit/library.cpp @@ -230,6 +230,10 @@ void init_library(py::module &m) { "_TK2_using_ZZPhase_and_swap", &CircPool::TK2_using_ZZPhase_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 ZZPhase gates."); + library_m.def( + "_TK2_using_TK2_or_swap", &CircPool::TK2_using_TK2_or_swap, + "Either the exact TK2, or a wire swap encoded in the implicit qubit " + "permutation of the Circuit and single qubit gates."); library_m.def( "_approx_TK2_using_1xZZPhase", &CircPool::approx_TK2_using_1xZZPhase, "Approximate equivalent to TK2, using 1 ZZPhase gate and single-qubit " diff --git a/pytket/pytket/passes/auto_rebase.py b/pytket/pytket/passes/auto_rebase.py index d22b9b23bc..4c9467fae5 100644 --- a/pytket/pytket/passes/auto_rebase.py +++ b/pytket/pytket/passes/auto_rebase.py @@ -37,14 +37,6 @@ def _TK2_using_TK2(a: Param, b: Param, c: Param) -> Circuit: return Circuit(2).TK2(a, b, c, 0, 1) -def _TK2_using_TK2_or_swap(a: Param, b: Param, c: Param) -> Circuit: - if a == b == c == 0.5: - c = Circuit(2).SWAP(0, 1) - c.replace_SWAPs() - return c - return Circuit(2).TK2(a, b, c, 0, 1) - - _TK2_CIRCS: Dict[OpType, Callable[[Param, Param, Param], "Circuit"]] = { OpType.TK2: _TK2_using_TK2, OpType.ZZPhase: _library._TK2_using_ZZPhase, @@ -53,8 +45,8 @@ def _TK2_using_TK2_or_swap(a: Param, b: Param, c: Param) -> Circuit: } _TK2_CIRCS_WIRE_SWAP: Dict[OpType, Callable[[Param, Param, Param], "Circuit"]] = { - OpType.TK2: _TK2_using_TK2_or_swap, - OpType.ZZPhase: _library._TK2_using_ZZPhase, + OpType.TK2: _library._TK2_using_TK2_or_swap, + OpType.ZZPhase: _library._TK2_using_ZZPhase_and_swap, OpType.CX: _library._TK2_using_CX_and_swap, OpType.ZZMax: _library._TK2_using_ZZMax_and_swap, } diff --git a/pytket/tests/transform_test.py b/pytket/tests/transform_test.py index de33143763..7ee97889dc 100644 --- a/pytket/tests/transform_test.py +++ b/pytket/tests/transform_test.py @@ -1388,7 +1388,7 @@ def test_auto_rebase_with_swap_zzphase() -> None: c_no_swap = Circuit(2).ISWAP(0.3, 0, 1) no_swap_pass.apply(c_no_swap) assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 2 - assert c_no_swap.n_gates == 13 + assert c_no_swap.n_gates == 14 c_swap = Circuit(2).ISWAPMax(0, 1).ISWAPMax(1, 0) swap_pass.apply(c_swap) @@ -1412,9 +1412,9 @@ def test_auto_rebase_with_swap_zzphase() -> None: c_no_swap = Circuit(2).SWAP(0, 1) no_swap_pass.apply(c_no_swap) assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 3 - assert c_no_swap.n_gates == 16 + assert c_no_swap.n_gates == 13 - c_swap = Circuit(2).ZZPhase(0, 1) + c_swap = Circuit(2).ZZPhase(0.4, 0, 1) swap_pass.apply(c_swap) assert c_swap.n_gates == 1 @@ -1428,6 +1428,7 @@ def test_auto_rebase_with_swap_tk2() -> None: no_swap_pass = auto_rebase_pass({OpType.TK2, OpType.PhasedX, OpType.Rz}, False) c_swap = Circuit(2).SWAP(0, 1) swap_pass.apply(c_swap) + print(c_swap.get_commands()) assert c_swap.n_gates == 0 c_no_swap = Circuit(2).SWAP(0, 1) no_swap_pass.apply(c_no_swap) diff --git a/tket/include/tket/Circuit/CircPool.hpp b/tket/include/tket/Circuit/CircPool.hpp index e80a8c8b04..c1e99cb1c2 100644 --- a/tket/include/tket/Circuit/CircPool.hpp +++ b/tket/include/tket/Circuit/CircPool.hpp @@ -387,6 +387,15 @@ Circuit TK2_using_ZZPhase( Circuit TK2_using_ZZPhase_and_swap( const Expr &alpha, const Expr &beta, const Expr &gamma); +/** + * @brief Either returns TK2(α, β, γ), or a wire swap and single qubit + * corrections + * + * @return Circuit Equivalent circuit, either a wire swap with single + * qubit corrections or TK2(α, β, γ). + */ +Circuit TK2_using_TK2_or_swap( + const Expr &alpha, const Expr &beta, const Expr &gamma); /** * @brief Equivalent to TK2(α, β, γ), using up to 3 ZZMax gates. * diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index 2ab20fab98..3154350304 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -1149,6 +1149,17 @@ Circuit TK2_using_ZZPhase_and_swap( return TK2_using_ZZPhase(alpha, beta, gamma); } +Circuit TK2_using_TK2_or_swap( + const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c = TK2_using_CX_and_swap(alpha, beta, gamma); + if (c.count_gates(OpType::CX) == 0) { + return c; + } + Circuit tk2(2); + tk2.add_op(OpType::TK2, {alpha, beta, gamma}, {0, 1}); + return tk2; +} + Circuit TK2_using_ZZMax( const Expr &alpha, const Expr &beta, const Expr &gamma) { Circuit c = TK2_using_CX(alpha, beta, gamma); @@ -1169,7 +1180,6 @@ Circuit TK2_using_ZZMax( Circuit TK2_using_ZZMax_and_swap( const Expr &alpha, const Expr &beta, const Expr &gamma) { Circuit c = TK2_using_CX_and_swap(alpha, beta, gamma); - if (c.count_gates(OpType::CX) < 3) { // Find the CX gates and replace them with ZZMax. VertexSet bin; diff --git a/tket/src/Transformations/Rebase.cpp b/tket/src/Transformations/Rebase.cpp index 14a060b2da..004d3fe7db 100644 --- a/tket/src/Transformations/Rebase.cpp +++ b/tket/src/Transformations/Rebase.cpp @@ -122,7 +122,6 @@ static bool standard_rebase_via_tk2( tk2_replacement) { bool success = false; VertexSet bin; - const bool TK2_allowed = allowed_gates.contains(OpType::TK2); // 1. Replace all multi-qubit gates outside the target gateset to TK2. for (const Vertex& v : circ.all_vertices()) { Op_ptr op = circ.get_Op_ptr_from_Vertex(v); @@ -139,27 +138,24 @@ static bool standard_rebase_via_tk2( continue; // need to convert Circuit replacement = TK2_circ_from_multiq(op); - // If TK2 gates are not allowed in the target gateset we find a - // replacement circuit by decomposing them - if (!TK2_allowed) { - VertexSet TK2_bin; - for (const Vertex& u : replacement.all_vertices()) { - Op_ptr op = circ.get_Op_ptr_from_Vertex(u); - TKET_ASSERT(op->get_type() != OpType::Conditional); - if (op->get_type() == OpType::TK2) { - std::vector params = op->get_params(); - TKET_ASSERT(params.size() == 3); - Circuit u_replacement = - tk2_replacement(params[0], params[1], params[2]); - replacement.substitute(u_replacement, u, Circuit::VertexDeletion::No); - TK2_bin.insert(u); - } + // Find replacement Circuit for all TK2 gates + VertexSet TK2_bin; + for (const Vertex& u : replacement.all_vertices()) { + Op_ptr op = circ.get_Op_ptr_from_Vertex(u); + TKET_ASSERT(op->get_type() != OpType::Conditional); + if (op->get_type() == OpType::TK2) { + std::vector params = op->get_params(); + TKET_ASSERT(params.size() == 3); + Circuit u_replacement = + tk2_replacement(params[0], params[1], params[2]); + replacement.substitute(u_replacement, u, Circuit::VertexDeletion::No); + TK2_bin.insert(u); } - Transforms::squash_1qb_to_tk1().apply(replacement); - remove_redundancies().apply(replacement); - replacement.remove_vertices( - TK2_bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes); } + Transforms::squash_1qb_to_tk1().apply(replacement); + remove_redundancies().apply(replacement); + replacement.remove_vertices( + TK2_bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes); if (conditional) { circ.substitute_conditional(replacement, v, Circuit::VertexDeletion::No); } else { diff --git a/tket/test/src/test_CompilerPass.cpp b/tket/test/src/test_CompilerPass.cpp index 4b44715869..55996b426f 100644 --- a/tket/test/src/test_CompilerPass.cpp +++ b/tket/test/src/test_CompilerPass.cpp @@ -1751,15 +1751,9 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { // c.add_op(OpType::Sycamore, {0, 1}); // CompilationUnit cu(c); // CHECK(pp_rebase_cx->apply(cu)); - // REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 1); + // REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) ==2); // auto u1 = tket_sim::get_unitary(c); // auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); - // CompilationUnit cu0(c); - // gen_rebase_pass_via_tk2( - // allowed_gates_cx, CircPool::TK2_using_CX, - // CircPool::tk1_to_PhasedXRz)->apply(cu0); - // auto u3 = tket_sim::get_unitary(cu.get_circ_ref()); - // REQUIRE(u1.isApprox(u3)); // REQUIRE(u1.isApprox(u2)); // } GIVEN("Targeting CX gates, ISWAP gate.") { @@ -1952,6 +1946,19 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); REQUIRE(u1.isApprox(u2)); } + GIVEN("Targeting TK2 gates, SWAP gate.") { + Circuit c(2); + c.add_op(OpType::SWAP, {0, 1}); + CompilationUnit cu(c); + CHECK(gen_rebase_pass_via_tk2( + {OpType::PhasedX, OpType::Rz, OpType::TK2}, + CircPool::TK2_using_TK2_or_swap, CircPool::tk1_to_PhasedXRz) + ->apply(cu)); + REQUIRE(cu.get_circ_ref().n_gates() == 0); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); + } } } // namespace test_CompilerPass } // namespace tket From 4fc8d9d7f4df652990b2a6cf419e279f4f4fa7a3 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Thu, 3 Aug 2023 12:41:55 +0100 Subject: [PATCH 25/29] Rewrite swap replacement ot be basic --- tket/src/Circuit/CircPool.cpp | 153 +++++----------------------- tket/test/src/test_CompilerPass.cpp | 65 ++++++------ 2 files changed, 59 insertions(+), 159 deletions(-) diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index 3154350304..79836a5de6 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -961,134 +961,35 @@ Circuit TK2_using_CX(const Expr &alpha, const Expr &beta, const Expr &gamma) { * @param angles The TK2 parameters * @return Circuit TK2-equivalent up to wire swap circuit */ -static Circuit TK2_swap_replacement(std::array angles) { - if (!in_weyl_chamber(angles)) { - throw std::domain_error("TK2 params are not normalised to Weyl chamber."); - } - unsigned n_gates = 3; // default to 3x CX - bool implicit_swap = false; // default to no implicit swap - - // Generate to support allowing swap gates - Circuit pre, post; - std::array angles_swapped; - angles_swapped = angles; - for (unsigned i = 0; i < 3; ++i) { - angles_swapped[i] += 0.5; - } - - std::tie(pre, angles_swapped, post) = normalise_TK2_angles( - angles_swapped[0], angles_swapped[1], angles_swapped[2]); - pre.add_phase(0.25); - - // Try to evaluate exprs to doubles. - std::array angles_eval; - std::array angles_eval_swapped; - unsigned last_angle = 0; - for (; last_angle < 3; ++last_angle) { - std::optional eval = eval_expr_mod(angles[last_angle]); - if (eval) { - angles_eval[last_angle] = *eval; - } else { - break; - } - eval = eval_expr_mod(angles_swapped[last_angle]); - TKET_ASSERT(eval); - angles_eval_swapped[last_angle] = *eval; - } - - // Check if fewer gates can be used. - if (last_angle <= 2) { - if (equiv_0(angles[2], 4) && equiv_0(angles[1], 4)) { - n_gates = 1; - } else if (equiv_0(angles_swapped[2], 4) && equiv_0(angles_swapped[1], 4)) { - implicit_swap = true; - n_gates = 1; - } else if (equiv_0(angles[2], 4)) { - n_gates = 2; - } else if (equiv_0(angles_swapped[2], 4)) { - implicit_swap = true; - n_gates = 2; - } - } else { - double max_fid = 0.; - - auto [a, b, c] = angles_eval; - auto [as, bs, cs] = angles_eval_swapped; - - double ncx_fid = trace_fidelity(a, b, c); - if (ncx_fid > max_fid) { - max_fid = ncx_fid; - n_gates = 0; - implicit_swap = false; - } - ncx_fid = trace_fidelity(as, bs, cs); - if (ncx_fid > max_fid) { - max_fid = ncx_fid; - n_gates = 0; - implicit_swap = true; - } - ncx_fid = trace_fidelity(0.5 - a, b, c); - if (ncx_fid > max_fid) { - max_fid = ncx_fid; - n_gates = 1; - implicit_swap = false; - } - ncx_fid = trace_fidelity(0.5 - as, bs, cs); - if (ncx_fid > max_fid) { - max_fid = ncx_fid; - n_gates = 1; - implicit_swap = true; - } - ncx_fid = trace_fidelity(0, 0, c); - if (ncx_fid > max_fid) { - max_fid = ncx_fid; - n_gates = 2; - implicit_swap = false; - } - ncx_fid = trace_fidelity(0, 0, cs); - if (ncx_fid > max_fid) { - max_fid = ncx_fid; - n_gates = 2; - implicit_swap = true; - } - if (1 > max_fid) { - max_fid = 1; - n_gates = 3; - implicit_swap = false; - } - } - - // Build circuit for substitution. - Circuit sub(2); - switch (n_gates) { - case 0: - break; - case 1: { - sub.append(CircPool::approx_TK2_using_1xCX()); - break; - } - case 2: { - sub.append(CircPool::approx_TK2_using_2xCX(angles[0], angles[1])); - break; - } - case 3: { - sub.append(CircPool::TK2_using_3xCX(angles[0], angles[1], angles[2])); - break; - } - default: - TKET_ASSERT(!"Number of CX invalid in decompose_TK2"); +static Circuit normalised_TK2_using_CX_and_swap( + const Expr &alpha, const Expr &beta, const Expr &gamma) { + // first construct parameters for producing TK2 with wire swap + std::tuple, Circuit> pre_angles_post = + normalise_TK2_angles(alpha + 0.5, beta + 0.5, gamma + 0.5); + std::array swap_angles = std::get<1>(pre_angles_post); + + // generate two circuits for both sets of angles + Circuit without_swap = normalised_TK2_using_CX(alpha, beta, gamma); + Circuit with_swap = + normalised_TK2_using_CX(swap_angles[0], swap_angles[1], swap_angles[2]); + + // if without_swap has same or fewer number of CX gates, return it, else build + // with_swap circuit + if (without_swap.count_gates(OpType::CX) <= + with_swap.count_gates(OpType::CX)) { + return without_swap; } + Circuit swap(2); + swap.add_op(OpType::SWAP, {0, 1}); + with_swap = std::get<0>(pre_angles_post) >> with_swap >> + std::get<2>(pre_angles_post) >> swap; + with_swap.add_phase(0.25); + with_swap.replace_SWAPs(); - if (implicit_swap) { - Circuit swap(2); - swap.add_op(OpType::SWAP, {0, 1}); - sub = pre >> sub >> post >> swap; - sub.replace_SWAPs(); - } // This decomposition can leave many extraneous single qubits gates: squash // them into TK1 that can be resynthesised - Transforms::squash_1qb_to_tk1().apply(sub); - return sub; + Transforms::squash_1qb_to_tk1().apply(with_swap); + return with_swap; } Circuit TK2_using_CX_and_swap( @@ -1100,8 +1001,8 @@ Circuit TK2_using_CX_and_swap( if (op->get_type() == OpType::TK2) { std::vector params = op->get_params(); TKET_ASSERT(params.size() == 3); - std::array arr_params = {params[0], params[1], params[2]}; - Circuit rep = TK2_swap_replacement(arr_params); + Circuit rep = + normalised_TK2_using_CX_and_swap(params[0], params[1], params[2]); c.substitute(rep, v, Circuit::VertexDeletion::Yes); break; } diff --git a/tket/test/src/test_CompilerPass.cpp b/tket/test/src/test_CompilerPass.cpp index 55996b426f..5a729c3b3f 100644 --- a/tket/test/src/test_CompilerPass.cpp +++ b/tket/test/src/test_CompilerPass.cpp @@ -196,9 +196,8 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { REQUIRE(!cu.check_all_predicates()); } WHEN("Ran in safe mode") { - REQUIRE( - cp_route->apply(cu, SafetyMode::Audit)); // warning should be logged - REQUIRE(!cu.check_all_predicates()); + REQUIRE(cp_route->apply(cu, SafetyMode::Audit)); // warning should be + logged REQUIRE(!cu.check_all_predicates()); } } GIVEN("Correct sequence of Synthesis, Routing and Rebasing") { @@ -1746,16 +1745,16 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); REQUIRE(u1.isApprox(u2)); } - // GIVEN("Targeting CX gates, Sycamore gate.") { - // Circuit c(2); - // c.add_op(OpType::Sycamore, {0, 1}); - // CompilationUnit cu(c); - // CHECK(pp_rebase_cx->apply(cu)); - // REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) ==2); - // auto u1 = tket_sim::get_unitary(c); - // auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); - // REQUIRE(u1.isApprox(u2)); - // } + GIVEN("Targeting CX gates, Sycamore gate.") { + Circuit c(2); + c.add_op(OpType::Sycamore, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_cx->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::CX) == 2); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); + } GIVEN("Targeting CX gates, ISWAP gate.") { Circuit c(2); c.add_op(OpType::ISWAP, 0.3, {0, 1}); @@ -1826,16 +1825,16 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); REQUIRE(u1.isApprox(u2)); } - // GIVEN("Targeting ZZMax gates, Sycamore gate.") { - // Circuit c(2); - // c.add_op(OpType::Sycamore, {0, 1}); - // CompilationUnit cu(c); - // CHECK(pp_rebase_zzmax->apply(cu)); - // REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 1); - // auto u1 = tket_sim::get_unitary(c); - // auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); - // REQUIRE(u1.isApprox(u2)); - // } + GIVEN("Targeting ZZMax gates, Sycamore gate.") { + Circuit c(2); + c.add_op(OpType::Sycamore, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_zzmax->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZMax) == 2); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); + } GIVEN("Targeting ZZMax gates, SWAP gate.") { Circuit c(2); c.add_op(OpType::SWAP, {0, 1}); @@ -1896,16 +1895,16 @@ SCENARIO("Custom rebase pass with implicit wire swaps.") { auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); REQUIRE(u1.isApprox(u2)); } - // GIVEN("Targeting ZZPhase gates, Sycamore gate.") { - // Circuit c(2); - // c.add_op(OpType::Sycamore, {0, 1}); - // CompilationUnit cu(c); - // CHECK(pp_rebase_zzphase->apply(cu)); - // REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZPhase) == 1); - // auto u1 = tket_sim::get_unitary(c); - // auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); - // REQUIRE(u1.isApprox(u2)); - // } + GIVEN("Targeting ZZPhase gates, Sycamore gate.") { + Circuit c(2); + c.add_op(OpType::Sycamore, {0, 1}); + CompilationUnit cu(c); + CHECK(pp_rebase_zzphase->apply(cu)); + REQUIRE(cu.get_circ_ref().count_gates(OpType::ZZPhase) == 2); + auto u1 = tket_sim::get_unitary(c); + auto u2 = tket_sim::get_unitary(cu.get_circ_ref()); + REQUIRE(u1.isApprox(u2)); + } GIVEN("Targeting ZZPhase gates, SWAP gate.") { Circuit c(2); c.add_op(OpType::SWAP, {0, 1}); From 97021753e39edad9d2f55dbb51e7b23e7a049868 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Thu, 3 Aug 2023 12:51:03 +0100 Subject: [PATCH 26/29] Update test_CompilerPass.cpp --- tket/test/src/test_CompilerPass.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tket/test/src/test_CompilerPass.cpp b/tket/test/src/test_CompilerPass.cpp index 5a729c3b3f..bb798770e6 100644 --- a/tket/test/src/test_CompilerPass.cpp +++ b/tket/test/src/test_CompilerPass.cpp @@ -196,8 +196,9 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { REQUIRE(!cu.check_all_predicates()); } WHEN("Ran in safe mode") { - REQUIRE(cp_route->apply(cu, SafetyMode::Audit)); // warning should be - logged REQUIRE(!cu.check_all_predicates()); + REQUIRE( + cp_route->apply(cu, SafetyMode::Audit)); // warning should be logged + REQUIRE(!cu.check_all_predicates()); } } GIVEN("Correct sequence of Synthesis, Routing and Rebasing") { From ae477586bfe7f3cf67dde9e0cdc1e56557526342 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Thu, 3 Aug 2023 13:34:58 +0100 Subject: [PATCH 27/29] Fix missing space and readd python tests --- pytket/binders/circuit/library.cpp | 6 +-- pytket/tests/transform_test.py | 63 +++++++++++++++--------------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/pytket/binders/circuit/library.cpp b/pytket/binders/circuit/library.cpp index 7fd391391c..2438b53222 100644 --- a/pytket/binders/circuit/library.cpp +++ b/pytket/binders/circuit/library.cpp @@ -45,7 +45,7 @@ void init_library(py::module &m) { "_TK2_using_CX_and_swap", &CircPool::TK2_using_CX_and_swap, "Given expressions α, β and γ, return circuit equivalent to " "TK2(α, β, γ), up to a wire swap that is encoded in the implicit " - " qubit permutation of the Circuit, using up to 3 CX and single-qubit " + "qubit permutation of the Circuit, using up to 3 CX and single-qubit " "gates.\n\n" "The decomposition minimizes the number of CX gates."); library_m.def( @@ -229,7 +229,7 @@ void init_library(py::module &m) { library_m.def( "_TK2_using_ZZPhase_and_swap", &CircPool::TK2_using_ZZPhase_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 ZZPhase gates."); + "qubit permutation of the Circuit, using up to 3 ZZPhase gates."); library_m.def( "_TK2_using_TK2_or_swap", &CircPool::TK2_using_TK2_or_swap, "Either the exact TK2, or a wire swap encoded in the implicit qubit " @@ -248,7 +248,7 @@ void init_library(py::module &m) { library_m.def( "_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."); + "qubit permutation of the Circuit, using up to 3 ZZMax gates."); library_m.def( "_XXPhase3_using_TK2", &CircPool::XXPhase3_using_TK2, "Equivalent to XXPhase3, using three TK2 gates"); diff --git a/pytket/tests/transform_test.py b/pytket/tests/transform_test.py index 7ee97889dc..e5173358b1 100644 --- a/pytket/tests/transform_test.py +++ b/pytket/tests/transform_test.py @@ -1230,15 +1230,15 @@ def test_auto_rebase_with_swap_cx() -> None: no_swap_pass.apply(c_no_swap) assert c_no_swap.n_gates_of_type(OpType.CX) == 2 - # c_swap = Circuit(2).Sycamore(0, 1) - # swap_pass.apply(c_swap) - # assert c_swap.n_gates_of_type(OpType.CX) == 1 - # iqp = c_swap.implicit_qubit_permutation() - # assert iqp[Qubit(0)] == Qubit(1) - # assert iqp[Qubit(1)] == Qubit(0) - # c_no_swap = Circuit(2).Sycamore(0, 1) - # no_swap_pass.apply(c_no_swap) - # assert c_no_swap.n_gates_of_type(OpType.CX) == 3 + c_swap = Circuit(2).Sycamore(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.CX) == 2 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(1) + assert iqp[Qubit(1)] == Qubit(0) + c_no_swap = Circuit(2).Sycamore(0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.CX) == 3 c_swap = Circuit(2).ISWAP(0.3, 0, 1) swap_pass.apply(c_swap) @@ -1295,18 +1295,18 @@ def test_auto_rebase_with_swap_zzmax() -> None: assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 2 assert c_no_swap.n_gates == 13 - # c_swap = Circuit(2).Sycamore(0, 1) - # swap_pass.apply(c_swap) - # assert c_swap.n_gates_of_type(OpType.ZZMax) == 1 - # assert c_swap.n_gates == 12 - # iqp = c_swap.implicit_qubit_permutation() - # assert iqp[Qubit(0)] == Qubit(1) - # assert iqp[Qubit(1)] == Qubit(0) + c_swap = Circuit(2).Sycamore(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.ZZMax) == 2 + assert c_swap.n_gates == 12 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(1) + assert iqp[Qubit(1)] == Qubit(0) - # c_no_swap = Circuit(2).Sycamore(0, 1) - # no_swap_pass.apply(c_no_swap) - # assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 3 - # assert c_no_swap.n_gates == 16 + c_no_swap = Circuit(2).Sycamore(0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.ZZMax) == 3 + assert c_no_swap.n_gates == 16 c_swap = Circuit(2).ISWAP(0.3, 0, 1) swap_pass.apply(c_swap) @@ -1365,18 +1365,18 @@ def test_auto_rebase_with_swap_zzphase() -> None: assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 2 assert c_no_swap.n_gates == 13 - # c_swap = Circuit(2).Sycamore(0, 1) - # swap_pass.apply(c_swap) - # assert c_swap.n_gates_of_type(OpType.ZZPhase) == 1 - # assert c_swap.n_gates == 12 - # iqp = c_swap.implicit_qubit_permutation() - # assert iqp[Qubit(0)] == Qubit(1) - # assert iqp[Qubit(1)] == Qubit(0) + c_swap = Circuit(2).Sycamore(0, 1) + swap_pass.apply(c_swap) + assert c_swap.n_gates_of_type(OpType.ZZPhase) == 2 + assert c_swap.n_gates == 12 + iqp = c_swap.implicit_qubit_permutation() + assert iqp[Qubit(0)] == Qubit(1) + assert iqp[Qubit(1)] == Qubit(0) - # c_no_swap = Circuit(2).Sycamore(0, 1) - # no_swap_pass.apply(c_no_swap) - # assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 3 - # assert c_no_swap.n_gates == 16 + c_no_swap = Circuit(2).Sycamore(0, 1) + no_swap_pass.apply(c_no_swap) + assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 3 + assert c_no_swap.n_gates == 16 c_swap = Circuit(2).ISWAP(0.3, 0, 1) swap_pass.apply(c_swap) @@ -1428,7 +1428,6 @@ def test_auto_rebase_with_swap_tk2() -> None: no_swap_pass = auto_rebase_pass({OpType.TK2, OpType.PhasedX, OpType.Rz}, False) c_swap = Circuit(2).SWAP(0, 1) swap_pass.apply(c_swap) - print(c_swap.get_commands()) assert c_swap.n_gates == 0 c_no_swap = Circuit(2).SWAP(0, 1) no_swap_pass.apply(c_no_swap) From a65eb1475fed47af7ccbbb640ceebcb98ea0e85f Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Thu, 3 Aug 2023 13:56:09 +0100 Subject: [PATCH 28/29] Update transform_test.py --- pytket/tests/transform_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pytket/tests/transform_test.py b/pytket/tests/transform_test.py index e5173358b1..540d113225 100644 --- a/pytket/tests/transform_test.py +++ b/pytket/tests/transform_test.py @@ -1298,7 +1298,7 @@ def test_auto_rebase_with_swap_zzmax() -> None: c_swap = Circuit(2).Sycamore(0, 1) swap_pass.apply(c_swap) assert c_swap.n_gates_of_type(OpType.ZZMax) == 2 - assert c_swap.n_gates == 12 + assert c_swap.n_gates == 11 iqp = c_swap.implicit_qubit_permutation() assert iqp[Qubit(0)] == Qubit(1) assert iqp[Qubit(1)] == Qubit(0) @@ -1368,7 +1368,7 @@ def test_auto_rebase_with_swap_zzphase() -> None: c_swap = Circuit(2).Sycamore(0, 1) swap_pass.apply(c_swap) assert c_swap.n_gates_of_type(OpType.ZZPhase) == 2 - assert c_swap.n_gates == 12 + assert c_swap.n_gates == 11 iqp = c_swap.implicit_qubit_permutation() assert iqp[Qubit(0)] == Qubit(1) assert iqp[Qubit(1)] == Qubit(0) From 5e24c2da1dee843a8dd780b1f50ae409b92ba555 Mon Sep 17 00:00:00 2001 From: sjdilkes Date: Thu, 3 Aug 2023 14:45:42 +0100 Subject: [PATCH 29/29] Update transform_test.py --- pytket/tests/transform_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/tests/transform_test.py b/pytket/tests/transform_test.py index 540d113225..49e2d8a919 100644 --- a/pytket/tests/transform_test.py +++ b/pytket/tests/transform_test.py @@ -1376,7 +1376,7 @@ def test_auto_rebase_with_swap_zzphase() -> None: c_no_swap = Circuit(2).Sycamore(0, 1) no_swap_pass.apply(c_no_swap) assert c_no_swap.n_gates_of_type(OpType.ZZPhase) == 3 - assert c_no_swap.n_gates == 16 + assert c_no_swap.n_gates == 15 c_swap = Circuit(2).ISWAP(0.3, 0, 1) swap_pass.apply(c_swap)