From 00f3ef3fe612bc365028764b3f8224ec25721eae Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Mon, 8 Jul 2024 15:23:03 +0100 Subject: [PATCH 01/57] Initial refactor --- .../GreedyPauliOptimisation.hpp | 127 +- .../GreedyPauliOptimisationLookupTables.hpp | 2032 +++++------------ 2 files changed, 594 insertions(+), 1565 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 1a7cdbbf0f..ac4c0e10e2 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -40,13 +40,17 @@ enum class TQEType : unsigned { }; /** - * @brief Local Clifford - * + * @brief The type of a pair of Pauli letters defined by + their commutation relation + * */ -enum class LocalCliffordType { - H, - S, - V, +enum class COMMUTE_TYPE : unsigned { + // Both are identity + Identity, + // Anti-commute + AntiCommute, + // Commute and not both identity + Commute, }; /** @@ -56,32 +60,25 @@ enum class LocalCliffordType { using TQE = std::tuple; /** - * @brief A Pauli exponential described by its commutation relations - * with the rows in a reference Clifford tableau. - * We store the commutation relations using an n-dimensional - * vector with entries in {0,1,2,3}, where - * 0: commute with ith Z row and ith X row - * 1: commute with ith Z row and anti-commute with ith X row - * 2: anti-commute with ith Z row and commute with ith X row - * 3: anti-commute with ith Z row and anti-commute with ith X row - * We call such vector a support vector + * @brief A Pauli exponential defined by a padded Pauli string + * and a rotation angle */ -class PauliExpNode { +class PauliRotation { public: /** - * @brief Construct a new PauliExpNode object. + * @brief Construct a new PauliRotation object. * - * @param support_vec the support vector + * @param string the Pauli string * @param theta the rotation angle in half-turns */ - PauliExpNode(std::vector support_vec, Expr theta); + PauliRotation(std::vector string, Expr theta); /** * @brief Number of TQEs required to reduce the weight to 1 * * @return unsigned */ - unsigned tqe_cost() const { return tqe_cost_; } + unsigned tqe_cost() const { return weight_; } /** * @brief Number of TQEs would required to reduce the weight to 1 @@ -108,38 +105,39 @@ class PauliExpNode { std::vector reduction_tqes() const; /** - * @brief Return the index and value of the first support + * @brief Return the index and value of the first non-identity * - * @return std::pair + * @return std::pair */ - std::pair first_support() const; + std::pair first_support() const; private: - std::vector support_vec_; + std::vector string_; Expr theta_; - unsigned tqe_cost_; + // extra cached data used by greedy synthesis + unsigned weight_; }; /** - * @brief Each row of a Clifford tableau consists a pair of anti-commuting - * Pauli strings (p0,p1). Similar to the PauliExpNode, such pairs can be - * alternatively described by their commutation relations with the rows in a - * reference Clifford tableau. Let Xi and Zi be the ith X row and the ith Z row - * in a reference Tableau T, then the commutation relation between (p0, p1) and - * the ith row of T is defined by how p0, p1 commute with Xi and Zi. That's 4 - * bits of information. We store such information using an n-dimensional vector - * with entries in {0,1,2,...,15}. The 4 bits from the most significant to the - * least are: f(p0, Xi), f(p0,Zi), f(q,Xi), f(q,Zi) where f(p,q)==1 if p,q - * anti-commute and 0 otherwise + * @brief Defines how a Pauli X and a Pauli Z on the same qubit + * get propagated from right to left through a Clifford operator. + * A n-qubit Clifford operator is completely defined by n such propagations + * with one on each qubit. A PauliPropagation also corresponds to a row in + * a Clifford tableau */ -class TableauRowNode { +class PauliPropagation { public: + /** - * @brief Construct a new TableauRowNode object. - * - * @param support_vec the support vector + * @brief Construct a new PauliPropagation object + * + * @param z_propagation + * @param x_propagation + * @param z_sign + * @param x_sign + * @param qubit_index */ - TableauRowNode(std::vector support_vec); + PauliPropagation(std::vector z_propagation,std::vector x_propagation, bool z_sign, bool x_sign, unsigned qubit_index); /** * @brief Number of TQEs required to reduce the weight to 1 @@ -158,40 +156,49 @@ class TableauRowNode { /** * @brief Update the support vector with a TQE gate - * - * @param tqe + * + * @param tqe */ void update(const TQE& tqe); + /** + * @brief Update the support vector with a single-qubit Clifford gate + * + * @param tqe + */ + void update(const OpType& sq_cliff, const unsigned& a); + + /** + * @brief Update the support vector with a SWAP gate + * + * @param tqe + */ + void swap(const unsigned& a, const unsigned& a); + /** * @brief Return all possible TQE gates that will reduce the tqe cost - * - * @return std::vector> + * + * @return std::vector */ std::vector reduction_tqes() const; /** - * @brief Return the index and value of the first support + * @brief Return the index and value of the first anti-commute entry */ - std::pair first_support() const; + std::tuple first_support() const; private: - std::vector support_vec_; - unsigned n_weaks_; - unsigned n_strongs_; - unsigned tqe_cost_; + std::vector z_propagation_; + std::vector x_propagation_; + bool z_sign_; + bool x_sign_; + unsigned qubit_index_; + // extra cached data used by greedy synthesis + std::vector commute_type_vec_; + unsigned n_commute_entries_; + unsigned n_anti_commute_entries_; }; -/** - * @brief The commutation relation between a TableauRowNode (p0,p1) and the ith - * row of the reference Tableau can be further classified as Strong, Weak or - * No-support. - */ -enum class SupportType : unsigned { - Strong, - Weak, - No, -}; /** * @brief Given a circuit consists of PauliExpBoxes followed by clifford gates, diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp index 2445513636..78931cf5b9 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp @@ -22,1545 +22,567 @@ namespace Transforms { namespace GreedyPauliSimp { -struct hash_tuple { - size_t operator()(const std::tuple& t) const { - return static_cast(std::get<0>(t)) * 10000 + - (std::get<1>(t) + 1) * 100 + std::get<2>(t); +struct hash_pauli_pauli { + size_t operator()(const std::pair& pair) const { + return pair.first * 10 + pair.second; } }; - -struct hash_pair { - size_t operator()(const std::pair& pair) const { - return pair.first * 100 + pair.second; +struct hash_optype_pauli { + size_t operator()(const std::pair& pair) const { + return pair.first * 10 + pair.second; + } +}; +struct hash_triple { + size_t operator()(const std::tuple& t) const { + return static_cast(std::get<0>(t)) * 100 + + (std::get<1>(t) + 1) * 10 + std::get<2>(t); + } +}; +struct hash_quadruple { + size_t operator()(const std::tuple& t) const { + return static_cast(std::get<0>(t)) * 1000 + + (std::get<1>(t) + 1) * 100 + (std::get<2>(t) + 1) * 10 + + std::get<3>(t); } }; /** - * @brief These are pre-calculated based on some property of the 2x2 matrix - * (i.e. local support matrix) defined by f(p0,Xi), f(p0,Zi), f(q,Xi), f(q,Zi) - * arxiv.org/abs/2305.10966 eq.27 + * @brief Transform a pair of anti-commuting pauli letters at the + * right-hand-side to Z/X For example, Sdg; H; X/Y = Z/X; Sdg; H + * */ -const static std::unordered_map FACTOR_WEAKNESS_MAP = { - {0, SupportType::No}, {1, SupportType::Weak}, - {2, SupportType::Weak}, {3, SupportType::Weak}, - {4, SupportType::Weak}, {5, SupportType::Weak}, - {6, SupportType::Strong}, {7, SupportType::Strong}, - {8, SupportType::Weak}, {9, SupportType::Strong}, - {10, SupportType::Weak}, {11, SupportType::Strong}, - {12, SupportType::Weak}, {13, SupportType::Strong}, - {14, SupportType::Strong}, {15, SupportType::Weak}}; +const static std::unordered_map AA_TO_ZX = { + {OpType::H, OpType::H}, + {OpType::S, OpType::Sdg}, + {OpType::Sdg, OpType::S}, + {OpType::V, OpType::Vdg}, + {OpType::Vdg, OpType::V}}; /** - * @brief Given a strong support in a factor support vector, returns the local - * clifford gates that turn it into an identity (i.e. 9). + * @brief Transform a pair of anti-commuting pauli letters at the + * right-hand-side to Z/X For example, Sdg; H; X/Y = Z/X; Sdg; H */ -const static std::unordered_map> - FACTOR_STRONG_TO_LOCALS = { - {9, {}}, - {6, {LocalCliffordType::H}}, - {11, {LocalCliffordType::S}}, - {13, {LocalCliffordType::V}}, - {14, {LocalCliffordType::S, LocalCliffordType::H}}, - {7, {LocalCliffordType::H, LocalCliffordType::S}}, -}; +const static std::unordered_map< + const std::pair & pair, std::vector, hash_pauli_pauli> + AA_TO_ZX = { + {{Pauli::X, Pauli::Y}, {OpType::Sdg, OpType::H}}, + {{Pauli::X, Pauli::Z}, {OpType::H}}, + {{Pauli::Y, Pauli::X}, {OpType::Vdg}}, + {{Pauli::Y, Pauli::Z}, {OpType::H, OpType::S}}, + {{Pauli::Z, Pauli::X}, {}}, + {{Pauli::Z, Pauli::Y}, {OpType::S}}}; /** - * @brief Transform a pair of entries in a singlet support vector using a TQE + * @brief Given a SQ Clifford gate g and a Pauli operator P, return Pauli + * P', and sign k such that g;P; = k* P';g + * */ const static std::unordered_map< - std::tuple, std::pair, - hash_tuple> - SINGLET_PAIR_TRANSFORMATION_MAP = { - {{TQEType::XX, 2, 2}, {2, 2}}, {{TQEType::XY, 2, 2}, {0, 2}}, - {{TQEType::XZ, 2, 2}, {0, 2}}, {{TQEType::YX, 2, 2}, {2, 0}}, - {{TQEType::YY, 2, 2}, {1, 1}}, {{TQEType::YZ, 2, 2}, {1, 3}}, - {{TQEType::ZX, 2, 2}, {2, 0}}, {{TQEType::ZY, 2, 2}, {3, 1}}, - {{TQEType::ZZ, 2, 2}, {3, 3}}, {{TQEType::XX, 3, 2}, {3, 0}}, - {{TQEType::XY, 3, 2}, {1, 1}}, {{TQEType::XZ, 3, 2}, {1, 3}}, - {{TQEType::YX, 3, 2}, {3, 2}}, {{TQEType::YY, 3, 2}, {0, 2}}, - {{TQEType::YZ, 3, 2}, {0, 2}}, {{TQEType::ZX, 3, 2}, {3, 0}}, - {{TQEType::ZY, 3, 2}, {2, 1}}, {{TQEType::ZZ, 3, 2}, {2, 3}}, - {{TQEType::XX, 1, 2}, {1, 0}}, {{TQEType::XY, 1, 2}, {3, 1}}, - {{TQEType::XZ, 1, 2}, {3, 3}}, {{TQEType::YX, 1, 2}, {1, 0}}, - {{TQEType::YY, 1, 2}, {2, 1}}, {{TQEType::YZ, 1, 2}, {2, 3}}, - {{TQEType::ZX, 1, 2}, {1, 2}}, {{TQEType::ZY, 1, 2}, {0, 2}}, - {{TQEType::ZZ, 1, 2}, {0, 2}}, {{TQEType::XX, 0, 2}, {0, 2}}, - {{TQEType::XY, 0, 2}, {2, 2}}, {{TQEType::XZ, 0, 2}, {2, 2}}, - {{TQEType::YX, 0, 2}, {0, 2}}, {{TQEType::YY, 0, 2}, {3, 2}}, - {{TQEType::YZ, 0, 2}, {3, 2}}, {{TQEType::ZX, 0, 2}, {0, 2}}, - {{TQEType::ZY, 0, 2}, {1, 2}}, {{TQEType::ZZ, 0, 2}, {1, 2}}, - {{TQEType::XX, 2, 3}, {0, 3}}, {{TQEType::XY, 2, 3}, {2, 3}}, - {{TQEType::XZ, 2, 3}, {0, 3}}, {{TQEType::YX, 2, 3}, {1, 1}}, - {{TQEType::YY, 2, 3}, {2, 0}}, {{TQEType::YZ, 2, 3}, {1, 2}}, - {{TQEType::ZX, 2, 3}, {3, 1}}, {{TQEType::ZY, 2, 3}, {2, 0}}, - {{TQEType::ZZ, 2, 3}, {3, 2}}, {{TQEType::XX, 3, 3}, {1, 1}}, - {{TQEType::XY, 3, 3}, {3, 0}}, {{TQEType::XZ, 3, 3}, {1, 2}}, - {{TQEType::YX, 3, 3}, {0, 3}}, {{TQEType::YY, 3, 3}, {3, 3}}, - {{TQEType::YZ, 3, 3}, {0, 3}}, {{TQEType::ZX, 3, 3}, {2, 1}}, - {{TQEType::ZY, 3, 3}, {3, 0}}, {{TQEType::ZZ, 3, 3}, {2, 2}}, - {{TQEType::XX, 1, 3}, {3, 1}}, {{TQEType::XY, 1, 3}, {1, 0}}, - {{TQEType::XZ, 1, 3}, {3, 2}}, {{TQEType::YX, 1, 3}, {2, 1}}, - {{TQEType::YY, 1, 3}, {1, 0}}, {{TQEType::YZ, 1, 3}, {2, 2}}, - {{TQEType::ZX, 1, 3}, {0, 3}}, {{TQEType::ZY, 1, 3}, {1, 3}}, - {{TQEType::ZZ, 1, 3}, {0, 3}}, {{TQEType::XX, 0, 3}, {2, 3}}, - {{TQEType::XY, 0, 3}, {0, 3}}, {{TQEType::XZ, 0, 3}, {2, 3}}, - {{TQEType::YX, 0, 3}, {3, 3}}, {{TQEType::YY, 0, 3}, {0, 3}}, - {{TQEType::YZ, 0, 3}, {3, 3}}, {{TQEType::ZX, 0, 3}, {1, 3}}, - {{TQEType::ZY, 0, 3}, {0, 3}}, {{TQEType::ZZ, 0, 3}, {1, 3}}, - {{TQEType::XX, 2, 1}, {0, 1}}, {{TQEType::XY, 2, 1}, {0, 1}}, - {{TQEType::XZ, 2, 1}, {2, 1}}, {{TQEType::YX, 2, 1}, {1, 3}}, - {{TQEType::YY, 2, 1}, {1, 2}}, {{TQEType::YZ, 2, 1}, {2, 0}}, - {{TQEType::ZX, 2, 1}, {3, 3}}, {{TQEType::ZY, 2, 1}, {3, 2}}, - {{TQEType::ZZ, 2, 1}, {2, 0}}, {{TQEType::XX, 3, 1}, {1, 3}}, - {{TQEType::XY, 3, 1}, {1, 2}}, {{TQEType::XZ, 3, 1}, {3, 0}}, - {{TQEType::YX, 3, 1}, {0, 1}}, {{TQEType::YY, 3, 1}, {0, 1}}, - {{TQEType::YZ, 3, 1}, {3, 1}}, {{TQEType::ZX, 3, 1}, {2, 3}}, - {{TQEType::ZY, 3, 1}, {2, 2}}, {{TQEType::ZZ, 3, 1}, {3, 0}}, - {{TQEType::XX, 1, 1}, {3, 3}}, {{TQEType::XY, 1, 1}, {3, 2}}, - {{TQEType::XZ, 1, 1}, {1, 0}}, {{TQEType::YX, 1, 1}, {2, 3}}, - {{TQEType::YY, 1, 1}, {2, 2}}, {{TQEType::YZ, 1, 1}, {1, 0}}, - {{TQEType::ZX, 1, 1}, {0, 1}}, {{TQEType::ZY, 1, 1}, {0, 1}}, - {{TQEType::ZZ, 1, 1}, {1, 1}}, {{TQEType::XX, 0, 1}, {2, 1}}, - {{TQEType::XY, 0, 1}, {2, 1}}, {{TQEType::XZ, 0, 1}, {0, 1}}, - {{TQEType::YX, 0, 1}, {3, 1}}, {{TQEType::YY, 0, 1}, {3, 1}}, - {{TQEType::YZ, 0, 1}, {0, 1}}, {{TQEType::ZX, 0, 1}, {1, 1}}, - {{TQEType::ZY, 0, 1}, {1, 1}}, {{TQEType::ZZ, 0, 1}, {0, 1}}, - {{TQEType::XX, 2, 0}, {2, 0}}, {{TQEType::XY, 2, 0}, {2, 0}}, - {{TQEType::XZ, 2, 0}, {2, 0}}, {{TQEType::YX, 2, 0}, {2, 2}}, - {{TQEType::YY, 2, 0}, {2, 3}}, {{TQEType::YZ, 2, 0}, {2, 1}}, - {{TQEType::ZX, 2, 0}, {2, 2}}, {{TQEType::ZY, 2, 0}, {2, 3}}, - {{TQEType::ZZ, 2, 0}, {2, 1}}, {{TQEType::XX, 3, 0}, {3, 2}}, - {{TQEType::XY, 3, 0}, {3, 3}}, {{TQEType::XZ, 3, 0}, {3, 1}}, - {{TQEType::YX, 3, 0}, {3, 0}}, {{TQEType::YY, 3, 0}, {3, 0}}, - {{TQEType::YZ, 3, 0}, {3, 0}}, {{TQEType::ZX, 3, 0}, {3, 2}}, - {{TQEType::ZY, 3, 0}, {3, 3}}, {{TQEType::ZZ, 3, 0}, {3, 1}}, - {{TQEType::XX, 1, 0}, {1, 2}}, {{TQEType::XY, 1, 0}, {1, 3}}, - {{TQEType::XZ, 1, 0}, {1, 1}}, {{TQEType::YX, 1, 0}, {1, 2}}, - {{TQEType::YY, 1, 0}, {1, 3}}, {{TQEType::YZ, 1, 0}, {1, 1}}, - {{TQEType::ZX, 1, 0}, {1, 0}}, {{TQEType::ZY, 1, 0}, {1, 0}}, - {{TQEType::ZZ, 1, 0}, {1, 0}}, {{TQEType::XX, 0, 0}, {0, 0}}, - {{TQEType::XY, 0, 0}, {0, 0}}, {{TQEType::XZ, 0, 0}, {0, 0}}, - {{TQEType::YX, 0, 0}, {0, 0}}, {{TQEType::YY, 0, 0}, {0, 0}}, - {{TQEType::YZ, 0, 0}, {0, 0}}, {{TQEType::ZX, 0, 0}, {0, 0}}, - {{TQEType::ZY, 0, 0}, {0, 0}}, {{TQEType::ZZ, 0, 0}, {0, 0}}}; + std::pair, std::pair, hash_optype_pauli> + SQ_CLIFF_MAP = { + {{OpType::H, Pauli::X}, {Pauli::Z, true}}, + {{OpType::S, Pauli::X}, {Pauli::Y, false}}, + {{OpType::Sdg, Pauli::X}, {Pauli::Y, true}}, + {{OpType::V, Pauli::X}, {Pauli::X, true}}, + {{OpType::Vdg, Pauli::X}, {Pauli::X, true}}, + {{OpType::X, Pauli::X}, {Pauli::X, true}}, + {{OpType::Y, Pauli::X}, {Pauli::X, false}}, + {{OpType::Z, Pauli::X}, {Pauli::X, false}}, + {{OpType::H, Pauli::Y}, {Pauli::Y, false}}, + {{OpType::S, Pauli::Y}, {Pauli::X, true}}, + {{OpType::Sdg, Pauli::Y}, {Pauli::X, false}}, + {{OpType::V, Pauli::Y}, {Pauli::Z, false}}, + {{OpType::Vdg, Pauli::Y}, {Pauli::Z, true}}, + {{OpType::X, Pauli::Y}, {Pauli::Y, false}}, + {{OpType::Y, Pauli::Y}, {Pauli::Y, true}}, + {{OpType::Z, Pauli::Y}, {Pauli::Y, false}}, + {{OpType::H, Pauli::Z}, {Pauli::X, true}}, + {{OpType::S, Pauli::Z}, {Pauli::Z, true}}, + {{OpType::Sdg, Pauli::Z}, {Pauli::Z, true}}, + {{OpType::V, Pauli::Z}, {Pauli::Y, true}}, + {{OpType::Vdg, Pauli::Z}, {Pauli::Y, false}}, + {{OpType::X, Pauli::Z}, {Pauli::Z, false}}, + {{OpType::Y, Pauli::Z}, {Pauli::Z, false}}, + {{OpType::Z, Pauli::Z}, {Pauli::Z, true}}}; /** - * @brief Maps a pair of non-zero entires in a singlet support vector - * to a set of 4 TQE gates that will reduce one of them to 0 + * @brief Given TQE;P(0);Q(1), return P'(0), Q'(0), and sign k such that + * TQE;P(0);Q(1) = k* P'(0);Q'(1);TQE */ const static std::unordered_map< - std::pair, std::vector, hash_pair> - SINGLET_PAIR_REDUCTION_TQES = { - {{2, 2}, {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::ZX}}, - {{3, 2}, {TQEType::XX, TQEType::YY, TQEType::YZ, TQEType::ZX}}, - {{1, 2}, {TQEType::XX, TQEType::YX, TQEType::ZY, TQEType::ZZ}}, - {{2, 3}, {TQEType::XX, TQEType::XZ, TQEType::YY, TQEType::ZY}}, - {{3, 3}, {TQEType::XY, TQEType::YX, TQEType::YZ, TQEType::ZY}}, - {{1, 3}, {TQEType::XY, TQEType::YY, TQEType::ZX, TQEType::ZZ}}, - {{2, 1}, {TQEType::XX, TQEType::XY, TQEType::YZ, TQEType::ZZ}}, - {{3, 1}, {TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZZ}}, - {{1, 1}, {TQEType::XZ, TQEType::YZ, TQEType::ZX, TQEType::ZY}}}; + std::tuple, std::pair, hash_triple> + TQE_PAULI_MAP = { + {{TQEType::XX, Pauli::X, Pauli::X}, {Pauli::X, Pauli::X, true}}, + {{TQEType::XY, Pauli::X, Pauli::X}, {Pauli::I, Pauli::X, true}}, + {{TQEType::XZ, Pauli::X, Pauli::X}, {Pauli::I, Pauli::X, true}}, + {{TQEType::YX, Pauli::X, Pauli::X}, {Pauli::X, Pauli::I, true}}, + {{TQEType::YY, Pauli::X, Pauli::X}, {Pauli::Z, Pauli::Z, true}}, + {{TQEType::YZ, Pauli::X, Pauli::X}, {Pauli::Z, Pauli::Y, false}}, + {{TQEType::ZX, Pauli::X, Pauli::X}, {Pauli::X, Pauli::I, true}}, + {{TQEType::ZY, Pauli::X, Pauli::X}, {Pauli::Y, Pauli::Z, false}}, + {{TQEType::ZZ, Pauli::X, Pauli::X}, {Pauli::Y, Pauli::Y, true}}, + {{TQEType::XX, Pauli::X, Pauli::Y}, {Pauli::I, Pauli::Y, true}}, + {{TQEType::XY, Pauli::X, Pauli::Y}, {Pauli::X, Pauli::Y, true}}, + {{TQEType::XZ, Pauli::X, Pauli::Y}, {Pauli::I, Pauli::Y, true}}, + {{TQEType::YX, Pauli::X, Pauli::Y}, {Pauli::Z, Pauli::Z, false}}, + {{TQEType::YY, Pauli::X, Pauli::Y}, {Pauli::X, Pauli::I, true}}, + {{TQEType::YZ, Pauli::X, Pauli::Y}, {Pauli::Z, Pauli::X, true}}, + {{TQEType::ZX, Pauli::X, Pauli::Y}, {Pauli::Y, Pauli::Z, true}}, + {{TQEType::ZY, Pauli::X, Pauli::Y}, {Pauli::X, Pauli::I, true}}, + {{TQEType::ZZ, Pauli::X, Pauli::Y}, {Pauli::Y, Pauli::X, false}}, + {{TQEType::XX, Pauli::X, Pauli::Z}, {Pauli::I, Pauli::Z, true}}, + {{TQEType::XY, Pauli::X, Pauli::Z}, {Pauli::I, Pauli::Z, true}}, + {{TQEType::XZ, Pauli::X, Pauli::Z}, {Pauli::X, Pauli::Z, true}}, + {{TQEType::YX, Pauli::X, Pauli::Z}, {Pauli::Z, Pauli::Y, true}}, + {{TQEType::YY, Pauli::X, Pauli::Z}, {Pauli::Z, Pauli::X, false}}, + {{TQEType::YZ, Pauli::X, Pauli::Z}, {Pauli::X, Pauli::I, true}}, + {{TQEType::ZX, Pauli::X, Pauli::Z}, {Pauli::Y, Pauli::Y, false}}, + {{TQEType::ZY, Pauli::X, Pauli::Z}, {Pauli::Y, Pauli::X, true}}, + {{TQEType::ZZ, Pauli::X, Pauli::Z}, {Pauli::X, Pauli::I, true}}, + {{TQEType::XX, Pauli::X, Pauli::I}, {Pauli::X, Pauli::I, true}}, + {{TQEType::XY, Pauli::X, Pauli::I}, {Pauli::X, Pauli::I, true}}, + {{TQEType::XZ, Pauli::X, Pauli::I}, {Pauli::X, Pauli::I, true}}, + {{TQEType::YX, Pauli::X, Pauli::I}, {Pauli::X, Pauli::X, true}}, + {{TQEType::YY, Pauli::X, Pauli::I}, {Pauli::X, Pauli::Y, true}}, + {{TQEType::YZ, Pauli::X, Pauli::I}, {Pauli::X, Pauli::Z, true}}, + {{TQEType::ZX, Pauli::X, Pauli::I}, {Pauli::X, Pauli::X, true}}, + {{TQEType::ZY, Pauli::X, Pauli::I}, {Pauli::X, Pauli::Y, true}}, + {{TQEType::ZZ, Pauli::X, Pauli::I}, {Pauli::X, Pauli::Z, true}}, + {{TQEType::XX, Pauli::Y, Pauli::X}, {Pauli::Y, Pauli::I, true}}, + {{TQEType::XY, Pauli::Y, Pauli::X}, {Pauli::Z, Pauli::Z, false}}, + {{TQEType::XZ, Pauli::Y, Pauli::X}, {Pauli::Z, Pauli::Y, true}}, + {{TQEType::YX, Pauli::Y, Pauli::X}, {Pauli::Y, Pauli::X, true}}, + {{TQEType::YY, Pauli::Y, Pauli::X}, {Pauli::I, Pauli::X, true}}, + {{TQEType::YZ, Pauli::Y, Pauli::X}, {Pauli::I, Pauli::X, true}}, + {{TQEType::ZX, Pauli::Y, Pauli::X}, {Pauli::Y, Pauli::I, true}}, + {{TQEType::ZY, Pauli::Y, Pauli::X}, {Pauli::X, Pauli::Z, true}}, + {{TQEType::ZZ, Pauli::Y, Pauli::X}, {Pauli::X, Pauli::Y, false}}, + {{TQEType::XX, Pauli::Y, Pauli::Y}, {Pauli::Z, Pauli::Z, true}}, + {{TQEType::XY, Pauli::Y, Pauli::Y}, {Pauli::Y, Pauli::I, true}}, + {{TQEType::XZ, Pauli::Y, Pauli::Y}, {Pauli::Z, Pauli::X, false}}, + {{TQEType::YX, Pauli::Y, Pauli::Y}, {Pauli::I, Pauli::Y, true}}, + {{TQEType::YY, Pauli::Y, Pauli::Y}, {Pauli::Y, Pauli::Y, true}}, + {{TQEType::YZ, Pauli::Y, Pauli::Y}, {Pauli::I, Pauli::Y, true}}, + {{TQEType::ZX, Pauli::Y, Pauli::Y}, {Pauli::X, Pauli::Z, false}}, + {{TQEType::ZY, Pauli::Y, Pauli::Y}, {Pauli::Y, Pauli::I, true}}, + {{TQEType::ZZ, Pauli::Y, Pauli::Y}, {Pauli::X, Pauli::X, true}}, + {{TQEType::XX, Pauli::Y, Pauli::Z}, {Pauli::Z, Pauli::Y, false}}, + {{TQEType::XY, Pauli::Y, Pauli::Z}, {Pauli::Z, Pauli::X, true}}, + {{TQEType::XZ, Pauli::Y, Pauli::Z}, {Pauli::Y, Pauli::I, true}}, + {{TQEType::YX, Pauli::Y, Pauli::Z}, {Pauli::I, Pauli::Z, true}}, + {{TQEType::YY, Pauli::Y, Pauli::Z}, {Pauli::I, Pauli::Z, true}}, + {{TQEType::YZ, Pauli::Y, Pauli::Z}, {Pauli::Y, Pauli::Z, true}}, + {{TQEType::ZX, Pauli::Y, Pauli::Z}, {Pauli::X, Pauli::Y, true}}, + {{TQEType::ZY, Pauli::Y, Pauli::Z}, {Pauli::X, Pauli::X, false}}, + {{TQEType::ZZ, Pauli::Y, Pauli::Z}, {Pauli::Y, Pauli::I, true}}, + {{TQEType::XX, Pauli::Y, Pauli::I}, {Pauli::Y, Pauli::X, true}}, + {{TQEType::XY, Pauli::Y, Pauli::I}, {Pauli::Y, Pauli::Y, true}}, + {{TQEType::XZ, Pauli::Y, Pauli::I}, {Pauli::Y, Pauli::Z, true}}, + {{TQEType::YX, Pauli::Y, Pauli::I}, {Pauli::Y, Pauli::I, true}}, + {{TQEType::YY, Pauli::Y, Pauli::I}, {Pauli::Y, Pauli::I, true}}, + {{TQEType::YZ, Pauli::Y, Pauli::I}, {Pauli::Y, Pauli::I, true}}, + {{TQEType::ZX, Pauli::Y, Pauli::I}, {Pauli::Y, Pauli::X, true}}, + {{TQEType::ZY, Pauli::Y, Pauli::I}, {Pauli::Y, Pauli::Y, true}}, + {{TQEType::ZZ, Pauli::Y, Pauli::I}, {Pauli::Y, Pauli::Z, true}}, + {{TQEType::XX, Pauli::Z, Pauli::X}, {Pauli::Z, Pauli::I, true}}, + {{TQEType::XY, Pauli::Z, Pauli::X}, {Pauli::Y, Pauli::Z, true}}, + {{TQEType::XZ, Pauli::Z, Pauli::X}, {Pauli::Y, Pauli::Y, false}}, + {{TQEType::YX, Pauli::Z, Pauli::X}, {Pauli::Z, Pauli::I, true}}, + {{TQEType::YY, Pauli::Z, Pauli::X}, {Pauli::X, Pauli::Z, false}}, + {{TQEType::YZ, Pauli::Z, Pauli::X}, {Pauli::X, Pauli::Y, true}}, + {{TQEType::ZX, Pauli::Z, Pauli::X}, {Pauli::Z, Pauli::X, true}}, + {{TQEType::ZY, Pauli::Z, Pauli::X}, {Pauli::I, Pauli::X, true}}, + {{TQEType::ZZ, Pauli::Z, Pauli::X}, {Pauli::I, Pauli::X, true}}, + {{TQEType::XX, Pauli::Z, Pauli::Y}, {Pauli::Y, Pauli::Z, false}}, + {{TQEType::XY, Pauli::Z, Pauli::Y}, {Pauli::Z, Pauli::I, true}}, + {{TQEType::XZ, Pauli::Z, Pauli::Y}, {Pauli::Y, Pauli::X, true}}, + {{TQEType::YX, Pauli::Z, Pauli::Y}, {Pauli::X, Pauli::Z, true}}, + {{TQEType::YY, Pauli::Z, Pauli::Y}, {Pauli::Z, Pauli::I, true}}, + {{TQEType::YZ, Pauli::Z, Pauli::Y}, {Pauli::X, Pauli::X, false}}, + {{TQEType::ZX, Pauli::Z, Pauli::Y}, {Pauli::I, Pauli::Y, true}}, + {{TQEType::ZY, Pauli::Z, Pauli::Y}, {Pauli::Z, Pauli::Y, true}}, + {{TQEType::ZZ, Pauli::Z, Pauli::Y}, {Pauli::I, Pauli::Y, true}}, + {{TQEType::XX, Pauli::Z, Pauli::Z}, {Pauli::Y, Pauli::Y, true}}, + {{TQEType::XY, Pauli::Z, Pauli::Z}, {Pauli::Y, Pauli::X, false}}, + {{TQEType::XZ, Pauli::Z, Pauli::Z}, {Pauli::Z, Pauli::I, true}}, + {{TQEType::YX, Pauli::Z, Pauli::Z}, {Pauli::X, Pauli::Y, false}}, + {{TQEType::YY, Pauli::Z, Pauli::Z}, {Pauli::X, Pauli::X, true}}, + {{TQEType::YZ, Pauli::Z, Pauli::Z}, {Pauli::Z, Pauli::I, true}}, + {{TQEType::ZX, Pauli::Z, Pauli::Z}, {Pauli::I, Pauli::Z, true}}, + {{TQEType::ZY, Pauli::Z, Pauli::Z}, {Pauli::I, Pauli::Z, true}}, + {{TQEType::ZZ, Pauli::Z, Pauli::Z}, {Pauli::Z, Pauli::Z, true}}, + {{TQEType::XX, Pauli::Z, Pauli::I}, {Pauli::Z, Pauli::X, true}}, + {{TQEType::XY, Pauli::Z, Pauli::I}, {Pauli::Z, Pauli::Y, true}}, + {{TQEType::XZ, Pauli::Z, Pauli::I}, {Pauli::Z, Pauli::Z, true}}, + {{TQEType::YX, Pauli::Z, Pauli::I}, {Pauli::Z, Pauli::X, true}}, + {{TQEType::YY, Pauli::Z, Pauli::I}, {Pauli::Z, Pauli::Y, true}}, + {{TQEType::YZ, Pauli::Z, Pauli::I}, {Pauli::Z, Pauli::Z, true}}, + {{TQEType::ZX, Pauli::Z, Pauli::I}, {Pauli::Z, Pauli::I, true}}, + {{TQEType::ZY, Pauli::Z, Pauli::I}, {Pauli::Z, Pauli::I, true}}, + {{TQEType::ZZ, Pauli::Z, Pauli::I}, {Pauli::Z, Pauli::I, true}}, + {{TQEType::XX, Pauli::I, Pauli::X}, {Pauli::I, Pauli::X, true}}, + {{TQEType::XY, Pauli::I, Pauli::X}, {Pauli::X, Pauli::X, true}}, + {{TQEType::XZ, Pauli::I, Pauli::X}, {Pauli::X, Pauli::X, true}}, + {{TQEType::YX, Pauli::I, Pauli::X}, {Pauli::I, Pauli::X, true}}, + {{TQEType::YY, Pauli::I, Pauli::X}, {Pauli::Y, Pauli::X, true}}, + {{TQEType::YZ, Pauli::I, Pauli::X}, {Pauli::Y, Pauli::X, true}}, + {{TQEType::ZX, Pauli::I, Pauli::X}, {Pauli::I, Pauli::X, true}}, + {{TQEType::ZY, Pauli::I, Pauli::X}, {Pauli::Z, Pauli::X, true}}, + {{TQEType::ZZ, Pauli::I, Pauli::X}, {Pauli::Z, Pauli::X, true}}, + {{TQEType::XX, Pauli::I, Pauli::Y}, {Pauli::X, Pauli::Y, true}}, + {{TQEType::XY, Pauli::I, Pauli::Y}, {Pauli::I, Pauli::Y, true}}, + {{TQEType::XZ, Pauli::I, Pauli::Y}, {Pauli::X, Pauli::Y, true}}, + {{TQEType::YX, Pauli::I, Pauli::Y}, {Pauli::Y, Pauli::Y, true}}, + {{TQEType::YY, Pauli::I, Pauli::Y}, {Pauli::I, Pauli::Y, true}}, + {{TQEType::YZ, Pauli::I, Pauli::Y}, {Pauli::Y, Pauli::Y, true}}, + {{TQEType::ZX, Pauli::I, Pauli::Y}, {Pauli::Z, Pauli::Y, true}}, + {{TQEType::ZY, Pauli::I, Pauli::Y}, {Pauli::I, Pauli::Y, true}}, + {{TQEType::ZZ, Pauli::I, Pauli::Y}, {Pauli::Z, Pauli::Y, true}}, + {{TQEType::XX, Pauli::I, Pauli::Z}, {Pauli::X, Pauli::Z, true}}, + {{TQEType::XY, Pauli::I, Pauli::Z}, {Pauli::X, Pauli::Z, true}}, + {{TQEType::XZ, Pauli::I, Pauli::Z}, {Pauli::I, Pauli::Z, true}}, + {{TQEType::YX, Pauli::I, Pauli::Z}, {Pauli::Y, Pauli::Z, true}}, + {{TQEType::YY, Pauli::I, Pauli::Z}, {Pauli::Y, Pauli::Z, true}}, + {{TQEType::YZ, Pauli::I, Pauli::Z}, {Pauli::I, Pauli::Z, true}}, + {{TQEType::ZX, Pauli::I, Pauli::Z}, {Pauli::Z, Pauli::Z, true}}, + {{TQEType::ZY, Pauli::I, Pauli::Z}, {Pauli::Z, Pauli::Z, true}}, + {{TQEType::ZZ, Pauli::I, Pauli::Z}, {Pauli::I, Pauli::Z, true}}, + {{TQEType::XX, Pauli::I, Pauli::I}, {Pauli::I, Pauli::I, true}}, + {{TQEType::XY, Pauli::I, Pauli::I}, {Pauli::I, Pauli::I, true}}, + {{TQEType::XZ, Pauli::I, Pauli::I}, {Pauli::I, Pauli::I, true}}, + {{TQEType::YX, Pauli::I, Pauli::I}, {Pauli::I, Pauli::I, true}}, + {{TQEType::YY, Pauli::I, Pauli::I}, {Pauli::I, Pauli::I, true}}, + {{TQEType::YZ, Pauli::I, Pauli::I}, {Pauli::I, Pauli::I, true}}, + {{TQEType::ZX, Pauli::I, Pauli::I}, {Pauli::I, Pauli::I, true}}, + {{TQEType::ZY, Pauli::I, Pauli::I}, {Pauli::I, Pauli::I, true}}, + {{TQEType::ZZ, Pauli::I, Pauli::I}, {Pauli::I, Pauli::I, true}}}; /** - * @brief Transform a pair of entries in a factor support vector using a TQE + * @brief Given non-identities P(0), Q(0), + * return a list of TQEs, T, such that t;P(0);Q(1) = P'(0);Q'(1);t, + * for all t in T, and one of P'(0), Q'(1) is identity. */ const static std::unordered_map< - std::tuple, std::pair, - hash_tuple> - FACTOR_PAIR_TRANSFORMATION_MAP = { - {{TQEType::XX, 7, 5}, {7, 4}}, {{TQEType::XY, 7, 5}, {2, 6}}, - {{TQEType::XZ, 7, 5}, {2, 7}}, {{TQEType::YX, 7, 5}, {7, 1}}, - {{TQEType::YY, 7, 5}, {8, 9}}, {{TQEType::YZ, 7, 5}, {8, 13}}, - {{TQEType::ZX, 7, 5}, {7, 0}}, {{TQEType::ZY, 7, 5}, {13, 10}}, - {{TQEType::ZZ, 7, 5}, {13, 15}}, {{TQEType::XX, 6, 5}, {6, 4}}, - {{TQEType::XY, 6, 5}, {3, 6}}, {{TQEType::XZ, 6, 5}, {3, 7}}, - {{TQEType::YX, 6, 5}, {6, 0}}, {{TQEType::YY, 6, 5}, {9, 10}}, - {{TQEType::YZ, 6, 5}, {9, 15}}, {{TQEType::ZX, 6, 5}, {6, 1}}, - {{TQEType::ZY, 6, 5}, {12, 9}}, {{TQEType::ZZ, 6, 5}, {12, 13}}, - {{TQEType::XX, 5, 7}, {4, 7}}, {{TQEType::XY, 5, 7}, {1, 7}}, - {{TQEType::XZ, 5, 7}, {0, 7}}, {{TQEType::YX, 5, 7}, {6, 2}}, - {{TQEType::YY, 5, 7}, {9, 8}}, {{TQEType::YZ, 5, 7}, {10, 13}}, - {{TQEType::ZX, 5, 7}, {7, 2}}, {{TQEType::ZY, 5, 7}, {13, 8}}, - {{TQEType::ZZ, 5, 7}, {15, 13}}, {{TQEType::XX, 4, 7}, {5, 7}}, - {{TQEType::XY, 4, 7}, {0, 7}}, {{TQEType::XZ, 4, 7}, {1, 7}}, - {{TQEType::YX, 4, 7}, {7, 3}}, {{TQEType::YY, 4, 7}, {8, 11}}, - {{TQEType::YZ, 4, 7}, {11, 15}}, {{TQEType::ZX, 4, 7}, {6, 3}}, - {{TQEType::ZY, 4, 7}, {12, 11}}, {{TQEType::ZZ, 4, 7}, {14, 15}}, - {{TQEType::XX, 5, 6}, {4, 6}}, {{TQEType::XY, 5, 6}, {0, 6}}, - {{TQEType::XZ, 5, 6}, {1, 6}}, {{TQEType::YX, 5, 6}, {6, 3}}, - {{TQEType::YY, 5, 6}, {10, 9}}, {{TQEType::YZ, 5, 6}, {9, 12}}, - {{TQEType::ZX, 5, 6}, {7, 3}}, {{TQEType::ZY, 5, 6}, {15, 9}}, - {{TQEType::ZZ, 5, 6}, {13, 12}}, {{TQEType::XX, 4, 6}, {5, 6}}, - {{TQEType::XY, 4, 6}, {1, 6}}, {{TQEType::XZ, 4, 6}, {0, 6}}, - {{TQEType::YX, 4, 6}, {7, 2}}, {{TQEType::YY, 4, 6}, {11, 10}}, - {{TQEType::YZ, 4, 6}, {8, 14}}, {{TQEType::ZX, 4, 6}, {6, 2}}, - {{TQEType::ZY, 4, 6}, {14, 10}}, {{TQEType::ZZ, 4, 6}, {12, 14}}, - {{TQEType::XX, 7, 4}, {7, 5}}, {{TQEType::XY, 7, 4}, {3, 7}}, - {{TQEType::XZ, 7, 4}, {3, 6}}, {{TQEType::YX, 7, 4}, {7, 0}}, - {{TQEType::YY, 7, 4}, {11, 8}}, {{TQEType::YZ, 7, 4}, {11, 12}}, - {{TQEType::ZX, 7, 4}, {7, 1}}, {{TQEType::ZY, 7, 4}, {15, 11}}, - {{TQEType::ZZ, 7, 4}, {15, 14}}, {{TQEType::XX, 6, 4}, {6, 5}}, - {{TQEType::XY, 6, 4}, {2, 7}}, {{TQEType::XZ, 6, 4}, {2, 6}}, - {{TQEType::YX, 6, 4}, {6, 1}}, {{TQEType::YY, 6, 4}, {10, 11}}, - {{TQEType::YZ, 6, 4}, {10, 14}}, {{TQEType::ZX, 6, 4}, {6, 0}}, - {{TQEType::ZY, 6, 4}, {14, 8}}, {{TQEType::ZZ, 6, 4}, {14, 12}}, - {{TQEType::XX, 5, 5}, {5, 5}}, {{TQEType::XY, 5, 5}, {0, 5}}, - {{TQEType::XZ, 5, 5}, {0, 5}}, {{TQEType::YX, 5, 5}, {5, 0}}, - {{TQEType::YY, 5, 5}, {10, 10}}, {{TQEType::YZ, 5, 5}, {10, 15}}, - {{TQEType::ZX, 5, 5}, {5, 0}}, {{TQEType::ZY, 5, 5}, {15, 10}}, - {{TQEType::ZZ, 5, 5}, {15, 15}}, {{TQEType::XX, 4, 5}, {4, 5}}, - {{TQEType::XY, 4, 5}, {1, 5}}, {{TQEType::XZ, 4, 5}, {1, 5}}, - {{TQEType::YX, 4, 5}, {4, 1}}, {{TQEType::YY, 4, 5}, {11, 9}}, - {{TQEType::YZ, 4, 5}, {11, 13}}, {{TQEType::ZX, 4, 5}, {4, 1}}, - {{TQEType::ZY, 4, 5}, {14, 9}}, {{TQEType::ZZ, 4, 5}, {14, 13}}, - {{TQEType::XX, 7, 7}, {6, 6}}, {{TQEType::XY, 7, 7}, {3, 4}}, - {{TQEType::XZ, 7, 7}, {2, 5}}, {{TQEType::YX, 7, 7}, {4, 3}}, - {{TQEType::YY, 7, 7}, {11, 11}}, {{TQEType::YZ, 7, 7}, {8, 15}}, - {{TQEType::ZX, 7, 7}, {5, 2}}, {{TQEType::ZY, 7, 7}, {15, 8}}, - {{TQEType::ZZ, 7, 7}, {13, 13}}, {{TQEType::XX, 6, 7}, {7, 6}}, - {{TQEType::XY, 6, 7}, {2, 4}}, {{TQEType::XZ, 6, 7}, {3, 5}}, - {{TQEType::YX, 6, 7}, {5, 2}}, {{TQEType::YY, 6, 7}, {10, 8}}, - {{TQEType::YZ, 6, 7}, {9, 13}}, {{TQEType::ZX, 6, 7}, {4, 3}}, - {{TQEType::ZY, 6, 7}, {14, 11}}, {{TQEType::ZZ, 6, 7}, {12, 15}}, - {{TQEType::XX, 7, 6}, {6, 7}}, {{TQEType::XY, 7, 6}, {2, 5}}, - {{TQEType::XZ, 7, 6}, {3, 4}}, {{TQEType::YX, 7, 6}, {4, 2}}, - {{TQEType::YY, 7, 6}, {8, 10}}, {{TQEType::YZ, 7, 6}, {11, 14}}, - {{TQEType::ZX, 7, 6}, {5, 3}}, {{TQEType::ZY, 7, 6}, {13, 9}}, - {{TQEType::ZZ, 7, 6}, {15, 12}}, {{TQEType::XX, 6, 6}, {7, 7}}, - {{TQEType::XY, 6, 6}, {3, 5}}, {{TQEType::XZ, 6, 6}, {2, 4}}, - {{TQEType::YX, 6, 6}, {5, 3}}, {{TQEType::YY, 6, 6}, {9, 9}}, - {{TQEType::YZ, 6, 6}, {10, 12}}, {{TQEType::ZX, 6, 6}, {4, 2}}, - {{TQEType::ZY, 6, 6}, {12, 10}}, {{TQEType::ZZ, 6, 6}, {14, 14}}, - {{TQEType::XX, 5, 4}, {5, 4}}, {{TQEType::XY, 5, 4}, {1, 4}}, - {{TQEType::XZ, 5, 4}, {1, 4}}, {{TQEType::YX, 5, 4}, {5, 1}}, - {{TQEType::YY, 5, 4}, {9, 11}}, {{TQEType::YZ, 5, 4}, {9, 14}}, - {{TQEType::ZX, 5, 4}, {5, 1}}, {{TQEType::ZY, 5, 4}, {13, 11}}, - {{TQEType::ZZ, 5, 4}, {13, 14}}, {{TQEType::XX, 4, 4}, {4, 4}}, - {{TQEType::XY, 4, 4}, {0, 4}}, {{TQEType::XZ, 4, 4}, {0, 4}}, - {{TQEType::YX, 4, 4}, {4, 0}}, {{TQEType::YY, 4, 4}, {8, 8}}, - {{TQEType::YZ, 4, 4}, {8, 12}}, {{TQEType::ZX, 4, 4}, {4, 0}}, - {{TQEType::ZY, 4, 4}, {12, 8}}, {{TQEType::ZZ, 4, 4}, {12, 12}}, - {{TQEType::XX, 13, 5}, {13, 1}}, {{TQEType::XY, 13, 5}, {8, 9}}, - {{TQEType::XZ, 13, 5}, {8, 13}}, {{TQEType::YX, 13, 5}, {13, 4}}, - {{TQEType::YY, 13, 5}, {2, 6}}, {{TQEType::YZ, 13, 5}, {2, 7}}, - {{TQEType::ZX, 13, 5}, {13, 0}}, {{TQEType::ZY, 13, 5}, {7, 10}}, - {{TQEType::ZZ, 13, 5}, {7, 15}}, {{TQEType::XX, 14, 5}, {14, 0}}, - {{TQEType::XY, 14, 5}, {11, 10}}, {{TQEType::XZ, 14, 5}, {11, 15}}, - {{TQEType::YX, 14, 5}, {14, 4}}, {{TQEType::YY, 14, 5}, {1, 6}}, - {{TQEType::YZ, 14, 5}, {1, 7}}, {{TQEType::ZX, 14, 5}, {14, 1}}, - {{TQEType::ZY, 14, 5}, {4, 9}}, {{TQEType::ZZ, 14, 5}, {4, 13}}, - {{TQEType::XX, 15, 7}, {14, 2}}, {{TQEType::XY, 15, 7}, {11, 8}}, - {{TQEType::XZ, 15, 7}, {10, 13}}, {{TQEType::YX, 15, 7}, {12, 7}}, - {{TQEType::YY, 15, 7}, {3, 7}}, {{TQEType::YZ, 15, 7}, {0, 7}}, - {{TQEType::ZX, 15, 7}, {13, 2}}, {{TQEType::ZY, 15, 7}, {7, 8}}, - {{TQEType::ZZ, 15, 7}, {5, 13}}, {{TQEType::XX, 12, 7}, {13, 3}}, - {{TQEType::XY, 12, 7}, {8, 11}}, {{TQEType::XZ, 12, 7}, {9, 15}}, - {{TQEType::YX, 12, 7}, {15, 7}}, {{TQEType::YY, 12, 7}, {0, 7}}, - {{TQEType::YZ, 12, 7}, {3, 7}}, {{TQEType::ZX, 12, 7}, {14, 3}}, - {{TQEType::ZY, 12, 7}, {4, 11}}, {{TQEType::ZZ, 12, 7}, {6, 15}}, - {{TQEType::XX, 15, 6}, {14, 3}}, {{TQEType::XY, 15, 6}, {10, 9}}, - {{TQEType::XZ, 15, 6}, {11, 12}}, {{TQEType::YX, 15, 6}, {12, 6}}, - {{TQEType::YY, 15, 6}, {0, 6}}, {{TQEType::YZ, 15, 6}, {3, 6}}, - {{TQEType::ZX, 15, 6}, {13, 3}}, {{TQEType::ZY, 15, 6}, {5, 9}}, - {{TQEType::ZZ, 15, 6}, {7, 12}}, {{TQEType::XX, 12, 6}, {13, 2}}, - {{TQEType::XY, 12, 6}, {9, 10}}, {{TQEType::XZ, 12, 6}, {8, 14}}, - {{TQEType::YX, 12, 6}, {15, 6}}, {{TQEType::YY, 12, 6}, {3, 6}}, - {{TQEType::YZ, 12, 6}, {0, 6}}, {{TQEType::ZX, 12, 6}, {14, 2}}, - {{TQEType::ZY, 12, 6}, {6, 10}}, {{TQEType::ZZ, 12, 6}, {4, 14}}, - {{TQEType::XX, 13, 4}, {13, 0}}, {{TQEType::XY, 13, 4}, {9, 8}}, - {{TQEType::XZ, 13, 4}, {9, 12}}, {{TQEType::YX, 13, 4}, {13, 5}}, - {{TQEType::YY, 13, 4}, {1, 7}}, {{TQEType::YZ, 13, 4}, {1, 6}}, - {{TQEType::ZX, 13, 4}, {13, 1}}, {{TQEType::ZY, 13, 4}, {5, 11}}, - {{TQEType::ZZ, 13, 4}, {5, 14}}, {{TQEType::XX, 14, 4}, {14, 1}}, - {{TQEType::XY, 14, 4}, {10, 11}}, {{TQEType::XZ, 14, 4}, {10, 14}}, - {{TQEType::YX, 14, 4}, {14, 5}}, {{TQEType::YY, 14, 4}, {2, 7}}, - {{TQEType::YZ, 14, 4}, {2, 6}}, {{TQEType::ZX, 14, 4}, {14, 0}}, - {{TQEType::ZY, 14, 4}, {6, 8}}, {{TQEType::ZZ, 14, 4}, {6, 12}}, - {{TQEType::XX, 15, 5}, {15, 0}}, {{TQEType::XY, 15, 5}, {10, 10}}, - {{TQEType::XZ, 15, 5}, {10, 15}}, {{TQEType::YX, 15, 5}, {15, 5}}, - {{TQEType::YY, 15, 5}, {0, 5}}, {{TQEType::YZ, 15, 5}, {0, 5}}, - {{TQEType::ZX, 15, 5}, {15, 0}}, {{TQEType::ZY, 15, 5}, {5, 10}}, - {{TQEType::ZZ, 15, 5}, {5, 15}}, {{TQEType::XX, 12, 5}, {12, 1}}, - {{TQEType::XY, 12, 5}, {9, 9}}, {{TQEType::XZ, 12, 5}, {9, 13}}, - {{TQEType::YX, 12, 5}, {12, 5}}, {{TQEType::YY, 12, 5}, {3, 5}}, - {{TQEType::YZ, 12, 5}, {3, 5}}, {{TQEType::ZX, 12, 5}, {12, 1}}, - {{TQEType::ZY, 12, 5}, {6, 9}}, {{TQEType::ZZ, 12, 5}, {6, 13}}, - {{TQEType::XX, 13, 7}, {12, 3}}, {{TQEType::XY, 13, 7}, {9, 11}}, - {{TQEType::XZ, 13, 7}, {8, 15}}, {{TQEType::YX, 13, 7}, {14, 6}}, - {{TQEType::YY, 13, 7}, {1, 4}}, {{TQEType::YZ, 13, 7}, {2, 5}}, - {{TQEType::ZX, 13, 7}, {15, 2}}, {{TQEType::ZY, 13, 7}, {5, 8}}, - {{TQEType::ZZ, 13, 7}, {7, 13}}, {{TQEType::XX, 14, 7}, {15, 2}}, - {{TQEType::XY, 14, 7}, {10, 8}}, {{TQEType::XZ, 14, 7}, {11, 13}}, - {{TQEType::YX, 14, 7}, {13, 6}}, {{TQEType::YY, 14, 7}, {2, 4}}, - {{TQEType::YZ, 14, 7}, {1, 5}}, {{TQEType::ZX, 14, 7}, {12, 3}}, - {{TQEType::ZY, 14, 7}, {6, 11}}, {{TQEType::ZZ, 14, 7}, {4, 15}}, - {{TQEType::XX, 13, 6}, {12, 2}}, {{TQEType::XY, 13, 6}, {8, 10}}, - {{TQEType::XZ, 13, 6}, {9, 14}}, {{TQEType::YX, 13, 6}, {14, 7}}, - {{TQEType::YY, 13, 6}, {2, 5}}, {{TQEType::YZ, 13, 6}, {1, 4}}, - {{TQEType::ZX, 13, 6}, {15, 3}}, {{TQEType::ZY, 13, 6}, {7, 9}}, - {{TQEType::ZZ, 13, 6}, {5, 12}}, {{TQEType::XX, 14, 6}, {15, 3}}, - {{TQEType::XY, 14, 6}, {11, 9}}, {{TQEType::XZ, 14, 6}, {10, 12}}, - {{TQEType::YX, 14, 6}, {13, 7}}, {{TQEType::YY, 14, 6}, {1, 5}}, - {{TQEType::YZ, 14, 6}, {2, 4}}, {{TQEType::ZX, 14, 6}, {12, 2}}, - {{TQEType::ZY, 14, 6}, {4, 10}}, {{TQEType::ZZ, 14, 6}, {6, 14}}, - {{TQEType::XX, 15, 4}, {15, 1}}, {{TQEType::XY, 15, 4}, {11, 11}}, - {{TQEType::XZ, 15, 4}, {11, 14}}, {{TQEType::YX, 15, 4}, {15, 4}}, - {{TQEType::YY, 15, 4}, {3, 4}}, {{TQEType::YZ, 15, 4}, {3, 4}}, - {{TQEType::ZX, 15, 4}, {15, 1}}, {{TQEType::ZY, 15, 4}, {7, 11}}, - {{TQEType::ZZ, 15, 4}, {7, 14}}, {{TQEType::XX, 12, 4}, {12, 0}}, - {{TQEType::XY, 12, 4}, {8, 8}}, {{TQEType::XZ, 12, 4}, {8, 12}}, - {{TQEType::YX, 12, 4}, {12, 4}}, {{TQEType::YY, 12, 4}, {0, 4}}, - {{TQEType::YZ, 12, 4}, {0, 4}}, {{TQEType::ZX, 12, 4}, {12, 0}}, - {{TQEType::ZY, 12, 4}, {4, 8}}, {{TQEType::ZZ, 12, 4}, {4, 12}}, - {{TQEType::XX, 9, 5}, {9, 1}}, {{TQEType::XY, 9, 5}, {12, 9}}, - {{TQEType::XZ, 9, 5}, {12, 13}}, {{TQEType::YX, 9, 5}, {9, 0}}, - {{TQEType::YY, 9, 5}, {6, 10}}, {{TQEType::YZ, 9, 5}, {6, 15}}, - {{TQEType::ZX, 9, 5}, {9, 4}}, {{TQEType::ZY, 9, 5}, {3, 6}}, - {{TQEType::ZZ, 9, 5}, {3, 7}}, {{TQEType::XX, 11, 5}, {11, 0}}, - {{TQEType::XY, 11, 5}, {14, 10}}, {{TQEType::XZ, 11, 5}, {14, 15}}, - {{TQEType::YX, 11, 5}, {11, 1}}, {{TQEType::YY, 11, 5}, {4, 9}}, - {{TQEType::YZ, 11, 5}, {4, 13}}, {{TQEType::ZX, 11, 5}, {11, 4}}, - {{TQEType::ZY, 11, 5}, {1, 6}}, {{TQEType::ZZ, 11, 5}, {1, 7}}, - {{TQEType::XX, 10, 7}, {11, 2}}, {{TQEType::XY, 10, 7}, {14, 8}}, - {{TQEType::XZ, 10, 7}, {15, 13}}, {{TQEType::YX, 10, 7}, {9, 2}}, - {{TQEType::YY, 10, 7}, {6, 8}}, {{TQEType::YZ, 10, 7}, {5, 13}}, - {{TQEType::ZX, 10, 7}, {8, 7}}, {{TQEType::ZY, 10, 7}, {2, 7}}, - {{TQEType::ZZ, 10, 7}, {0, 7}}, {{TQEType::XX, 8, 7}, {9, 3}}, - {{TQEType::XY, 8, 7}, {12, 11}}, {{TQEType::XZ, 8, 7}, {13, 15}}, - {{TQEType::YX, 8, 7}, {11, 3}}, {{TQEType::YY, 8, 7}, {4, 11}}, - {{TQEType::YZ, 8, 7}, {7, 15}}, {{TQEType::ZX, 8, 7}, {10, 7}}, - {{TQEType::ZY, 8, 7}, {0, 7}}, {{TQEType::ZZ, 8, 7}, {2, 7}}, - {{TQEType::XX, 10, 6}, {11, 3}}, {{TQEType::XY, 10, 6}, {15, 9}}, - {{TQEType::XZ, 10, 6}, {14, 12}}, {{TQEType::YX, 10, 6}, {9, 3}}, - {{TQEType::YY, 10, 6}, {5, 9}}, {{TQEType::YZ, 10, 6}, {6, 12}}, - {{TQEType::ZX, 10, 6}, {8, 6}}, {{TQEType::ZY, 10, 6}, {0, 6}}, - {{TQEType::ZZ, 10, 6}, {2, 6}}, {{TQEType::XX, 8, 6}, {9, 2}}, - {{TQEType::XY, 8, 6}, {13, 10}}, {{TQEType::XZ, 8, 6}, {12, 14}}, - {{TQEType::YX, 8, 6}, {11, 2}}, {{TQEType::YY, 8, 6}, {7, 10}}, - {{TQEType::YZ, 8, 6}, {4, 14}}, {{TQEType::ZX, 8, 6}, {10, 6}}, - {{TQEType::ZY, 8, 6}, {2, 6}}, {{TQEType::ZZ, 8, 6}, {0, 6}}, - {{TQEType::XX, 9, 4}, {9, 0}}, {{TQEType::XY, 9, 4}, {13, 8}}, - {{TQEType::XZ, 9, 4}, {13, 12}}, {{TQEType::YX, 9, 4}, {9, 1}}, - {{TQEType::YY, 9, 4}, {5, 11}}, {{TQEType::YZ, 9, 4}, {5, 14}}, - {{TQEType::ZX, 9, 4}, {9, 5}}, {{TQEType::ZY, 9, 4}, {1, 7}}, - {{TQEType::ZZ, 9, 4}, {1, 6}}, {{TQEType::XX, 11, 4}, {11, 1}}, - {{TQEType::XY, 11, 4}, {15, 11}}, {{TQEType::XZ, 11, 4}, {15, 14}}, - {{TQEType::YX, 11, 4}, {11, 0}}, {{TQEType::YY, 11, 4}, {7, 8}}, - {{TQEType::YZ, 11, 4}, {7, 12}}, {{TQEType::ZX, 11, 4}, {11, 5}}, - {{TQEType::ZY, 11, 4}, {3, 7}}, {{TQEType::ZZ, 11, 4}, {3, 6}}, - {{TQEType::XX, 10, 5}, {10, 0}}, {{TQEType::XY, 10, 5}, {15, 10}}, - {{TQEType::XZ, 10, 5}, {15, 15}}, {{TQEType::YX, 10, 5}, {10, 0}}, - {{TQEType::YY, 10, 5}, {5, 10}}, {{TQEType::YZ, 10, 5}, {5, 15}}, - {{TQEType::ZX, 10, 5}, {10, 5}}, {{TQEType::ZY, 10, 5}, {0, 5}}, - {{TQEType::ZZ, 10, 5}, {0, 5}}, {{TQEType::XX, 8, 5}, {8, 1}}, - {{TQEType::XY, 8, 5}, {13, 9}}, {{TQEType::XZ, 8, 5}, {13, 13}}, - {{TQEType::YX, 8, 5}, {8, 1}}, {{TQEType::YY, 8, 5}, {7, 9}}, - {{TQEType::YZ, 8, 5}, {7, 13}}, {{TQEType::ZX, 8, 5}, {8, 5}}, - {{TQEType::ZY, 8, 5}, {2, 5}}, {{TQEType::ZZ, 8, 5}, {2, 5}}, - {{TQEType::XX, 9, 7}, {8, 3}}, {{TQEType::XY, 9, 7}, {13, 11}}, - {{TQEType::XZ, 9, 7}, {12, 15}}, {{TQEType::YX, 9, 7}, {10, 2}}, - {{TQEType::YY, 9, 7}, {5, 8}}, {{TQEType::YZ, 9, 7}, {6, 13}}, - {{TQEType::ZX, 9, 7}, {11, 6}}, {{TQEType::ZY, 9, 7}, {1, 4}}, - {{TQEType::ZZ, 9, 7}, {3, 5}}, {{TQEType::XX, 11, 7}, {10, 2}}, - {{TQEType::XY, 11, 7}, {15, 8}}, {{TQEType::XZ, 11, 7}, {14, 13}}, - {{TQEType::YX, 11, 7}, {8, 3}}, {{TQEType::YY, 11, 7}, {7, 11}}, - {{TQEType::YZ, 11, 7}, {4, 15}}, {{TQEType::ZX, 11, 7}, {9, 6}}, - {{TQEType::ZY, 11, 7}, {3, 4}}, {{TQEType::ZZ, 11, 7}, {1, 5}}, - {{TQEType::XX, 9, 6}, {8, 2}}, {{TQEType::XY, 9, 6}, {12, 10}}, - {{TQEType::XZ, 9, 6}, {13, 14}}, {{TQEType::YX, 9, 6}, {10, 3}}, - {{TQEType::YY, 9, 6}, {6, 9}}, {{TQEType::YZ, 9, 6}, {5, 12}}, - {{TQEType::ZX, 9, 6}, {11, 7}}, {{TQEType::ZY, 9, 6}, {3, 5}}, - {{TQEType::ZZ, 9, 6}, {1, 4}}, {{TQEType::XX, 11, 6}, {10, 3}}, - {{TQEType::XY, 11, 6}, {14, 9}}, {{TQEType::XZ, 11, 6}, {15, 12}}, - {{TQEType::YX, 11, 6}, {8, 2}}, {{TQEType::YY, 11, 6}, {4, 10}}, - {{TQEType::YZ, 11, 6}, {7, 14}}, {{TQEType::ZX, 11, 6}, {9, 7}}, - {{TQEType::ZY, 11, 6}, {1, 5}}, {{TQEType::ZZ, 11, 6}, {3, 4}}, - {{TQEType::XX, 10, 4}, {10, 1}}, {{TQEType::XY, 10, 4}, {14, 11}}, - {{TQEType::XZ, 10, 4}, {14, 14}}, {{TQEType::YX, 10, 4}, {10, 1}}, - {{TQEType::YY, 10, 4}, {6, 11}}, {{TQEType::YZ, 10, 4}, {6, 14}}, - {{TQEType::ZX, 10, 4}, {10, 4}}, {{TQEType::ZY, 10, 4}, {2, 4}}, - {{TQEType::ZZ, 10, 4}, {2, 4}}, {{TQEType::XX, 8, 4}, {8, 0}}, - {{TQEType::XY, 8, 4}, {12, 8}}, {{TQEType::XZ, 8, 4}, {12, 12}}, - {{TQEType::YX, 8, 4}, {8, 0}}, {{TQEType::YY, 8, 4}, {4, 8}}, - {{TQEType::YZ, 8, 4}, {4, 12}}, {{TQEType::ZX, 8, 4}, {8, 4}}, - {{TQEType::ZY, 8, 4}, {0, 4}}, {{TQEType::ZZ, 8, 4}, {0, 4}}, - {{TQEType::XX, 1, 7}, {0, 7}}, {{TQEType::XY, 1, 7}, {5, 7}}, - {{TQEType::XZ, 1, 7}, {4, 7}}, {{TQEType::YX, 1, 7}, {2, 6}}, - {{TQEType::YY, 1, 7}, {13, 4}}, {{TQEType::YZ, 1, 7}, {14, 5}}, - {{TQEType::ZX, 1, 7}, {3, 6}}, {{TQEType::ZY, 1, 7}, {9, 4}}, - {{TQEType::ZZ, 1, 7}, {11, 5}}, {{TQEType::XX, 3, 7}, {2, 6}}, - {{TQEType::XY, 3, 7}, {7, 4}}, {{TQEType::XZ, 3, 7}, {6, 5}}, - {{TQEType::YX, 3, 7}, {0, 7}}, {{TQEType::YY, 3, 7}, {15, 7}}, - {{TQEType::YZ, 3, 7}, {12, 7}}, {{TQEType::ZX, 3, 7}, {1, 6}}, - {{TQEType::ZY, 3, 7}, {11, 4}}, {{TQEType::ZZ, 3, 7}, {9, 5}}, - {{TQEType::XX, 2, 7}, {3, 6}}, {{TQEType::XY, 2, 7}, {6, 4}}, - {{TQEType::XZ, 2, 7}, {7, 5}}, {{TQEType::YX, 2, 7}, {1, 6}}, - {{TQEType::YY, 2, 7}, {14, 4}}, {{TQEType::YZ, 2, 7}, {13, 5}}, - {{TQEType::ZX, 2, 7}, {0, 7}}, {{TQEType::ZY, 2, 7}, {10, 7}}, - {{TQEType::ZZ, 2, 7}, {8, 7}}, {{TQEType::XX, 0, 7}, {1, 7}}, - {{TQEType::XY, 0, 7}, {4, 7}}, {{TQEType::XZ, 0, 7}, {5, 7}}, - {{TQEType::YX, 0, 7}, {3, 7}}, {{TQEType::YY, 0, 7}, {12, 7}}, - {{TQEType::YZ, 0, 7}, {15, 7}}, {{TQEType::ZX, 0, 7}, {2, 7}}, - {{TQEType::ZY, 0, 7}, {8, 7}}, {{TQEType::ZZ, 0, 7}, {10, 7}}, - {{TQEType::XX, 1, 6}, {0, 6}}, {{TQEType::XY, 1, 6}, {4, 6}}, - {{TQEType::XZ, 1, 6}, {5, 6}}, {{TQEType::YX, 1, 6}, {2, 7}}, - {{TQEType::YY, 1, 6}, {14, 5}}, {{TQEType::YZ, 1, 6}, {13, 4}}, - {{TQEType::ZX, 1, 6}, {3, 7}}, {{TQEType::ZY, 1, 6}, {11, 5}}, - {{TQEType::ZZ, 1, 6}, {9, 4}}, {{TQEType::XX, 3, 6}, {2, 7}}, - {{TQEType::XY, 3, 6}, {6, 5}}, {{TQEType::XZ, 3, 6}, {7, 4}}, - {{TQEType::YX, 3, 6}, {0, 6}}, {{TQEType::YY, 3, 6}, {12, 6}}, - {{TQEType::YZ, 3, 6}, {15, 6}}, {{TQEType::ZX, 3, 6}, {1, 7}}, - {{TQEType::ZY, 3, 6}, {9, 5}}, {{TQEType::ZZ, 3, 6}, {11, 4}}, - {{TQEType::XX, 2, 6}, {3, 7}}, {{TQEType::XY, 2, 6}, {7, 5}}, - {{TQEType::XZ, 2, 6}, {6, 4}}, {{TQEType::YX, 2, 6}, {1, 7}}, - {{TQEType::YY, 2, 6}, {13, 5}}, {{TQEType::YZ, 2, 6}, {14, 4}}, - {{TQEType::ZX, 2, 6}, {0, 6}}, {{TQEType::ZY, 2, 6}, {8, 6}}, - {{TQEType::ZZ, 2, 6}, {10, 6}}, {{TQEType::XX, 0, 6}, {1, 6}}, - {{TQEType::XY, 0, 6}, {5, 6}}, {{TQEType::XZ, 0, 6}, {4, 6}}, - {{TQEType::YX, 0, 6}, {3, 6}}, {{TQEType::YY, 0, 6}, {15, 6}}, - {{TQEType::YZ, 0, 6}, {12, 6}}, {{TQEType::ZX, 0, 6}, {2, 6}}, - {{TQEType::ZY, 0, 6}, {10, 6}}, {{TQEType::ZZ, 0, 6}, {8, 6}}, - {{TQEType::XX, 1, 5}, {1, 5}}, {{TQEType::XY, 1, 5}, {4, 5}}, - {{TQEType::XZ, 1, 5}, {4, 5}}, {{TQEType::YX, 1, 5}, {1, 4}}, - {{TQEType::YY, 1, 5}, {14, 6}}, {{TQEType::YZ, 1, 5}, {14, 7}}, - {{TQEType::ZX, 1, 5}, {1, 4}}, {{TQEType::ZY, 1, 5}, {11, 6}}, - {{TQEType::ZZ, 1, 5}, {11, 7}}, {{TQEType::XX, 3, 5}, {3, 4}}, - {{TQEType::XY, 3, 5}, {6, 6}}, {{TQEType::XZ, 3, 5}, {6, 7}}, - {{TQEType::YX, 3, 5}, {3, 5}}, {{TQEType::YY, 3, 5}, {12, 5}}, - {{TQEType::YZ, 3, 5}, {12, 5}}, {{TQEType::ZX, 3, 5}, {3, 4}}, - {{TQEType::ZY, 3, 5}, {9, 6}}, {{TQEType::ZZ, 3, 5}, {9, 7}}, - {{TQEType::XX, 2, 5}, {2, 4}}, {{TQEType::XY, 2, 5}, {7, 6}}, - {{TQEType::XZ, 2, 5}, {7, 7}}, {{TQEType::YX, 2, 5}, {2, 4}}, - {{TQEType::YY, 2, 5}, {13, 6}}, {{TQEType::YZ, 2, 5}, {13, 7}}, - {{TQEType::ZX, 2, 5}, {2, 5}}, {{TQEType::ZY, 2, 5}, {8, 5}}, - {{TQEType::ZZ, 2, 5}, {8, 5}}, {{TQEType::XX, 0, 5}, {0, 5}}, - {{TQEType::XY, 0, 5}, {5, 5}}, {{TQEType::XZ, 0, 5}, {5, 5}}, - {{TQEType::YX, 0, 5}, {0, 5}}, {{TQEType::YY, 0, 5}, {15, 5}}, - {{TQEType::YZ, 0, 5}, {15, 5}}, {{TQEType::ZX, 0, 5}, {0, 5}}, - {{TQEType::ZY, 0, 5}, {10, 5}}, {{TQEType::ZZ, 0, 5}, {10, 5}}, - {{TQEType::XX, 1, 4}, {1, 4}}, {{TQEType::XY, 1, 4}, {5, 4}}, - {{TQEType::XZ, 1, 4}, {5, 4}}, {{TQEType::YX, 1, 4}, {1, 5}}, - {{TQEType::YY, 1, 4}, {13, 7}}, {{TQEType::YZ, 1, 4}, {13, 6}}, - {{TQEType::ZX, 1, 4}, {1, 5}}, {{TQEType::ZY, 1, 4}, {9, 7}}, - {{TQEType::ZZ, 1, 4}, {9, 6}}, {{TQEType::XX, 3, 4}, {3, 5}}, - {{TQEType::XY, 3, 4}, {7, 7}}, {{TQEType::XZ, 3, 4}, {7, 6}}, - {{TQEType::YX, 3, 4}, {3, 4}}, {{TQEType::YY, 3, 4}, {15, 4}}, - {{TQEType::YZ, 3, 4}, {15, 4}}, {{TQEType::ZX, 3, 4}, {3, 5}}, - {{TQEType::ZY, 3, 4}, {11, 7}}, {{TQEType::ZZ, 3, 4}, {11, 6}}, - {{TQEType::XX, 2, 4}, {2, 5}}, {{TQEType::XY, 2, 4}, {6, 7}}, - {{TQEType::XZ, 2, 4}, {6, 6}}, {{TQEType::YX, 2, 4}, {2, 5}}, - {{TQEType::YY, 2, 4}, {14, 7}}, {{TQEType::YZ, 2, 4}, {14, 6}}, - {{TQEType::ZX, 2, 4}, {2, 4}}, {{TQEType::ZY, 2, 4}, {10, 4}}, - {{TQEType::ZZ, 2, 4}, {10, 4}}, {{TQEType::XX, 0, 4}, {0, 4}}, - {{TQEType::XY, 0, 4}, {4, 4}}, {{TQEType::XZ, 0, 4}, {4, 4}}, - {{TQEType::YX, 0, 4}, {0, 4}}, {{TQEType::YY, 0, 4}, {12, 4}}, - {{TQEType::YZ, 0, 4}, {12, 4}}, {{TQEType::ZX, 0, 4}, {0, 4}}, - {{TQEType::ZY, 0, 4}, {8, 4}}, {{TQEType::ZZ, 0, 4}, {8, 4}}, - {{TQEType::XX, 5, 13}, {1, 13}}, {{TQEType::XY, 5, 13}, {4, 13}}, - {{TQEType::XZ, 5, 13}, {0, 13}}, {{TQEType::YX, 5, 13}, {9, 8}}, - {{TQEType::YY, 5, 13}, {6, 2}}, {{TQEType::YZ, 5, 13}, {10, 7}}, - {{TQEType::ZX, 5, 13}, {13, 8}}, {{TQEType::ZY, 5, 13}, {7, 2}}, - {{TQEType::ZZ, 5, 13}, {15, 7}}, {{TQEType::XX, 4, 13}, {0, 13}}, - {{TQEType::XY, 4, 13}, {5, 13}}, {{TQEType::XZ, 4, 13}, {1, 13}}, - {{TQEType::YX, 4, 13}, {8, 9}}, {{TQEType::YY, 4, 13}, {7, 1}}, - {{TQEType::YZ, 4, 13}, {11, 5}}, {{TQEType::ZX, 4, 13}, {12, 9}}, - {{TQEType::ZY, 4, 13}, {6, 1}}, {{TQEType::ZZ, 4, 13}, {14, 5}}, - {{TQEType::XX, 7, 15}, {2, 14}}, {{TQEType::XY, 7, 15}, {7, 12}}, - {{TQEType::XZ, 7, 15}, {2, 13}}, {{TQEType::YX, 7, 15}, {8, 11}}, - {{TQEType::YY, 7, 15}, {7, 3}}, {{TQEType::YZ, 7, 15}, {8, 7}}, - {{TQEType::ZX, 7, 15}, {13, 10}}, {{TQEType::ZY, 7, 15}, {7, 0}}, - {{TQEType::ZZ, 7, 15}, {13, 5}}, {{TQEType::XX, 6, 15}, {3, 14}}, - {{TQEType::XY, 6, 15}, {6, 12}}, {{TQEType::XZ, 6, 15}, {3, 13}}, - {{TQEType::YX, 6, 15}, {9, 10}}, {{TQEType::YY, 6, 15}, {6, 0}}, - {{TQEType::YZ, 6, 15}, {9, 5}}, {{TQEType::ZX, 6, 15}, {12, 11}}, - {{TQEType::ZY, 6, 15}, {6, 3}}, {{TQEType::ZZ, 6, 15}, {12, 7}}, - {{TQEType::XX, 5, 14}, {0, 14}}, {{TQEType::XY, 5, 14}, {4, 14}}, - {{TQEType::XZ, 5, 14}, {1, 14}}, {{TQEType::YX, 5, 14}, {10, 11}}, - {{TQEType::YY, 5, 14}, {6, 1}}, {{TQEType::YZ, 5, 14}, {9, 4}}, - {{TQEType::ZX, 5, 14}, {15, 11}}, {{TQEType::ZY, 5, 14}, {7, 1}}, - {{TQEType::ZZ, 5, 14}, {13, 4}}, {{TQEType::XX, 4, 14}, {1, 14}}, - {{TQEType::XY, 4, 14}, {5, 14}}, {{TQEType::XZ, 4, 14}, {0, 14}}, - {{TQEType::YX, 4, 14}, {11, 10}}, {{TQEType::YY, 4, 14}, {7, 2}}, - {{TQEType::YZ, 4, 14}, {8, 6}}, {{TQEType::ZX, 4, 14}, {14, 10}}, - {{TQEType::ZY, 4, 14}, {6, 2}}, {{TQEType::ZZ, 4, 14}, {12, 6}}, - {{TQEType::XX, 7, 12}, {3, 13}}, {{TQEType::XY, 7, 12}, {7, 15}}, - {{TQEType::XZ, 7, 12}, {3, 14}}, {{TQEType::YX, 7, 12}, {11, 8}}, - {{TQEType::YY, 7, 12}, {7, 0}}, {{TQEType::YZ, 7, 12}, {11, 4}}, - {{TQEType::ZX, 7, 12}, {15, 9}}, {{TQEType::ZY, 7, 12}, {7, 3}}, - {{TQEType::ZZ, 7, 12}, {15, 6}}, {{TQEType::XX, 6, 12}, {2, 13}}, - {{TQEType::XY, 6, 12}, {6, 15}}, {{TQEType::XZ, 6, 12}, {2, 14}}, - {{TQEType::YX, 6, 12}, {10, 9}}, {{TQEType::YY, 6, 12}, {6, 3}}, - {{TQEType::YZ, 6, 12}, {10, 6}}, {{TQEType::ZX, 6, 12}, {14, 8}}, - {{TQEType::ZY, 6, 12}, {6, 0}}, {{TQEType::ZZ, 6, 12}, {14, 4}}, - {{TQEType::XX, 7, 13}, {3, 12}}, {{TQEType::XY, 7, 13}, {6, 14}}, - {{TQEType::XZ, 7, 13}, {2, 15}}, {{TQEType::YX, 7, 13}, {11, 9}}, - {{TQEType::YY, 7, 13}, {4, 1}}, {{TQEType::YZ, 7, 13}, {8, 5}}, - {{TQEType::ZX, 7, 13}, {15, 8}}, {{TQEType::ZY, 7, 13}, {5, 2}}, - {{TQEType::ZZ, 7, 13}, {13, 7}}, {{TQEType::XX, 6, 13}, {2, 12}}, - {{TQEType::XY, 6, 13}, {7, 14}}, {{TQEType::XZ, 6, 13}, {3, 15}}, - {{TQEType::YX, 6, 13}, {10, 8}}, {{TQEType::YY, 6, 13}, {5, 2}}, - {{TQEType::YZ, 6, 13}, {9, 7}}, {{TQEType::ZX, 6, 13}, {14, 9}}, - {{TQEType::ZY, 6, 13}, {4, 1}}, {{TQEType::ZZ, 6, 13}, {12, 5}}, - {{TQEType::XX, 5, 15}, {0, 15}}, {{TQEType::XY, 5, 15}, {5, 15}}, - {{TQEType::XZ, 5, 15}, {0, 15}}, {{TQEType::YX, 5, 15}, {10, 10}}, - {{TQEType::YY, 5, 15}, {5, 0}}, {{TQEType::YZ, 5, 15}, {10, 5}}, - {{TQEType::ZX, 5, 15}, {15, 10}}, {{TQEType::ZY, 5, 15}, {5, 0}}, - {{TQEType::ZZ, 5, 15}, {15, 5}}, {{TQEType::XX, 4, 15}, {1, 15}}, - {{TQEType::XY, 4, 15}, {4, 15}}, {{TQEType::XZ, 4, 15}, {1, 15}}, - {{TQEType::YX, 4, 15}, {11, 11}}, {{TQEType::YY, 4, 15}, {4, 3}}, - {{TQEType::YZ, 4, 15}, {11, 7}}, {{TQEType::ZX, 4, 15}, {14, 11}}, - {{TQEType::ZY, 4, 15}, {4, 3}}, {{TQEType::ZZ, 4, 15}, {14, 7}}, - {{TQEType::XX, 7, 14}, {2, 15}}, {{TQEType::XY, 7, 14}, {6, 13}}, - {{TQEType::XZ, 7, 14}, {3, 12}}, {{TQEType::YX, 7, 14}, {8, 10}}, - {{TQEType::YY, 7, 14}, {4, 2}}, {{TQEType::YZ, 7, 14}, {11, 6}}, - {{TQEType::ZX, 7, 14}, {13, 11}}, {{TQEType::ZY, 7, 14}, {5, 1}}, - {{TQEType::ZZ, 7, 14}, {15, 4}}, {{TQEType::XX, 6, 14}, {3, 15}}, - {{TQEType::XY, 6, 14}, {7, 13}}, {{TQEType::XZ, 6, 14}, {2, 12}}, - {{TQEType::YX, 6, 14}, {9, 11}}, {{TQEType::YY, 6, 14}, {5, 1}}, - {{TQEType::YZ, 6, 14}, {10, 4}}, {{TQEType::ZX, 6, 14}, {12, 10}}, - {{TQEType::ZY, 6, 14}, {4, 2}}, {{TQEType::ZZ, 6, 14}, {14, 6}}, - {{TQEType::XX, 5, 12}, {1, 12}}, {{TQEType::XY, 5, 12}, {5, 12}}, - {{TQEType::XZ, 5, 12}, {1, 12}}, {{TQEType::YX, 5, 12}, {9, 9}}, - {{TQEType::YY, 5, 12}, {5, 3}}, {{TQEType::YZ, 5, 12}, {9, 6}}, - {{TQEType::ZX, 5, 12}, {13, 9}}, {{TQEType::ZY, 5, 12}, {5, 3}}, - {{TQEType::ZZ, 5, 12}, {13, 6}}, {{TQEType::XX, 4, 12}, {0, 12}}, - {{TQEType::XY, 4, 12}, {4, 12}}, {{TQEType::XZ, 4, 12}, {0, 12}}, - {{TQEType::YX, 4, 12}, {8, 8}}, {{TQEType::YY, 4, 12}, {4, 0}}, - {{TQEType::YZ, 4, 12}, {8, 4}}, {{TQEType::ZX, 4, 12}, {12, 8}}, - {{TQEType::ZY, 4, 12}, {4, 0}}, {{TQEType::ZZ, 4, 12}, {12, 4}}, - {{TQEType::XX, 15, 13}, {11, 8}}, {{TQEType::XY, 15, 13}, {14, 2}}, - {{TQEType::XZ, 15, 13}, {10, 7}}, {{TQEType::YX, 15, 13}, {3, 13}}, - {{TQEType::YY, 15, 13}, {12, 13}}, {{TQEType::YZ, 15, 13}, {0, 13}}, - {{TQEType::ZX, 15, 13}, {7, 8}}, {{TQEType::ZY, 15, 13}, {13, 2}}, - {{TQEType::ZZ, 15, 13}, {5, 7}}, {{TQEType::XX, 12, 13}, {8, 9}}, - {{TQEType::XY, 12, 13}, {13, 1}}, {{TQEType::XZ, 12, 13}, {9, 5}}, - {{TQEType::YX, 12, 13}, {0, 13}}, {{TQEType::YY, 12, 13}, {15, 13}}, - {{TQEType::YZ, 12, 13}, {3, 13}}, {{TQEType::ZX, 12, 13}, {4, 9}}, - {{TQEType::ZY, 12, 13}, {14, 1}}, {{TQEType::ZZ, 12, 13}, {6, 5}}, - {{TQEType::XX, 13, 15}, {8, 11}}, {{TQEType::XY, 13, 15}, {13, 3}}, - {{TQEType::XZ, 13, 15}, {8, 7}}, {{TQEType::YX, 13, 15}, {2, 14}}, - {{TQEType::YY, 13, 15}, {13, 12}}, {{TQEType::YZ, 13, 15}, {2, 13}}, - {{TQEType::ZX, 13, 15}, {7, 10}}, {{TQEType::ZY, 13, 15}, {13, 0}}, - {{TQEType::ZZ, 13, 15}, {7, 5}}, {{TQEType::XX, 14, 15}, {11, 10}}, - {{TQEType::XY, 14, 15}, {14, 0}}, {{TQEType::XZ, 14, 15}, {11, 5}}, - {{TQEType::YX, 14, 15}, {1, 14}}, {{TQEType::YY, 14, 15}, {14, 12}}, - {{TQEType::YZ, 14, 15}, {1, 13}}, {{TQEType::ZX, 14, 15}, {4, 11}}, - {{TQEType::ZY, 14, 15}, {14, 3}}, {{TQEType::ZZ, 14, 15}, {4, 7}}, - {{TQEType::XX, 15, 14}, {10, 11}}, {{TQEType::XY, 15, 14}, {14, 1}}, - {{TQEType::XZ, 15, 14}, {11, 4}}, {{TQEType::YX, 15, 14}, {0, 14}}, - {{TQEType::YY, 15, 14}, {12, 14}}, {{TQEType::YZ, 15, 14}, {3, 14}}, - {{TQEType::ZX, 15, 14}, {5, 11}}, {{TQEType::ZY, 15, 14}, {13, 1}}, - {{TQEType::ZZ, 15, 14}, {7, 4}}, {{TQEType::XX, 12, 14}, {9, 10}}, - {{TQEType::XY, 12, 14}, {13, 2}}, {{TQEType::XZ, 12, 14}, {8, 6}}, - {{TQEType::YX, 12, 14}, {3, 14}}, {{TQEType::YY, 12, 14}, {15, 14}}, - {{TQEType::YZ, 12, 14}, {0, 14}}, {{TQEType::ZX, 12, 14}, {6, 10}}, - {{TQEType::ZY, 12, 14}, {14, 2}}, {{TQEType::ZZ, 12, 14}, {4, 6}}, - {{TQEType::XX, 13, 12}, {9, 8}}, {{TQEType::XY, 13, 12}, {13, 0}}, - {{TQEType::XZ, 13, 12}, {9, 4}}, {{TQEType::YX, 13, 12}, {1, 13}}, - {{TQEType::YY, 13, 12}, {13, 15}}, {{TQEType::YZ, 13, 12}, {1, 14}}, - {{TQEType::ZX, 13, 12}, {5, 9}}, {{TQEType::ZY, 13, 12}, {13, 3}}, - {{TQEType::ZZ, 13, 12}, {5, 6}}, {{TQEType::XX, 14, 12}, {10, 9}}, - {{TQEType::XY, 14, 12}, {14, 3}}, {{TQEType::XZ, 14, 12}, {10, 6}}, - {{TQEType::YX, 14, 12}, {2, 13}}, {{TQEType::YY, 14, 12}, {14, 15}}, - {{TQEType::YZ, 14, 12}, {2, 14}}, {{TQEType::ZX, 14, 12}, {6, 8}}, - {{TQEType::ZY, 14, 12}, {14, 0}}, {{TQEType::ZZ, 14, 12}, {6, 4}}, - {{TQEType::XX, 13, 13}, {9, 9}}, {{TQEType::XY, 13, 13}, {12, 1}}, - {{TQEType::XZ, 13, 13}, {8, 5}}, {{TQEType::YX, 13, 13}, {1, 12}}, - {{TQEType::YY, 13, 13}, {14, 14}}, {{TQEType::YZ, 13, 13}, {2, 15}}, - {{TQEType::ZX, 13, 13}, {5, 8}}, {{TQEType::ZY, 13, 13}, {15, 2}}, - {{TQEType::ZZ, 13, 13}, {7, 7}}, {{TQEType::XX, 14, 13}, {10, 8}}, - {{TQEType::XY, 14, 13}, {15, 2}}, {{TQEType::XZ, 14, 13}, {11, 7}}, - {{TQEType::YX, 14, 13}, {2, 12}}, {{TQEType::YY, 14, 13}, {13, 14}}, - {{TQEType::YZ, 14, 13}, {1, 15}}, {{TQEType::ZX, 14, 13}, {6, 9}}, - {{TQEType::ZY, 14, 13}, {12, 1}}, {{TQEType::ZZ, 14, 13}, {4, 5}}, - {{TQEType::XX, 15, 15}, {10, 10}}, {{TQEType::XY, 15, 15}, {15, 0}}, - {{TQEType::XZ, 15, 15}, {10, 5}}, {{TQEType::YX, 15, 15}, {0, 15}}, - {{TQEType::YY, 15, 15}, {15, 15}}, {{TQEType::YZ, 15, 15}, {0, 15}}, - {{TQEType::ZX, 15, 15}, {5, 10}}, {{TQEType::ZY, 15, 15}, {15, 0}}, - {{TQEType::ZZ, 15, 15}, {5, 5}}, {{TQEType::XX, 12, 15}, {9, 11}}, - {{TQEType::XY, 12, 15}, {12, 3}}, {{TQEType::XZ, 12, 15}, {9, 7}}, - {{TQEType::YX, 12, 15}, {3, 15}}, {{TQEType::YY, 12, 15}, {12, 15}}, - {{TQEType::YZ, 12, 15}, {3, 15}}, {{TQEType::ZX, 12, 15}, {6, 11}}, - {{TQEType::ZY, 12, 15}, {12, 3}}, {{TQEType::ZZ, 12, 15}, {6, 7}}, - {{TQEType::XX, 13, 14}, {8, 10}}, {{TQEType::XY, 13, 14}, {12, 2}}, - {{TQEType::XZ, 13, 14}, {9, 6}}, {{TQEType::YX, 13, 14}, {2, 15}}, - {{TQEType::YY, 13, 14}, {14, 13}}, {{TQEType::YZ, 13, 14}, {1, 12}}, - {{TQEType::ZX, 13, 14}, {7, 11}}, {{TQEType::ZY, 13, 14}, {15, 1}}, - {{TQEType::ZZ, 13, 14}, {5, 4}}, {{TQEType::XX, 14, 14}, {11, 11}}, - {{TQEType::XY, 14, 14}, {15, 1}}, {{TQEType::XZ, 14, 14}, {10, 4}}, - {{TQEType::YX, 14, 14}, {1, 15}}, {{TQEType::YY, 14, 14}, {13, 13}}, - {{TQEType::YZ, 14, 14}, {2, 12}}, {{TQEType::ZX, 14, 14}, {4, 10}}, - {{TQEType::ZY, 14, 14}, {12, 2}}, {{TQEType::ZZ, 14, 14}, {6, 6}}, - {{TQEType::XX, 15, 12}, {11, 9}}, {{TQEType::XY, 15, 12}, {15, 3}}, - {{TQEType::XZ, 15, 12}, {11, 6}}, {{TQEType::YX, 15, 12}, {3, 12}}, - {{TQEType::YY, 15, 12}, {15, 12}}, {{TQEType::YZ, 15, 12}, {3, 12}}, - {{TQEType::ZX, 15, 12}, {7, 9}}, {{TQEType::ZY, 15, 12}, {15, 3}}, - {{TQEType::ZZ, 15, 12}, {7, 6}}, {{TQEType::XX, 12, 12}, {8, 8}}, - {{TQEType::XY, 12, 12}, {12, 0}}, {{TQEType::XZ, 12, 12}, {8, 4}}, - {{TQEType::YX, 12, 12}, {0, 12}}, {{TQEType::YY, 12, 12}, {12, 12}}, - {{TQEType::YZ, 12, 12}, {0, 12}}, {{TQEType::ZX, 12, 12}, {4, 8}}, - {{TQEType::ZY, 12, 12}, {12, 0}}, {{TQEType::ZZ, 12, 12}, {4, 4}}, - {{TQEType::XX, 10, 13}, {14, 8}}, {{TQEType::XY, 10, 13}, {11, 2}}, - {{TQEType::XZ, 10, 13}, {15, 7}}, {{TQEType::YX, 10, 13}, {6, 8}}, - {{TQEType::YY, 10, 13}, {9, 2}}, {{TQEType::YZ, 10, 13}, {5, 7}}, - {{TQEType::ZX, 10, 13}, {2, 13}}, {{TQEType::ZY, 10, 13}, {8, 13}}, - {{TQEType::ZZ, 10, 13}, {0, 13}}, {{TQEType::XX, 8, 13}, {12, 9}}, - {{TQEType::XY, 8, 13}, {9, 1}}, {{TQEType::XZ, 8, 13}, {13, 5}}, - {{TQEType::YX, 8, 13}, {4, 9}}, {{TQEType::YY, 8, 13}, {11, 1}}, - {{TQEType::YZ, 8, 13}, {7, 5}}, {{TQEType::ZX, 8, 13}, {0, 13}}, - {{TQEType::ZY, 8, 13}, {10, 13}}, {{TQEType::ZZ, 8, 13}, {2, 13}}, - {{TQEType::XX, 9, 15}, {12, 11}}, {{TQEType::XY, 9, 15}, {9, 3}}, - {{TQEType::XZ, 9, 15}, {12, 7}}, {{TQEType::YX, 9, 15}, {6, 10}}, - {{TQEType::YY, 9, 15}, {9, 0}}, {{TQEType::YZ, 9, 15}, {6, 5}}, - {{TQEType::ZX, 9, 15}, {3, 14}}, {{TQEType::ZY, 9, 15}, {9, 12}}, - {{TQEType::ZZ, 9, 15}, {3, 13}}, {{TQEType::XX, 11, 15}, {14, 10}}, - {{TQEType::XY, 11, 15}, {11, 0}}, {{TQEType::XZ, 11, 15}, {14, 5}}, - {{TQEType::YX, 11, 15}, {4, 11}}, {{TQEType::YY, 11, 15}, {11, 3}}, - {{TQEType::YZ, 11, 15}, {4, 7}}, {{TQEType::ZX, 11, 15}, {1, 14}}, - {{TQEType::ZY, 11, 15}, {11, 12}}, {{TQEType::ZZ, 11, 15}, {1, 13}}, - {{TQEType::XX, 10, 14}, {15, 11}}, {{TQEType::XY, 10, 14}, {11, 1}}, - {{TQEType::XZ, 10, 14}, {14, 4}}, {{TQEType::YX, 10, 14}, {5, 11}}, - {{TQEType::YY, 10, 14}, {9, 1}}, {{TQEType::YZ, 10, 14}, {6, 4}}, - {{TQEType::ZX, 10, 14}, {0, 14}}, {{TQEType::ZY, 10, 14}, {8, 14}}, - {{TQEType::ZZ, 10, 14}, {2, 14}}, {{TQEType::XX, 8, 14}, {13, 10}}, - {{TQEType::XY, 8, 14}, {9, 2}}, {{TQEType::XZ, 8, 14}, {12, 6}}, - {{TQEType::YX, 8, 14}, {7, 10}}, {{TQEType::YY, 8, 14}, {11, 2}}, - {{TQEType::YZ, 8, 14}, {4, 6}}, {{TQEType::ZX, 8, 14}, {2, 14}}, - {{TQEType::ZY, 8, 14}, {10, 14}}, {{TQEType::ZZ, 8, 14}, {0, 14}}, - {{TQEType::XX, 9, 12}, {13, 8}}, {{TQEType::XY, 9, 12}, {9, 0}}, - {{TQEType::XZ, 9, 12}, {13, 4}}, {{TQEType::YX, 9, 12}, {5, 9}}, - {{TQEType::YY, 9, 12}, {9, 3}}, {{TQEType::YZ, 9, 12}, {5, 6}}, - {{TQEType::ZX, 9, 12}, {1, 13}}, {{TQEType::ZY, 9, 12}, {9, 15}}, - {{TQEType::ZZ, 9, 12}, {1, 14}}, {{TQEType::XX, 11, 12}, {15, 9}}, - {{TQEType::XY, 11, 12}, {11, 3}}, {{TQEType::XZ, 11, 12}, {15, 6}}, - {{TQEType::YX, 11, 12}, {7, 8}}, {{TQEType::YY, 11, 12}, {11, 0}}, - {{TQEType::YZ, 11, 12}, {7, 4}}, {{TQEType::ZX, 11, 12}, {3, 13}}, - {{TQEType::ZY, 11, 12}, {11, 15}}, {{TQEType::ZZ, 11, 12}, {3, 14}}, - {{TQEType::XX, 9, 13}, {13, 9}}, {{TQEType::XY, 9, 13}, {8, 1}}, - {{TQEType::XZ, 9, 13}, {12, 5}}, {{TQEType::YX, 9, 13}, {5, 8}}, - {{TQEType::YY, 9, 13}, {10, 2}}, {{TQEType::YZ, 9, 13}, {6, 7}}, - {{TQEType::ZX, 9, 13}, {1, 12}}, {{TQEType::ZY, 9, 13}, {11, 14}}, - {{TQEType::ZZ, 9, 13}, {3, 15}}, {{TQEType::XX, 11, 13}, {15, 8}}, - {{TQEType::XY, 11, 13}, {10, 2}}, {{TQEType::XZ, 11, 13}, {14, 7}}, - {{TQEType::YX, 11, 13}, {7, 9}}, {{TQEType::YY, 11, 13}, {8, 1}}, - {{TQEType::YZ, 11, 13}, {4, 5}}, {{TQEType::ZX, 11, 13}, {3, 12}}, - {{TQEType::ZY, 11, 13}, {9, 14}}, {{TQEType::ZZ, 11, 13}, {1, 15}}, - {{TQEType::XX, 10, 15}, {15, 10}}, {{TQEType::XY, 10, 15}, {10, 0}}, - {{TQEType::XZ, 10, 15}, {15, 5}}, {{TQEType::YX, 10, 15}, {5, 10}}, - {{TQEType::YY, 10, 15}, {10, 0}}, {{TQEType::YZ, 10, 15}, {5, 5}}, - {{TQEType::ZX, 10, 15}, {0, 15}}, {{TQEType::ZY, 10, 15}, {10, 15}}, - {{TQEType::ZZ, 10, 15}, {0, 15}}, {{TQEType::XX, 8, 15}, {13, 11}}, - {{TQEType::XY, 8, 15}, {8, 3}}, {{TQEType::XZ, 8, 15}, {13, 7}}, - {{TQEType::YX, 8, 15}, {7, 11}}, {{TQEType::YY, 8, 15}, {8, 3}}, - {{TQEType::YZ, 8, 15}, {7, 7}}, {{TQEType::ZX, 8, 15}, {2, 15}}, - {{TQEType::ZY, 8, 15}, {8, 15}}, {{TQEType::ZZ, 8, 15}, {2, 15}}, - {{TQEType::XX, 9, 14}, {12, 10}}, {{TQEType::XY, 9, 14}, {8, 2}}, - {{TQEType::XZ, 9, 14}, {13, 6}}, {{TQEType::YX, 9, 14}, {6, 11}}, - {{TQEType::YY, 9, 14}, {10, 1}}, {{TQEType::YZ, 9, 14}, {5, 4}}, - {{TQEType::ZX, 9, 14}, {3, 15}}, {{TQEType::ZY, 9, 14}, {11, 13}}, - {{TQEType::ZZ, 9, 14}, {1, 12}}, {{TQEType::XX, 11, 14}, {14, 11}}, - {{TQEType::XY, 11, 14}, {10, 1}}, {{TQEType::XZ, 11, 14}, {15, 4}}, - {{TQEType::YX, 11, 14}, {4, 10}}, {{TQEType::YY, 11, 14}, {8, 2}}, - {{TQEType::YZ, 11, 14}, {7, 6}}, {{TQEType::ZX, 11, 14}, {1, 15}}, - {{TQEType::ZY, 11, 14}, {9, 13}}, {{TQEType::ZZ, 11, 14}, {3, 12}}, - {{TQEType::XX, 10, 12}, {14, 9}}, {{TQEType::XY, 10, 12}, {10, 3}}, - {{TQEType::XZ, 10, 12}, {14, 6}}, {{TQEType::YX, 10, 12}, {6, 9}}, - {{TQEType::YY, 10, 12}, {10, 3}}, {{TQEType::YZ, 10, 12}, {6, 6}}, - {{TQEType::ZX, 10, 12}, {2, 12}}, {{TQEType::ZY, 10, 12}, {10, 12}}, - {{TQEType::ZZ, 10, 12}, {2, 12}}, {{TQEType::XX, 8, 12}, {12, 8}}, - {{TQEType::XY, 8, 12}, {8, 0}}, {{TQEType::XZ, 8, 12}, {12, 4}}, - {{TQEType::YX, 8, 12}, {4, 8}}, {{TQEType::YY, 8, 12}, {8, 0}}, - {{TQEType::YZ, 8, 12}, {4, 4}}, {{TQEType::ZX, 8, 12}, {0, 12}}, - {{TQEType::ZY, 8, 12}, {8, 12}}, {{TQEType::ZZ, 8, 12}, {0, 12}}, - {{TQEType::XX, 1, 13}, {5, 13}}, {{TQEType::XY, 1, 13}, {0, 13}}, - {{TQEType::XZ, 1, 13}, {4, 13}}, {{TQEType::YX, 1, 13}, {13, 12}}, - {{TQEType::YY, 1, 13}, {2, 14}}, {{TQEType::YZ, 1, 13}, {14, 15}}, - {{TQEType::ZX, 1, 13}, {9, 12}}, {{TQEType::ZY, 1, 13}, {3, 14}}, - {{TQEType::ZZ, 1, 13}, {11, 15}}, {{TQEType::XX, 3, 13}, {7, 12}}, - {{TQEType::XY, 3, 13}, {2, 14}}, {{TQEType::XZ, 3, 13}, {6, 15}}, - {{TQEType::YX, 3, 13}, {15, 13}}, {{TQEType::YY, 3, 13}, {0, 13}}, - {{TQEType::YZ, 3, 13}, {12, 13}}, {{TQEType::ZX, 3, 13}, {11, 12}}, - {{TQEType::ZY, 3, 13}, {1, 14}}, {{TQEType::ZZ, 3, 13}, {9, 15}}, - {{TQEType::XX, 2, 13}, {6, 12}}, {{TQEType::XY, 2, 13}, {3, 14}}, - {{TQEType::XZ, 2, 13}, {7, 15}}, {{TQEType::YX, 2, 13}, {14, 12}}, - {{TQEType::YY, 2, 13}, {1, 14}}, {{TQEType::YZ, 2, 13}, {13, 15}}, - {{TQEType::ZX, 2, 13}, {10, 13}}, {{TQEType::ZY, 2, 13}, {0, 13}}, - {{TQEType::ZZ, 2, 13}, {8, 13}}, {{TQEType::XX, 0, 13}, {4, 13}}, - {{TQEType::XY, 0, 13}, {1, 13}}, {{TQEType::XZ, 0, 13}, {5, 13}}, - {{TQEType::YX, 0, 13}, {12, 13}}, {{TQEType::YY, 0, 13}, {3, 13}}, - {{TQEType::YZ, 0, 13}, {15, 13}}, {{TQEType::ZX, 0, 13}, {8, 13}}, - {{TQEType::ZY, 0, 13}, {2, 13}}, {{TQEType::ZZ, 0, 13}, {10, 13}}, - {{TQEType::XX, 1, 14}, {4, 14}}, {{TQEType::XY, 1, 14}, {0, 14}}, - {{TQEType::XZ, 1, 14}, {5, 14}}, {{TQEType::YX, 1, 14}, {14, 15}}, - {{TQEType::YY, 1, 14}, {2, 13}}, {{TQEType::YZ, 1, 14}, {13, 12}}, - {{TQEType::ZX, 1, 14}, {11, 15}}, {{TQEType::ZY, 1, 14}, {3, 13}}, - {{TQEType::ZZ, 1, 14}, {9, 12}}, {{TQEType::XX, 3, 14}, {6, 15}}, - {{TQEType::XY, 3, 14}, {2, 13}}, {{TQEType::XZ, 3, 14}, {7, 12}}, - {{TQEType::YX, 3, 14}, {12, 14}}, {{TQEType::YY, 3, 14}, {0, 14}}, - {{TQEType::YZ, 3, 14}, {15, 14}}, {{TQEType::ZX, 3, 14}, {9, 15}}, - {{TQEType::ZY, 3, 14}, {1, 13}}, {{TQEType::ZZ, 3, 14}, {11, 12}}, - {{TQEType::XX, 2, 14}, {7, 15}}, {{TQEType::XY, 2, 14}, {3, 13}}, - {{TQEType::XZ, 2, 14}, {6, 12}}, {{TQEType::YX, 2, 14}, {13, 15}}, - {{TQEType::YY, 2, 14}, {1, 13}}, {{TQEType::YZ, 2, 14}, {14, 12}}, - {{TQEType::ZX, 2, 14}, {8, 14}}, {{TQEType::ZY, 2, 14}, {0, 14}}, - {{TQEType::ZZ, 2, 14}, {10, 14}}, {{TQEType::XX, 0, 14}, {5, 14}}, - {{TQEType::XY, 0, 14}, {1, 14}}, {{TQEType::XZ, 0, 14}, {4, 14}}, - {{TQEType::YX, 0, 14}, {15, 14}}, {{TQEType::YY, 0, 14}, {3, 14}}, - {{TQEType::YZ, 0, 14}, {12, 14}}, {{TQEType::ZX, 0, 14}, {10, 14}}, - {{TQEType::ZY, 0, 14}, {2, 14}}, {{TQEType::ZZ, 0, 14}, {8, 14}}, - {{TQEType::XX, 1, 15}, {4, 15}}, {{TQEType::XY, 1, 15}, {1, 15}}, - {{TQEType::XZ, 1, 15}, {4, 15}}, {{TQEType::YX, 1, 15}, {14, 14}}, - {{TQEType::YY, 1, 15}, {1, 12}}, {{TQEType::YZ, 1, 15}, {14, 13}}, - {{TQEType::ZX, 1, 15}, {11, 14}}, {{TQEType::ZY, 1, 15}, {1, 12}}, - {{TQEType::ZZ, 1, 15}, {11, 13}}, {{TQEType::XX, 3, 15}, {6, 14}}, - {{TQEType::XY, 3, 15}, {3, 12}}, {{TQEType::XZ, 3, 15}, {6, 13}}, - {{TQEType::YX, 3, 15}, {12, 15}}, {{TQEType::YY, 3, 15}, {3, 15}}, - {{TQEType::YZ, 3, 15}, {12, 15}}, {{TQEType::ZX, 3, 15}, {9, 14}}, - {{TQEType::ZY, 3, 15}, {3, 12}}, {{TQEType::ZZ, 3, 15}, {9, 13}}, - {{TQEType::XX, 2, 15}, {7, 14}}, {{TQEType::XY, 2, 15}, {2, 12}}, - {{TQEType::XZ, 2, 15}, {7, 13}}, {{TQEType::YX, 2, 15}, {13, 14}}, - {{TQEType::YY, 2, 15}, {2, 12}}, {{TQEType::YZ, 2, 15}, {13, 13}}, - {{TQEType::ZX, 2, 15}, {8, 15}}, {{TQEType::ZY, 2, 15}, {2, 15}}, - {{TQEType::ZZ, 2, 15}, {8, 15}}, {{TQEType::XX, 0, 15}, {5, 15}}, - {{TQEType::XY, 0, 15}, {0, 15}}, {{TQEType::XZ, 0, 15}, {5, 15}}, - {{TQEType::YX, 0, 15}, {15, 15}}, {{TQEType::YY, 0, 15}, {0, 15}}, - {{TQEType::YZ, 0, 15}, {15, 15}}, {{TQEType::ZX, 0, 15}, {10, 15}}, - {{TQEType::ZY, 0, 15}, {0, 15}}, {{TQEType::ZZ, 0, 15}, {10, 15}}, - {{TQEType::XX, 1, 12}, {5, 12}}, {{TQEType::XY, 1, 12}, {1, 12}}, - {{TQEType::XZ, 1, 12}, {5, 12}}, {{TQEType::YX, 1, 12}, {13, 13}}, - {{TQEType::YY, 1, 12}, {1, 15}}, {{TQEType::YZ, 1, 12}, {13, 14}}, - {{TQEType::ZX, 1, 12}, {9, 13}}, {{TQEType::ZY, 1, 12}, {1, 15}}, - {{TQEType::ZZ, 1, 12}, {9, 14}}, {{TQEType::XX, 3, 12}, {7, 13}}, - {{TQEType::XY, 3, 12}, {3, 15}}, {{TQEType::XZ, 3, 12}, {7, 14}}, - {{TQEType::YX, 3, 12}, {15, 12}}, {{TQEType::YY, 3, 12}, {3, 12}}, - {{TQEType::YZ, 3, 12}, {15, 12}}, {{TQEType::ZX, 3, 12}, {11, 13}}, - {{TQEType::ZY, 3, 12}, {3, 15}}, {{TQEType::ZZ, 3, 12}, {11, 14}}, - {{TQEType::XX, 2, 12}, {6, 13}}, {{TQEType::XY, 2, 12}, {2, 15}}, - {{TQEType::XZ, 2, 12}, {6, 14}}, {{TQEType::YX, 2, 12}, {14, 13}}, - {{TQEType::YY, 2, 12}, {2, 15}}, {{TQEType::YZ, 2, 12}, {14, 14}}, - {{TQEType::ZX, 2, 12}, {10, 12}}, {{TQEType::ZY, 2, 12}, {2, 12}}, - {{TQEType::ZZ, 2, 12}, {10, 12}}, {{TQEType::XX, 0, 12}, {4, 12}}, - {{TQEType::XY, 0, 12}, {0, 12}}, {{TQEType::XZ, 0, 12}, {4, 12}}, - {{TQEType::YX, 0, 12}, {12, 12}}, {{TQEType::YY, 0, 12}, {0, 12}}, - {{TQEType::YZ, 0, 12}, {12, 12}}, {{TQEType::ZX, 0, 12}, {8, 12}}, - {{TQEType::ZY, 0, 12}, {0, 12}}, {{TQEType::ZZ, 0, 12}, {8, 12}}, - {{TQEType::XX, 5, 9}, {1, 9}}, {{TQEType::XY, 5, 9}, {0, 9}}, - {{TQEType::XZ, 5, 9}, {4, 9}}, {{TQEType::YX, 5, 9}, {9, 12}}, - {{TQEType::YY, 5, 9}, {10, 6}}, {{TQEType::YZ, 5, 9}, {6, 3}}, - {{TQEType::ZX, 5, 9}, {13, 12}}, {{TQEType::ZY, 5, 9}, {15, 6}}, - {{TQEType::ZZ, 5, 9}, {7, 3}}, {{TQEType::XX, 4, 9}, {0, 9}}, - {{TQEType::XY, 4, 9}, {1, 9}}, {{TQEType::XZ, 4, 9}, {5, 9}}, - {{TQEType::YX, 4, 9}, {8, 13}}, {{TQEType::YY, 4, 9}, {11, 5}}, - {{TQEType::YZ, 4, 9}, {7, 1}}, {{TQEType::ZX, 4, 9}, {12, 13}}, - {{TQEType::ZY, 4, 9}, {14, 5}}, {{TQEType::ZZ, 4, 9}, {6, 1}}, - {{TQEType::XX, 5, 11}, {0, 11}}, {{TQEType::XY, 5, 11}, {1, 11}}, - {{TQEType::XZ, 5, 11}, {4, 11}}, {{TQEType::YX, 5, 11}, {10, 14}}, - {{TQEType::YY, 5, 11}, {9, 4}}, {{TQEType::YZ, 5, 11}, {6, 1}}, - {{TQEType::ZX, 5, 11}, {15, 14}}, {{TQEType::ZY, 5, 11}, {13, 4}}, - {{TQEType::ZZ, 5, 11}, {7, 1}}, {{TQEType::XX, 4, 11}, {1, 11}}, - {{TQEType::XY, 4, 11}, {0, 11}}, {{TQEType::XZ, 4, 11}, {5, 11}}, - {{TQEType::YX, 4, 11}, {11, 15}}, {{TQEType::YY, 4, 11}, {8, 7}}, - {{TQEType::YZ, 4, 11}, {7, 3}}, {{TQEType::ZX, 4, 11}, {14, 15}}, - {{TQEType::ZY, 4, 11}, {12, 7}}, {{TQEType::ZZ, 4, 11}, {6, 3}}, - {{TQEType::XX, 7, 10}, {2, 11}}, {{TQEType::XY, 7, 10}, {2, 9}}, - {{TQEType::XZ, 7, 10}, {7, 8}}, {{TQEType::YX, 7, 10}, {8, 14}}, - {{TQEType::YY, 7, 10}, {8, 6}}, {{TQEType::YZ, 7, 10}, {7, 2}}, - {{TQEType::ZX, 7, 10}, {13, 15}}, {{TQEType::ZY, 7, 10}, {13, 5}}, - {{TQEType::ZZ, 7, 10}, {7, 0}}, {{TQEType::XX, 6, 10}, {3, 11}}, - {{TQEType::XY, 6, 10}, {3, 9}}, {{TQEType::XZ, 6, 10}, {6, 8}}, - {{TQEType::YX, 6, 10}, {9, 15}}, {{TQEType::YY, 6, 10}, {9, 5}}, - {{TQEType::YZ, 6, 10}, {6, 0}}, {{TQEType::ZX, 6, 10}, {12, 14}}, - {{TQEType::ZY, 6, 10}, {12, 6}}, {{TQEType::ZZ, 6, 10}, {6, 2}}, - {{TQEType::XX, 7, 8}, {3, 9}}, {{TQEType::XY, 7, 8}, {3, 11}}, - {{TQEType::XZ, 7, 8}, {7, 10}}, {{TQEType::YX, 7, 8}, {11, 12}}, - {{TQEType::YY, 7, 8}, {11, 4}}, {{TQEType::YZ, 7, 8}, {7, 0}}, - {{TQEType::ZX, 7, 8}, {15, 13}}, {{TQEType::ZY, 7, 8}, {15, 7}}, - {{TQEType::ZZ, 7, 8}, {7, 2}}, {{TQEType::XX, 6, 8}, {2, 9}}, - {{TQEType::XY, 6, 8}, {2, 11}}, {{TQEType::XZ, 6, 8}, {6, 10}}, - {{TQEType::YX, 6, 8}, {10, 13}}, {{TQEType::YY, 6, 8}, {10, 7}}, - {{TQEType::YZ, 6, 8}, {6, 2}}, {{TQEType::ZX, 6, 8}, {14, 12}}, - {{TQEType::ZY, 6, 8}, {14, 4}}, {{TQEType::ZZ, 6, 8}, {6, 0}}, - {{TQEType::XX, 7, 9}, {3, 8}}, {{TQEType::XY, 7, 9}, {2, 10}}, - {{TQEType::XZ, 7, 9}, {6, 11}}, {{TQEType::YX, 7, 9}, {11, 13}}, - {{TQEType::YY, 7, 9}, {8, 5}}, {{TQEType::YZ, 7, 9}, {4, 1}}, - {{TQEType::ZX, 7, 9}, {15, 12}}, {{TQEType::ZY, 7, 9}, {13, 6}}, - {{TQEType::ZZ, 7, 9}, {5, 3}}, {{TQEType::XX, 6, 9}, {2, 8}}, - {{TQEType::XY, 6, 9}, {3, 10}}, {{TQEType::XZ, 6, 9}, {7, 11}}, - {{TQEType::YX, 6, 9}, {10, 12}}, {{TQEType::YY, 6, 9}, {9, 6}}, - {{TQEType::YZ, 6, 9}, {5, 3}}, {{TQEType::ZX, 6, 9}, {14, 13}}, - {{TQEType::ZY, 6, 9}, {12, 5}}, {{TQEType::ZZ, 6, 9}, {4, 1}}, - {{TQEType::XX, 7, 11}, {2, 10}}, {{TQEType::XY, 7, 11}, {3, 8}}, - {{TQEType::XZ, 7, 11}, {6, 9}}, {{TQEType::YX, 7, 11}, {8, 15}}, - {{TQEType::YY, 7, 11}, {11, 7}}, {{TQEType::YZ, 7, 11}, {4, 3}}, - {{TQEType::ZX, 7, 11}, {13, 14}}, {{TQEType::ZY, 7, 11}, {15, 4}}, - {{TQEType::ZZ, 7, 11}, {5, 1}}, {{TQEType::XX, 6, 11}, {3, 10}}, - {{TQEType::XY, 6, 11}, {2, 8}}, {{TQEType::XZ, 6, 11}, {7, 9}}, - {{TQEType::YX, 6, 11}, {9, 14}}, {{TQEType::YY, 6, 11}, {10, 4}}, - {{TQEType::YZ, 6, 11}, {5, 1}}, {{TQEType::ZX, 6, 11}, {12, 15}}, - {{TQEType::ZY, 6, 11}, {14, 7}}, {{TQEType::ZZ, 6, 11}, {4, 3}}, - {{TQEType::XX, 5, 10}, {0, 10}}, {{TQEType::XY, 5, 10}, {0, 10}}, - {{TQEType::XZ, 5, 10}, {5, 10}}, {{TQEType::YX, 5, 10}, {10, 15}}, - {{TQEType::YY, 5, 10}, {10, 5}}, {{TQEType::YZ, 5, 10}, {5, 0}}, - {{TQEType::ZX, 5, 10}, {15, 15}}, {{TQEType::ZY, 5, 10}, {15, 5}}, - {{TQEType::ZZ, 5, 10}, {5, 0}}, {{TQEType::XX, 4, 10}, {1, 10}}, - {{TQEType::XY, 4, 10}, {1, 10}}, {{TQEType::XZ, 4, 10}, {4, 10}}, - {{TQEType::YX, 4, 10}, {11, 14}}, {{TQEType::YY, 4, 10}, {11, 6}}, - {{TQEType::YZ, 4, 10}, {4, 2}}, {{TQEType::ZX, 4, 10}, {14, 14}}, - {{TQEType::ZY, 4, 10}, {14, 6}}, {{TQEType::ZZ, 4, 10}, {4, 2}}, - {{TQEType::XX, 5, 8}, {1, 8}}, {{TQEType::XY, 5, 8}, {1, 8}}, - {{TQEType::XZ, 5, 8}, {5, 8}}, {{TQEType::YX, 5, 8}, {9, 13}}, - {{TQEType::YY, 5, 8}, {9, 7}}, {{TQEType::YZ, 5, 8}, {5, 2}}, - {{TQEType::ZX, 5, 8}, {13, 13}}, {{TQEType::ZY, 5, 8}, {13, 7}}, - {{TQEType::ZZ, 5, 8}, {5, 2}}, {{TQEType::XX, 4, 8}, {0, 8}}, - {{TQEType::XY, 4, 8}, {0, 8}}, {{TQEType::XZ, 4, 8}, {4, 8}}, - {{TQEType::YX, 4, 8}, {8, 12}}, {{TQEType::YY, 4, 8}, {8, 4}}, - {{TQEType::YZ, 4, 8}, {4, 0}}, {{TQEType::ZX, 4, 8}, {12, 12}}, - {{TQEType::ZY, 4, 8}, {12, 4}}, {{TQEType::ZZ, 4, 8}, {4, 0}}, - {{TQEType::XX, 15, 9}, {11, 12}}, {{TQEType::XY, 15, 9}, {10, 6}}, - {{TQEType::XZ, 15, 9}, {14, 3}}, {{TQEType::YX, 15, 9}, {3, 9}}, - {{TQEType::YY, 15, 9}, {0, 9}}, {{TQEType::YZ, 15, 9}, {12, 9}}, - {{TQEType::ZX, 15, 9}, {7, 12}}, {{TQEType::ZY, 15, 9}, {5, 6}}, - {{TQEType::ZZ, 15, 9}, {13, 3}}, {{TQEType::XX, 12, 9}, {8, 13}}, - {{TQEType::XY, 12, 9}, {9, 5}}, {{TQEType::XZ, 12, 9}, {13, 1}}, - {{TQEType::YX, 12, 9}, {0, 9}}, {{TQEType::YY, 12, 9}, {3, 9}}, - {{TQEType::YZ, 12, 9}, {15, 9}}, {{TQEType::ZX, 12, 9}, {4, 13}}, - {{TQEType::ZY, 12, 9}, {6, 5}}, {{TQEType::ZZ, 12, 9}, {14, 1}}, - {{TQEType::XX, 15, 11}, {10, 14}}, {{TQEType::XY, 15, 11}, {11, 4}}, - {{TQEType::XZ, 15, 11}, {14, 1}}, {{TQEType::YX, 15, 11}, {0, 11}}, - {{TQEType::YY, 15, 11}, {3, 11}}, {{TQEType::YZ, 15, 11}, {12, 11}}, - {{TQEType::ZX, 15, 11}, {5, 14}}, {{TQEType::ZY, 15, 11}, {7, 4}}, - {{TQEType::ZZ, 15, 11}, {13, 1}}, {{TQEType::XX, 12, 11}, {9, 15}}, - {{TQEType::XY, 12, 11}, {8, 7}}, {{TQEType::XZ, 12, 11}, {13, 3}}, - {{TQEType::YX, 12, 11}, {3, 11}}, {{TQEType::YY, 12, 11}, {0, 11}}, - {{TQEType::YZ, 12, 11}, {15, 11}}, {{TQEType::ZX, 12, 11}, {6, 15}}, - {{TQEType::ZY, 12, 11}, {4, 7}}, {{TQEType::ZZ, 12, 11}, {14, 3}}, - {{TQEType::XX, 13, 10}, {8, 14}}, {{TQEType::XY, 13, 10}, {8, 6}}, - {{TQEType::XZ, 13, 10}, {13, 2}}, {{TQEType::YX, 13, 10}, {2, 11}}, - {{TQEType::YY, 13, 10}, {2, 9}}, {{TQEType::YZ, 13, 10}, {13, 8}}, - {{TQEType::ZX, 13, 10}, {7, 15}}, {{TQEType::ZY, 13, 10}, {7, 5}}, - {{TQEType::ZZ, 13, 10}, {13, 0}}, {{TQEType::XX, 14, 10}, {11, 15}}, - {{TQEType::XY, 14, 10}, {11, 5}}, {{TQEType::XZ, 14, 10}, {14, 0}}, - {{TQEType::YX, 14, 10}, {1, 11}}, {{TQEType::YY, 14, 10}, {1, 9}}, - {{TQEType::YZ, 14, 10}, {14, 8}}, {{TQEType::ZX, 14, 10}, {4, 14}}, - {{TQEType::ZY, 14, 10}, {4, 6}}, {{TQEType::ZZ, 14, 10}, {14, 2}}, - {{TQEType::XX, 13, 8}, {9, 12}}, {{TQEType::XY, 13, 8}, {9, 4}}, - {{TQEType::XZ, 13, 8}, {13, 0}}, {{TQEType::YX, 13, 8}, {1, 9}}, - {{TQEType::YY, 13, 8}, {1, 11}}, {{TQEType::YZ, 13, 8}, {13, 10}}, - {{TQEType::ZX, 13, 8}, {5, 13}}, {{TQEType::ZY, 13, 8}, {5, 7}}, - {{TQEType::ZZ, 13, 8}, {13, 2}}, {{TQEType::XX, 14, 8}, {10, 13}}, - {{TQEType::XY, 14, 8}, {10, 7}}, {{TQEType::XZ, 14, 8}, {14, 2}}, - {{TQEType::YX, 14, 8}, {2, 9}}, {{TQEType::YY, 14, 8}, {2, 11}}, - {{TQEType::YZ, 14, 8}, {14, 10}}, {{TQEType::ZX, 14, 8}, {6, 12}}, - {{TQEType::ZY, 14, 8}, {6, 4}}, {{TQEType::ZZ, 14, 8}, {14, 0}}, - {{TQEType::XX, 13, 9}, {9, 13}}, {{TQEType::XY, 13, 9}, {8, 5}}, - {{TQEType::XZ, 13, 9}, {12, 1}}, {{TQEType::YX, 13, 9}, {1, 8}}, - {{TQEType::YY, 13, 9}, {2, 10}}, {{TQEType::YZ, 13, 9}, {14, 11}}, - {{TQEType::ZX, 13, 9}, {5, 12}}, {{TQEType::ZY, 13, 9}, {7, 6}}, - {{TQEType::ZZ, 13, 9}, {15, 3}}, {{TQEType::XX, 14, 9}, {10, 12}}, - {{TQEType::XY, 14, 9}, {11, 6}}, {{TQEType::XZ, 14, 9}, {15, 3}}, - {{TQEType::YX, 14, 9}, {2, 8}}, {{TQEType::YY, 14, 9}, {1, 10}}, - {{TQEType::YZ, 14, 9}, {13, 11}}, {{TQEType::ZX, 14, 9}, {6, 13}}, - {{TQEType::ZY, 14, 9}, {4, 5}}, {{TQEType::ZZ, 14, 9}, {12, 1}}, - {{TQEType::XX, 13, 11}, {8, 15}}, {{TQEType::XY, 13, 11}, {9, 7}}, - {{TQEType::XZ, 13, 11}, {12, 3}}, {{TQEType::YX, 13, 11}, {2, 10}}, - {{TQEType::YY, 13, 11}, {1, 8}}, {{TQEType::YZ, 13, 11}, {14, 9}}, - {{TQEType::ZX, 13, 11}, {7, 14}}, {{TQEType::ZY, 13, 11}, {5, 4}}, - {{TQEType::ZZ, 13, 11}, {15, 1}}, {{TQEType::XX, 14, 11}, {11, 14}}, - {{TQEType::XY, 14, 11}, {10, 4}}, {{TQEType::XZ, 14, 11}, {15, 1}}, - {{TQEType::YX, 14, 11}, {1, 10}}, {{TQEType::YY, 14, 11}, {2, 8}}, - {{TQEType::YZ, 14, 11}, {13, 9}}, {{TQEType::ZX, 14, 11}, {4, 15}}, - {{TQEType::ZY, 14, 11}, {6, 7}}, {{TQEType::ZZ, 14, 11}, {12, 3}}, - {{TQEType::XX, 15, 10}, {10, 15}}, {{TQEType::XY, 15, 10}, {10, 5}}, - {{TQEType::XZ, 15, 10}, {15, 0}}, {{TQEType::YX, 15, 10}, {0, 10}}, - {{TQEType::YY, 15, 10}, {0, 10}}, {{TQEType::YZ, 15, 10}, {15, 10}}, - {{TQEType::ZX, 15, 10}, {5, 15}}, {{TQEType::ZY, 15, 10}, {5, 5}}, - {{TQEType::ZZ, 15, 10}, {15, 0}}, {{TQEType::XX, 12, 10}, {9, 14}}, - {{TQEType::XY, 12, 10}, {9, 6}}, {{TQEType::XZ, 12, 10}, {12, 2}}, - {{TQEType::YX, 12, 10}, {3, 10}}, {{TQEType::YY, 12, 10}, {3, 10}}, - {{TQEType::YZ, 12, 10}, {12, 10}}, {{TQEType::ZX, 12, 10}, {6, 14}}, - {{TQEType::ZY, 12, 10}, {6, 6}}, {{TQEType::ZZ, 12, 10}, {12, 2}}, - {{TQEType::XX, 15, 8}, {11, 13}}, {{TQEType::XY, 15, 8}, {11, 7}}, - {{TQEType::XZ, 15, 8}, {15, 2}}, {{TQEType::YX, 15, 8}, {3, 8}}, - {{TQEType::YY, 15, 8}, {3, 8}}, {{TQEType::YZ, 15, 8}, {15, 8}}, - {{TQEType::ZX, 15, 8}, {7, 13}}, {{TQEType::ZY, 15, 8}, {7, 7}}, - {{TQEType::ZZ, 15, 8}, {15, 2}}, {{TQEType::XX, 12, 8}, {8, 12}}, - {{TQEType::XY, 12, 8}, {8, 4}}, {{TQEType::XZ, 12, 8}, {12, 0}}, - {{TQEType::YX, 12, 8}, {0, 8}}, {{TQEType::YY, 12, 8}, {0, 8}}, - {{TQEType::YZ, 12, 8}, {12, 8}}, {{TQEType::ZX, 12, 8}, {4, 12}}, - {{TQEType::ZY, 12, 8}, {4, 4}}, {{TQEType::ZZ, 12, 8}, {12, 0}}, - {{TQEType::XX, 10, 9}, {14, 12}}, {{TQEType::XY, 10, 9}, {15, 6}}, - {{TQEType::XZ, 10, 9}, {11, 3}}, {{TQEType::YX, 10, 9}, {6, 12}}, - {{TQEType::YY, 10, 9}, {5, 6}}, {{TQEType::YZ, 10, 9}, {9, 3}}, - {{TQEType::ZX, 10, 9}, {2, 9}}, {{TQEType::ZY, 10, 9}, {0, 9}}, - {{TQEType::ZZ, 10, 9}, {8, 9}}, {{TQEType::XX, 8, 9}, {12, 13}}, - {{TQEType::XY, 8, 9}, {13, 5}}, {{TQEType::XZ, 8, 9}, {9, 1}}, - {{TQEType::YX, 8, 9}, {4, 13}}, {{TQEType::YY, 8, 9}, {7, 5}}, - {{TQEType::YZ, 8, 9}, {11, 1}}, {{TQEType::ZX, 8, 9}, {0, 9}}, - {{TQEType::ZY, 8, 9}, {2, 9}}, {{TQEType::ZZ, 8, 9}, {10, 9}}, - {{TQEType::XX, 10, 11}, {15, 14}}, {{TQEType::XY, 10, 11}, {14, 4}}, - {{TQEType::XZ, 10, 11}, {11, 1}}, {{TQEType::YX, 10, 11}, {5, 14}}, - {{TQEType::YY, 10, 11}, {6, 4}}, {{TQEType::YZ, 10, 11}, {9, 1}}, - {{TQEType::ZX, 10, 11}, {0, 11}}, {{TQEType::ZY, 10, 11}, {2, 11}}, - {{TQEType::ZZ, 10, 11}, {8, 11}}, {{TQEType::XX, 8, 11}, {13, 15}}, - {{TQEType::XY, 8, 11}, {12, 7}}, {{TQEType::XZ, 8, 11}, {9, 3}}, - {{TQEType::YX, 8, 11}, {7, 15}}, {{TQEType::YY, 8, 11}, {4, 7}}, - {{TQEType::YZ, 8, 11}, {11, 3}}, {{TQEType::ZX, 8, 11}, {2, 11}}, - {{TQEType::ZY, 8, 11}, {0, 11}}, {{TQEType::ZZ, 8, 11}, {10, 11}}, - {{TQEType::XX, 9, 10}, {12, 14}}, {{TQEType::XY, 9, 10}, {12, 6}}, - {{TQEType::XZ, 9, 10}, {9, 2}}, {{TQEType::YX, 9, 10}, {6, 15}}, - {{TQEType::YY, 9, 10}, {6, 5}}, {{TQEType::YZ, 9, 10}, {9, 0}}, - {{TQEType::ZX, 9, 10}, {3, 11}}, {{TQEType::ZY, 9, 10}, {3, 9}}, - {{TQEType::ZZ, 9, 10}, {9, 8}}, {{TQEType::XX, 11, 10}, {14, 15}}, - {{TQEType::XY, 11, 10}, {14, 5}}, {{TQEType::XZ, 11, 10}, {11, 0}}, - {{TQEType::YX, 11, 10}, {4, 14}}, {{TQEType::YY, 11, 10}, {4, 6}}, - {{TQEType::YZ, 11, 10}, {11, 2}}, {{TQEType::ZX, 11, 10}, {1, 11}}, - {{TQEType::ZY, 11, 10}, {1, 9}}, {{TQEType::ZZ, 11, 10}, {11, 8}}, - {{TQEType::XX, 9, 8}, {13, 12}}, {{TQEType::XY, 9, 8}, {13, 4}}, - {{TQEType::XZ, 9, 8}, {9, 0}}, {{TQEType::YX, 9, 8}, {5, 13}}, - {{TQEType::YY, 9, 8}, {5, 7}}, {{TQEType::YZ, 9, 8}, {9, 2}}, - {{TQEType::ZX, 9, 8}, {1, 9}}, {{TQEType::ZY, 9, 8}, {1, 11}}, - {{TQEType::ZZ, 9, 8}, {9, 10}}, {{TQEType::XX, 11, 8}, {15, 13}}, - {{TQEType::XY, 11, 8}, {15, 7}}, {{TQEType::XZ, 11, 8}, {11, 2}}, - {{TQEType::YX, 11, 8}, {7, 12}}, {{TQEType::YY, 11, 8}, {7, 4}}, - {{TQEType::YZ, 11, 8}, {11, 0}}, {{TQEType::ZX, 11, 8}, {3, 9}}, - {{TQEType::ZY, 11, 8}, {3, 11}}, {{TQEType::ZZ, 11, 8}, {11, 10}}, - {{TQEType::XX, 9, 9}, {13, 13}}, {{TQEType::XY, 9, 9}, {12, 5}}, - {{TQEType::XZ, 9, 9}, {8, 1}}, {{TQEType::YX, 9, 9}, {5, 12}}, - {{TQEType::YY, 9, 9}, {6, 6}}, {{TQEType::YZ, 9, 9}, {10, 3}}, - {{TQEType::ZX, 9, 9}, {1, 8}}, {{TQEType::ZY, 9, 9}, {3, 10}}, - {{TQEType::ZZ, 9, 9}, {11, 11}}, {{TQEType::XX, 11, 9}, {15, 12}}, - {{TQEType::XY, 11, 9}, {14, 6}}, {{TQEType::XZ, 11, 9}, {10, 3}}, - {{TQEType::YX, 11, 9}, {7, 13}}, {{TQEType::YY, 11, 9}, {4, 5}}, - {{TQEType::YZ, 11, 9}, {8, 1}}, {{TQEType::ZX, 11, 9}, {3, 8}}, - {{TQEType::ZY, 11, 9}, {1, 10}}, {{TQEType::ZZ, 11, 9}, {9, 11}}, - {{TQEType::XX, 9, 11}, {12, 15}}, {{TQEType::XY, 9, 11}, {13, 7}}, - {{TQEType::XZ, 9, 11}, {8, 3}}, {{TQEType::YX, 9, 11}, {6, 14}}, - {{TQEType::YY, 9, 11}, {5, 4}}, {{TQEType::YZ, 9, 11}, {10, 1}}, - {{TQEType::ZX, 9, 11}, {3, 10}}, {{TQEType::ZY, 9, 11}, {1, 8}}, - {{TQEType::ZZ, 9, 11}, {11, 9}}, {{TQEType::XX, 11, 11}, {14, 14}}, - {{TQEType::XY, 11, 11}, {15, 4}}, {{TQEType::XZ, 11, 11}, {10, 1}}, - {{TQEType::YX, 11, 11}, {4, 15}}, {{TQEType::YY, 11, 11}, {7, 7}}, - {{TQEType::YZ, 11, 11}, {8, 3}}, {{TQEType::ZX, 11, 11}, {1, 10}}, - {{TQEType::ZY, 11, 11}, {3, 8}}, {{TQEType::ZZ, 11, 11}, {9, 9}}, - {{TQEType::XX, 10, 10}, {15, 15}}, {{TQEType::XY, 10, 10}, {15, 5}}, - {{TQEType::XZ, 10, 10}, {10, 0}}, {{TQEType::YX, 10, 10}, {5, 15}}, - {{TQEType::YY, 10, 10}, {5, 5}}, {{TQEType::YZ, 10, 10}, {10, 0}}, - {{TQEType::ZX, 10, 10}, {0, 10}}, {{TQEType::ZY, 10, 10}, {0, 10}}, - {{TQEType::ZZ, 10, 10}, {10, 10}}, {{TQEType::XX, 8, 10}, {13, 14}}, - {{TQEType::XY, 8, 10}, {13, 6}}, {{TQEType::XZ, 8, 10}, {8, 2}}, - {{TQEType::YX, 8, 10}, {7, 14}}, {{TQEType::YY, 8, 10}, {7, 6}}, - {{TQEType::YZ, 8, 10}, {8, 2}}, {{TQEType::ZX, 8, 10}, {2, 10}}, - {{TQEType::ZY, 8, 10}, {2, 10}}, {{TQEType::ZZ, 8, 10}, {8, 10}}, - {{TQEType::XX, 10, 8}, {14, 13}}, {{TQEType::XY, 10, 8}, {14, 7}}, - {{TQEType::XZ, 10, 8}, {10, 2}}, {{TQEType::YX, 10, 8}, {6, 13}}, - {{TQEType::YY, 10, 8}, {6, 7}}, {{TQEType::YZ, 10, 8}, {10, 2}}, - {{TQEType::ZX, 10, 8}, {2, 8}}, {{TQEType::ZY, 10, 8}, {2, 8}}, - {{TQEType::ZZ, 10, 8}, {10, 8}}, {{TQEType::XX, 8, 8}, {12, 12}}, - {{TQEType::XY, 8, 8}, {12, 4}}, {{TQEType::XZ, 8, 8}, {8, 0}}, - {{TQEType::YX, 8, 8}, {4, 12}}, {{TQEType::YY, 8, 8}, {4, 4}}, - {{TQEType::YZ, 8, 8}, {8, 0}}, {{TQEType::ZX, 8, 8}, {0, 8}}, - {{TQEType::ZY, 8, 8}, {0, 8}}, {{TQEType::ZZ, 8, 8}, {8, 8}}, - {{TQEType::XX, 1, 9}, {5, 9}}, {{TQEType::XY, 1, 9}, {4, 9}}, - {{TQEType::XZ, 1, 9}, {0, 9}}, {{TQEType::YX, 1, 9}, {13, 8}}, - {{TQEType::YY, 1, 9}, {14, 10}}, {{TQEType::YZ, 1, 9}, {2, 11}}, - {{TQEType::ZX, 1, 9}, {9, 8}}, {{TQEType::ZY, 1, 9}, {11, 10}}, - {{TQEType::ZZ, 1, 9}, {3, 11}}, {{TQEType::XX, 3, 9}, {7, 8}}, - {{TQEType::XY, 3, 9}, {6, 10}}, {{TQEType::XZ, 3, 9}, {2, 11}}, - {{TQEType::YX, 3, 9}, {15, 9}}, {{TQEType::YY, 3, 9}, {12, 9}}, - {{TQEType::YZ, 3, 9}, {0, 9}}, {{TQEType::ZX, 3, 9}, {11, 8}}, - {{TQEType::ZY, 3, 9}, {9, 10}}, {{TQEType::ZZ, 3, 9}, {1, 11}}, - {{TQEType::XX, 2, 9}, {6, 8}}, {{TQEType::XY, 2, 9}, {7, 10}}, - {{TQEType::XZ, 2, 9}, {3, 11}}, {{TQEType::YX, 2, 9}, {14, 8}}, - {{TQEType::YY, 2, 9}, {13, 10}}, {{TQEType::YZ, 2, 9}, {1, 11}}, - {{TQEType::ZX, 2, 9}, {10, 9}}, {{TQEType::ZY, 2, 9}, {8, 9}}, - {{TQEType::ZZ, 2, 9}, {0, 9}}, {{TQEType::XX, 0, 9}, {4, 9}}, - {{TQEType::XY, 0, 9}, {5, 9}}, {{TQEType::XZ, 0, 9}, {1, 9}}, - {{TQEType::YX, 0, 9}, {12, 9}}, {{TQEType::YY, 0, 9}, {15, 9}}, - {{TQEType::YZ, 0, 9}, {3, 9}}, {{TQEType::ZX, 0, 9}, {8, 9}}, - {{TQEType::ZY, 0, 9}, {10, 9}}, {{TQEType::ZZ, 0, 9}, {2, 9}}, - {{TQEType::XX, 1, 11}, {4, 11}}, {{TQEType::XY, 1, 11}, {5, 11}}, - {{TQEType::XZ, 1, 11}, {0, 11}}, {{TQEType::YX, 1, 11}, {14, 10}}, - {{TQEType::YY, 1, 11}, {13, 8}}, {{TQEType::YZ, 1, 11}, {2, 9}}, - {{TQEType::ZX, 1, 11}, {11, 10}}, {{TQEType::ZY, 1, 11}, {9, 8}}, - {{TQEType::ZZ, 1, 11}, {3, 9}}, {{TQEType::XX, 3, 11}, {6, 10}}, - {{TQEType::XY, 3, 11}, {7, 8}}, {{TQEType::XZ, 3, 11}, {2, 9}}, - {{TQEType::YX, 3, 11}, {12, 11}}, {{TQEType::YY, 3, 11}, {15, 11}}, - {{TQEType::YZ, 3, 11}, {0, 11}}, {{TQEType::ZX, 3, 11}, {9, 10}}, - {{TQEType::ZY, 3, 11}, {11, 8}}, {{TQEType::ZZ, 3, 11}, {1, 9}}, - {{TQEType::XX, 2, 11}, {7, 10}}, {{TQEType::XY, 2, 11}, {6, 8}}, - {{TQEType::XZ, 2, 11}, {3, 9}}, {{TQEType::YX, 2, 11}, {13, 10}}, - {{TQEType::YY, 2, 11}, {14, 8}}, {{TQEType::YZ, 2, 11}, {1, 9}}, - {{TQEType::ZX, 2, 11}, {8, 11}}, {{TQEType::ZY, 2, 11}, {10, 11}}, - {{TQEType::ZZ, 2, 11}, {0, 11}}, {{TQEType::XX, 0, 11}, {5, 11}}, - {{TQEType::XY, 0, 11}, {4, 11}}, {{TQEType::XZ, 0, 11}, {1, 11}}, - {{TQEType::YX, 0, 11}, {15, 11}}, {{TQEType::YY, 0, 11}, {12, 11}}, - {{TQEType::YZ, 0, 11}, {3, 11}}, {{TQEType::ZX, 0, 11}, {10, 11}}, - {{TQEType::ZY, 0, 11}, {8, 11}}, {{TQEType::ZZ, 0, 11}, {2, 11}}, - {{TQEType::XX, 1, 10}, {4, 10}}, {{TQEType::XY, 1, 10}, {4, 10}}, - {{TQEType::XZ, 1, 10}, {1, 10}}, {{TQEType::YX, 1, 10}, {14, 11}}, - {{TQEType::YY, 1, 10}, {14, 9}}, {{TQEType::YZ, 1, 10}, {1, 8}}, - {{TQEType::ZX, 1, 10}, {11, 11}}, {{TQEType::ZY, 1, 10}, {11, 9}}, - {{TQEType::ZZ, 1, 10}, {1, 8}}, {{TQEType::XX, 3, 10}, {6, 11}}, - {{TQEType::XY, 3, 10}, {6, 9}}, {{TQEType::XZ, 3, 10}, {3, 8}}, - {{TQEType::YX, 3, 10}, {12, 10}}, {{TQEType::YY, 3, 10}, {12, 10}}, - {{TQEType::YZ, 3, 10}, {3, 10}}, {{TQEType::ZX, 3, 10}, {9, 11}}, - {{TQEType::ZY, 3, 10}, {9, 9}}, {{TQEType::ZZ, 3, 10}, {3, 8}}, - {{TQEType::XX, 2, 10}, {7, 11}}, {{TQEType::XY, 2, 10}, {7, 9}}, - {{TQEType::XZ, 2, 10}, {2, 8}}, {{TQEType::YX, 2, 10}, {13, 11}}, - {{TQEType::YY, 2, 10}, {13, 9}}, {{TQEType::YZ, 2, 10}, {2, 8}}, - {{TQEType::ZX, 2, 10}, {8, 10}}, {{TQEType::ZY, 2, 10}, {8, 10}}, - {{TQEType::ZZ, 2, 10}, {2, 10}}, {{TQEType::XX, 0, 10}, {5, 10}}, - {{TQEType::XY, 0, 10}, {5, 10}}, {{TQEType::XZ, 0, 10}, {0, 10}}, - {{TQEType::YX, 0, 10}, {15, 10}}, {{TQEType::YY, 0, 10}, {15, 10}}, - {{TQEType::YZ, 0, 10}, {0, 10}}, {{TQEType::ZX, 0, 10}, {10, 10}}, - {{TQEType::ZY, 0, 10}, {10, 10}}, {{TQEType::ZZ, 0, 10}, {0, 10}}, - {{TQEType::XX, 1, 8}, {5, 8}}, {{TQEType::XY, 1, 8}, {5, 8}}, - {{TQEType::XZ, 1, 8}, {1, 8}}, {{TQEType::YX, 1, 8}, {13, 9}}, - {{TQEType::YY, 1, 8}, {13, 11}}, {{TQEType::YZ, 1, 8}, {1, 10}}, - {{TQEType::ZX, 1, 8}, {9, 9}}, {{TQEType::ZY, 1, 8}, {9, 11}}, - {{TQEType::ZZ, 1, 8}, {1, 10}}, {{TQEType::XX, 3, 8}, {7, 9}}, - {{TQEType::XY, 3, 8}, {7, 11}}, {{TQEType::XZ, 3, 8}, {3, 10}}, - {{TQEType::YX, 3, 8}, {15, 8}}, {{TQEType::YY, 3, 8}, {15, 8}}, - {{TQEType::YZ, 3, 8}, {3, 8}}, {{TQEType::ZX, 3, 8}, {11, 9}}, - {{TQEType::ZY, 3, 8}, {11, 11}}, {{TQEType::ZZ, 3, 8}, {3, 10}}, - {{TQEType::XX, 2, 8}, {6, 9}}, {{TQEType::XY, 2, 8}, {6, 11}}, - {{TQEType::XZ, 2, 8}, {2, 10}}, {{TQEType::YX, 2, 8}, {14, 9}}, - {{TQEType::YY, 2, 8}, {14, 11}}, {{TQEType::YZ, 2, 8}, {2, 10}}, - {{TQEType::ZX, 2, 8}, {10, 8}}, {{TQEType::ZY, 2, 8}, {10, 8}}, - {{TQEType::ZZ, 2, 8}, {2, 8}}, {{TQEType::XX, 0, 8}, {4, 8}}, - {{TQEType::XY, 0, 8}, {4, 8}}, {{TQEType::XZ, 0, 8}, {0, 8}}, - {{TQEType::YX, 0, 8}, {12, 8}}, {{TQEType::YY, 0, 8}, {12, 8}}, - {{TQEType::YZ, 0, 8}, {0, 8}}, {{TQEType::ZX, 0, 8}, {8, 8}}, - {{TQEType::ZY, 0, 8}, {8, 8}}, {{TQEType::ZZ, 0, 8}, {0, 8}}, - {{TQEType::XX, 7, 1}, {7, 0}}, {{TQEType::XY, 7, 1}, {6, 2}}, - {{TQEType::XZ, 7, 1}, {6, 3}}, {{TQEType::YX, 7, 1}, {7, 5}}, - {{TQEType::YY, 7, 1}, {4, 13}}, {{TQEType::YZ, 7, 1}, {4, 9}}, - {{TQEType::ZX, 7, 1}, {7, 4}}, {{TQEType::ZY, 7, 1}, {5, 14}}, - {{TQEType::ZZ, 7, 1}, {5, 11}}, {{TQEType::XX, 6, 1}, {6, 0}}, - {{TQEType::XY, 6, 1}, {7, 2}}, {{TQEType::XZ, 6, 1}, {7, 3}}, - {{TQEType::YX, 6, 1}, {6, 4}}, {{TQEType::YY, 6, 1}, {5, 14}}, - {{TQEType::YZ, 6, 1}, {5, 11}}, {{TQEType::ZX, 6, 1}, {6, 5}}, - {{TQEType::ZY, 6, 1}, {4, 13}}, {{TQEType::ZZ, 6, 1}, {4, 9}}, - {{TQEType::XX, 7, 3}, {6, 2}}, {{TQEType::XY, 7, 3}, {7, 0}}, - {{TQEType::XZ, 7, 3}, {6, 1}}, {{TQEType::YX, 7, 3}, {4, 7}}, - {{TQEType::YY, 7, 3}, {7, 15}}, {{TQEType::YZ, 7, 3}, {4, 11}}, - {{TQEType::ZX, 7, 3}, {5, 6}}, {{TQEType::ZY, 7, 3}, {7, 12}}, - {{TQEType::ZZ, 7, 3}, {5, 9}}, {{TQEType::XX, 6, 3}, {7, 2}}, - {{TQEType::XY, 6, 3}, {6, 0}}, {{TQEType::XZ, 6, 3}, {7, 1}}, - {{TQEType::YX, 6, 3}, {5, 6}}, {{TQEType::YY, 6, 3}, {6, 12}}, - {{TQEType::YZ, 6, 3}, {5, 9}}, {{TQEType::ZX, 6, 3}, {4, 7}}, - {{TQEType::ZY, 6, 3}, {6, 15}}, {{TQEType::ZZ, 6, 3}, {4, 11}}, - {{TQEType::XX, 7, 2}, {6, 3}}, {{TQEType::XY, 7, 2}, {6, 1}}, - {{TQEType::XZ, 7, 2}, {7, 0}}, {{TQEType::YX, 7, 2}, {4, 6}}, - {{TQEType::YY, 7, 2}, {4, 14}}, {{TQEType::YZ, 7, 2}, {7, 10}}, - {{TQEType::ZX, 7, 2}, {5, 7}}, {{TQEType::ZY, 7, 2}, {5, 13}}, - {{TQEType::ZZ, 7, 2}, {7, 8}}, {{TQEType::XX, 6, 2}, {7, 3}}, - {{TQEType::XY, 6, 2}, {7, 1}}, {{TQEType::XZ, 6, 2}, {6, 0}}, - {{TQEType::YX, 6, 2}, {5, 7}}, {{TQEType::YY, 6, 2}, {5, 13}}, - {{TQEType::YZ, 6, 2}, {6, 8}}, {{TQEType::ZX, 6, 2}, {4, 6}}, - {{TQEType::ZY, 6, 2}, {4, 14}}, {{TQEType::ZZ, 6, 2}, {6, 10}}, - {{TQEType::XX, 7, 0}, {7, 1}}, {{TQEType::XY, 7, 0}, {7, 3}}, - {{TQEType::XZ, 7, 0}, {7, 2}}, {{TQEType::YX, 7, 0}, {7, 4}}, - {{TQEType::YY, 7, 0}, {7, 12}}, {{TQEType::YZ, 7, 0}, {7, 8}}, - {{TQEType::ZX, 7, 0}, {7, 5}}, {{TQEType::ZY, 7, 0}, {7, 15}}, - {{TQEType::ZZ, 7, 0}, {7, 10}}, {{TQEType::XX, 6, 0}, {6, 1}}, - {{TQEType::XY, 6, 0}, {6, 3}}, {{TQEType::XZ, 6, 0}, {6, 2}}, - {{TQEType::YX, 6, 0}, {6, 5}}, {{TQEType::YY, 6, 0}, {6, 15}}, - {{TQEType::YZ, 6, 0}, {6, 10}}, {{TQEType::ZX, 6, 0}, {6, 4}}, - {{TQEType::ZY, 6, 0}, {6, 12}}, {{TQEType::ZZ, 6, 0}, {6, 8}}, - {{TQEType::XX, 5, 1}, {5, 1}}, {{TQEType::XY, 5, 1}, {4, 1}}, - {{TQEType::XZ, 5, 1}, {4, 1}}, {{TQEType::YX, 5, 1}, {5, 4}}, - {{TQEType::YY, 5, 1}, {6, 14}}, {{TQEType::YZ, 5, 1}, {6, 11}}, - {{TQEType::ZX, 5, 1}, {5, 4}}, {{TQEType::ZY, 5, 1}, {7, 14}}, - {{TQEType::ZZ, 5, 1}, {7, 11}}, {{TQEType::XX, 4, 1}, {4, 1}}, - {{TQEType::XY, 4, 1}, {5, 1}}, {{TQEType::XZ, 4, 1}, {5, 1}}, - {{TQEType::YX, 4, 1}, {4, 5}}, {{TQEType::YY, 4, 1}, {7, 13}}, - {{TQEType::YZ, 4, 1}, {7, 9}}, {{TQEType::ZX, 4, 1}, {4, 5}}, - {{TQEType::ZY, 4, 1}, {6, 13}}, {{TQEType::ZZ, 4, 1}, {6, 9}}, - {{TQEType::XX, 5, 3}, {4, 3}}, {{TQEType::XY, 5, 3}, {5, 3}}, - {{TQEType::XZ, 5, 3}, {4, 3}}, {{TQEType::YX, 5, 3}, {6, 6}}, - {{TQEType::YY, 5, 3}, {5, 12}}, {{TQEType::YZ, 5, 3}, {6, 9}}, - {{TQEType::ZX, 5, 3}, {7, 6}}, {{TQEType::ZY, 5, 3}, {5, 12}}, - {{TQEType::ZZ, 5, 3}, {7, 9}}, {{TQEType::XX, 4, 3}, {5, 3}}, - {{TQEType::XY, 4, 3}, {4, 3}}, {{TQEType::XZ, 4, 3}, {5, 3}}, - {{TQEType::YX, 4, 3}, {7, 7}}, {{TQEType::YY, 4, 3}, {4, 15}}, - {{TQEType::YZ, 4, 3}, {7, 11}}, {{TQEType::ZX, 4, 3}, {6, 7}}, - {{TQEType::ZY, 4, 3}, {4, 15}}, {{TQEType::ZZ, 4, 3}, {6, 11}}, - {{TQEType::XX, 5, 2}, {4, 2}}, {{TQEType::XY, 5, 2}, {4, 2}}, - {{TQEType::XZ, 5, 2}, {5, 2}}, {{TQEType::YX, 5, 2}, {6, 7}}, - {{TQEType::YY, 5, 2}, {6, 13}}, {{TQEType::YZ, 5, 2}, {5, 8}}, - {{TQEType::ZX, 5, 2}, {7, 7}}, {{TQEType::ZY, 5, 2}, {7, 13}}, - {{TQEType::ZZ, 5, 2}, {5, 8}}, {{TQEType::XX, 4, 2}, {5, 2}}, - {{TQEType::XY, 4, 2}, {5, 2}}, {{TQEType::XZ, 4, 2}, {4, 2}}, - {{TQEType::YX, 4, 2}, {7, 6}}, {{TQEType::YY, 4, 2}, {7, 14}}, - {{TQEType::YZ, 4, 2}, {4, 10}}, {{TQEType::ZX, 4, 2}, {6, 6}}, - {{TQEType::ZY, 4, 2}, {6, 14}}, {{TQEType::ZZ, 4, 2}, {4, 10}}, - {{TQEType::XX, 5, 0}, {5, 0}}, {{TQEType::XY, 5, 0}, {5, 0}}, - {{TQEType::XZ, 5, 0}, {5, 0}}, {{TQEType::YX, 5, 0}, {5, 5}}, - {{TQEType::YY, 5, 0}, {5, 15}}, {{TQEType::YZ, 5, 0}, {5, 10}}, - {{TQEType::ZX, 5, 0}, {5, 5}}, {{TQEType::ZY, 5, 0}, {5, 15}}, - {{TQEType::ZZ, 5, 0}, {5, 10}}, {{TQEType::XX, 4, 0}, {4, 0}}, - {{TQEType::XY, 4, 0}, {4, 0}}, {{TQEType::XZ, 4, 0}, {4, 0}}, - {{TQEType::YX, 4, 0}, {4, 4}}, {{TQEType::YY, 4, 0}, {4, 12}}, - {{TQEType::YZ, 4, 0}, {4, 8}}, {{TQEType::ZX, 4, 0}, {4, 4}}, - {{TQEType::ZY, 4, 0}, {4, 12}}, {{TQEType::ZZ, 4, 0}, {4, 8}}, - {{TQEType::XX, 13, 1}, {13, 5}}, {{TQEType::XY, 13, 1}, {12, 13}}, - {{TQEType::XZ, 13, 1}, {12, 9}}, {{TQEType::YX, 13, 1}, {13, 0}}, - {{TQEType::YY, 13, 1}, {14, 2}}, {{TQEType::YZ, 13, 1}, {14, 3}}, - {{TQEType::ZX, 13, 1}, {13, 4}}, {{TQEType::ZY, 13, 1}, {15, 14}}, - {{TQEType::ZZ, 13, 1}, {15, 11}}, {{TQEType::XX, 14, 1}, {14, 4}}, - {{TQEType::XY, 14, 1}, {15, 14}}, {{TQEType::XZ, 14, 1}, {15, 11}}, - {{TQEType::YX, 14, 1}, {14, 0}}, {{TQEType::YY, 14, 1}, {13, 2}}, - {{TQEType::YZ, 14, 1}, {13, 3}}, {{TQEType::ZX, 14, 1}, {14, 5}}, - {{TQEType::ZY, 14, 1}, {12, 13}}, {{TQEType::ZZ, 14, 1}, {12, 9}}, - {{TQEType::XX, 13, 3}, {12, 7}}, {{TQEType::XY, 13, 3}, {13, 15}}, - {{TQEType::XZ, 13, 3}, {12, 11}}, {{TQEType::YX, 13, 3}, {14, 2}}, - {{TQEType::YY, 13, 3}, {13, 0}}, {{TQEType::YZ, 13, 3}, {14, 1}}, - {{TQEType::ZX, 13, 3}, {15, 6}}, {{TQEType::ZY, 13, 3}, {13, 12}}, - {{TQEType::ZZ, 13, 3}, {15, 9}}, {{TQEType::XX, 14, 3}, {15, 6}}, - {{TQEType::XY, 14, 3}, {14, 12}}, {{TQEType::XZ, 14, 3}, {15, 9}}, - {{TQEType::YX, 14, 3}, {13, 2}}, {{TQEType::YY, 14, 3}, {14, 0}}, - {{TQEType::YZ, 14, 3}, {13, 1}}, {{TQEType::ZX, 14, 3}, {12, 7}}, - {{TQEType::ZY, 14, 3}, {14, 15}}, {{TQEType::ZZ, 14, 3}, {12, 11}}, - {{TQEType::XX, 13, 2}, {12, 6}}, {{TQEType::XY, 13, 2}, {12, 14}}, - {{TQEType::XZ, 13, 2}, {13, 10}}, {{TQEType::YX, 13, 2}, {14, 3}}, - {{TQEType::YY, 13, 2}, {14, 1}}, {{TQEType::YZ, 13, 2}, {13, 0}}, - {{TQEType::ZX, 13, 2}, {15, 7}}, {{TQEType::ZY, 13, 2}, {15, 13}}, - {{TQEType::ZZ, 13, 2}, {13, 8}}, {{TQEType::XX, 14, 2}, {15, 7}}, - {{TQEType::XY, 14, 2}, {15, 13}}, {{TQEType::XZ, 14, 2}, {14, 8}}, - {{TQEType::YX, 14, 2}, {13, 3}}, {{TQEType::YY, 14, 2}, {13, 1}}, - {{TQEType::YZ, 14, 2}, {14, 0}}, {{TQEType::ZX, 14, 2}, {12, 6}}, - {{TQEType::ZY, 14, 2}, {12, 14}}, {{TQEType::ZZ, 14, 2}, {14, 10}}, - {{TQEType::XX, 13, 0}, {13, 4}}, {{TQEType::XY, 13, 0}, {13, 12}}, - {{TQEType::XZ, 13, 0}, {13, 8}}, {{TQEType::YX, 13, 0}, {13, 1}}, - {{TQEType::YY, 13, 0}, {13, 3}}, {{TQEType::YZ, 13, 0}, {13, 2}}, - {{TQEType::ZX, 13, 0}, {13, 5}}, {{TQEType::ZY, 13, 0}, {13, 15}}, - {{TQEType::ZZ, 13, 0}, {13, 10}}, {{TQEType::XX, 14, 0}, {14, 5}}, - {{TQEType::XY, 14, 0}, {14, 15}}, {{TQEType::XZ, 14, 0}, {14, 10}}, - {{TQEType::YX, 14, 0}, {14, 1}}, {{TQEType::YY, 14, 0}, {14, 3}}, - {{TQEType::YZ, 14, 0}, {14, 2}}, {{TQEType::ZX, 14, 0}, {14, 4}}, - {{TQEType::ZY, 14, 0}, {14, 12}}, {{TQEType::ZZ, 14, 0}, {14, 8}}, - {{TQEType::XX, 15, 1}, {15, 4}}, {{TQEType::XY, 15, 1}, {14, 14}}, - {{TQEType::XZ, 15, 1}, {14, 11}}, {{TQEType::YX, 15, 1}, {15, 1}}, - {{TQEType::YY, 15, 1}, {12, 1}}, {{TQEType::YZ, 15, 1}, {12, 1}}, - {{TQEType::ZX, 15, 1}, {15, 4}}, {{TQEType::ZY, 15, 1}, {13, 14}}, - {{TQEType::ZZ, 15, 1}, {13, 11}}, {{TQEType::XX, 12, 1}, {12, 5}}, - {{TQEType::XY, 12, 1}, {13, 13}}, {{TQEType::XZ, 12, 1}, {13, 9}}, - {{TQEType::YX, 12, 1}, {12, 1}}, {{TQEType::YY, 12, 1}, {15, 1}}, - {{TQEType::YZ, 12, 1}, {15, 1}}, {{TQEType::ZX, 12, 1}, {12, 5}}, - {{TQEType::ZY, 12, 1}, {14, 13}}, {{TQEType::ZZ, 12, 1}, {14, 9}}, - {{TQEType::XX, 15, 3}, {14, 6}}, {{TQEType::XY, 15, 3}, {15, 12}}, - {{TQEType::XZ, 15, 3}, {14, 9}}, {{TQEType::YX, 15, 3}, {12, 3}}, - {{TQEType::YY, 15, 3}, {15, 3}}, {{TQEType::YZ, 15, 3}, {12, 3}}, - {{TQEType::ZX, 15, 3}, {13, 6}}, {{TQEType::ZY, 15, 3}, {15, 12}}, - {{TQEType::ZZ, 15, 3}, {13, 9}}, {{TQEType::XX, 12, 3}, {13, 7}}, - {{TQEType::XY, 12, 3}, {12, 15}}, {{TQEType::XZ, 12, 3}, {13, 11}}, - {{TQEType::YX, 12, 3}, {15, 3}}, {{TQEType::YY, 12, 3}, {12, 3}}, - {{TQEType::YZ, 12, 3}, {15, 3}}, {{TQEType::ZX, 12, 3}, {14, 7}}, - {{TQEType::ZY, 12, 3}, {12, 15}}, {{TQEType::ZZ, 12, 3}, {14, 11}}, - {{TQEType::XX, 15, 2}, {14, 7}}, {{TQEType::XY, 15, 2}, {14, 13}}, - {{TQEType::XZ, 15, 2}, {15, 8}}, {{TQEType::YX, 15, 2}, {12, 2}}, - {{TQEType::YY, 15, 2}, {12, 2}}, {{TQEType::YZ, 15, 2}, {15, 2}}, - {{TQEType::ZX, 15, 2}, {13, 7}}, {{TQEType::ZY, 15, 2}, {13, 13}}, - {{TQEType::ZZ, 15, 2}, {15, 8}}, {{TQEType::XX, 12, 2}, {13, 6}}, - {{TQEType::XY, 12, 2}, {13, 14}}, {{TQEType::XZ, 12, 2}, {12, 10}}, - {{TQEType::YX, 12, 2}, {15, 2}}, {{TQEType::YY, 12, 2}, {15, 2}}, - {{TQEType::YZ, 12, 2}, {12, 2}}, {{TQEType::ZX, 12, 2}, {14, 6}}, - {{TQEType::ZY, 12, 2}, {14, 14}}, {{TQEType::ZZ, 12, 2}, {12, 10}}, - {{TQEType::XX, 15, 0}, {15, 5}}, {{TQEType::XY, 15, 0}, {15, 15}}, - {{TQEType::XZ, 15, 0}, {15, 10}}, {{TQEType::YX, 15, 0}, {15, 0}}, - {{TQEType::YY, 15, 0}, {15, 0}}, {{TQEType::YZ, 15, 0}, {15, 0}}, - {{TQEType::ZX, 15, 0}, {15, 5}}, {{TQEType::ZY, 15, 0}, {15, 15}}, - {{TQEType::ZZ, 15, 0}, {15, 10}}, {{TQEType::XX, 12, 0}, {12, 4}}, - {{TQEType::XY, 12, 0}, {12, 12}}, {{TQEType::XZ, 12, 0}, {12, 8}}, - {{TQEType::YX, 12, 0}, {12, 0}}, {{TQEType::YY, 12, 0}, {12, 0}}, - {{TQEType::YZ, 12, 0}, {12, 0}}, {{TQEType::ZX, 12, 0}, {12, 4}}, - {{TQEType::ZY, 12, 0}, {12, 12}}, {{TQEType::ZZ, 12, 0}, {12, 8}}, - {{TQEType::XX, 9, 1}, {9, 5}}, {{TQEType::XY, 9, 1}, {8, 13}}, - {{TQEType::XZ, 9, 1}, {8, 9}}, {{TQEType::YX, 9, 1}, {9, 4}}, - {{TQEType::YY, 9, 1}, {10, 14}}, {{TQEType::YZ, 9, 1}, {10, 11}}, - {{TQEType::ZX, 9, 1}, {9, 0}}, {{TQEType::ZY, 9, 1}, {11, 2}}, - {{TQEType::ZZ, 9, 1}, {11, 3}}, {{TQEType::XX, 11, 1}, {11, 4}}, - {{TQEType::XY, 11, 1}, {10, 14}}, {{TQEType::XZ, 11, 1}, {10, 11}}, - {{TQEType::YX, 11, 1}, {11, 5}}, {{TQEType::YY, 11, 1}, {8, 13}}, - {{TQEType::YZ, 11, 1}, {8, 9}}, {{TQEType::ZX, 11, 1}, {11, 0}}, - {{TQEType::ZY, 11, 1}, {9, 2}}, {{TQEType::ZZ, 11, 1}, {9, 3}}, - {{TQEType::XX, 9, 3}, {8, 7}}, {{TQEType::XY, 9, 3}, {9, 15}}, - {{TQEType::XZ, 9, 3}, {8, 11}}, {{TQEType::YX, 9, 3}, {10, 6}}, - {{TQEType::YY, 9, 3}, {9, 12}}, {{TQEType::YZ, 9, 3}, {10, 9}}, - {{TQEType::ZX, 9, 3}, {11, 2}}, {{TQEType::ZY, 9, 3}, {9, 0}}, - {{TQEType::ZZ, 9, 3}, {11, 1}}, {{TQEType::XX, 11, 3}, {10, 6}}, - {{TQEType::XY, 11, 3}, {11, 12}}, {{TQEType::XZ, 11, 3}, {10, 9}}, - {{TQEType::YX, 11, 3}, {8, 7}}, {{TQEType::YY, 11, 3}, {11, 15}}, - {{TQEType::YZ, 11, 3}, {8, 11}}, {{TQEType::ZX, 11, 3}, {9, 2}}, - {{TQEType::ZY, 11, 3}, {11, 0}}, {{TQEType::ZZ, 11, 3}, {9, 1}}, - {{TQEType::XX, 9, 2}, {8, 6}}, {{TQEType::XY, 9, 2}, {8, 14}}, - {{TQEType::XZ, 9, 2}, {9, 10}}, {{TQEType::YX, 9, 2}, {10, 7}}, - {{TQEType::YY, 9, 2}, {10, 13}}, {{TQEType::YZ, 9, 2}, {9, 8}}, - {{TQEType::ZX, 9, 2}, {11, 3}}, {{TQEType::ZY, 9, 2}, {11, 1}}, - {{TQEType::ZZ, 9, 2}, {9, 0}}, {{TQEType::XX, 11, 2}, {10, 7}}, - {{TQEType::XY, 11, 2}, {10, 13}}, {{TQEType::XZ, 11, 2}, {11, 8}}, - {{TQEType::YX, 11, 2}, {8, 6}}, {{TQEType::YY, 11, 2}, {8, 14}}, - {{TQEType::YZ, 11, 2}, {11, 10}}, {{TQEType::ZX, 11, 2}, {9, 3}}, - {{TQEType::ZY, 11, 2}, {9, 1}}, {{TQEType::ZZ, 11, 2}, {11, 0}}, - {{TQEType::XX, 9, 0}, {9, 4}}, {{TQEType::XY, 9, 0}, {9, 12}}, - {{TQEType::XZ, 9, 0}, {9, 8}}, {{TQEType::YX, 9, 0}, {9, 5}}, - {{TQEType::YY, 9, 0}, {9, 15}}, {{TQEType::YZ, 9, 0}, {9, 10}}, - {{TQEType::ZX, 9, 0}, {9, 1}}, {{TQEType::ZY, 9, 0}, {9, 3}}, - {{TQEType::ZZ, 9, 0}, {9, 2}}, {{TQEType::XX, 11, 0}, {11, 5}}, - {{TQEType::XY, 11, 0}, {11, 15}}, {{TQEType::XZ, 11, 0}, {11, 10}}, - {{TQEType::YX, 11, 0}, {11, 4}}, {{TQEType::YY, 11, 0}, {11, 12}}, - {{TQEType::YZ, 11, 0}, {11, 8}}, {{TQEType::ZX, 11, 0}, {11, 1}}, - {{TQEType::ZY, 11, 0}, {11, 3}}, {{TQEType::ZZ, 11, 0}, {11, 2}}, - {{TQEType::XX, 10, 1}, {10, 4}}, {{TQEType::XY, 10, 1}, {11, 14}}, - {{TQEType::XZ, 10, 1}, {11, 11}}, {{TQEType::YX, 10, 1}, {10, 4}}, - {{TQEType::YY, 10, 1}, {9, 14}}, {{TQEType::YZ, 10, 1}, {9, 11}}, - {{TQEType::ZX, 10, 1}, {10, 1}}, {{TQEType::ZY, 10, 1}, {8, 1}}, - {{TQEType::ZZ, 10, 1}, {8, 1}}, {{TQEType::XX, 8, 1}, {8, 5}}, - {{TQEType::XY, 8, 1}, {9, 13}}, {{TQEType::XZ, 8, 1}, {9, 9}}, - {{TQEType::YX, 8, 1}, {8, 5}}, {{TQEType::YY, 8, 1}, {11, 13}}, - {{TQEType::YZ, 8, 1}, {11, 9}}, {{TQEType::ZX, 8, 1}, {8, 1}}, - {{TQEType::ZY, 8, 1}, {10, 1}}, {{TQEType::ZZ, 8, 1}, {10, 1}}, - {{TQEType::XX, 10, 3}, {11, 6}}, {{TQEType::XY, 10, 3}, {10, 12}}, - {{TQEType::XZ, 10, 3}, {11, 9}}, {{TQEType::YX, 10, 3}, {9, 6}}, - {{TQEType::YY, 10, 3}, {10, 12}}, {{TQEType::YZ, 10, 3}, {9, 9}}, - {{TQEType::ZX, 10, 3}, {8, 3}}, {{TQEType::ZY, 10, 3}, {10, 3}}, - {{TQEType::ZZ, 10, 3}, {8, 3}}, {{TQEType::XX, 8, 3}, {9, 7}}, - {{TQEType::XY, 8, 3}, {8, 15}}, {{TQEType::XZ, 8, 3}, {9, 11}}, - {{TQEType::YX, 8, 3}, {11, 7}}, {{TQEType::YY, 8, 3}, {8, 15}}, - {{TQEType::YZ, 8, 3}, {11, 11}}, {{TQEType::ZX, 8, 3}, {10, 3}}, - {{TQEType::ZY, 8, 3}, {8, 3}}, {{TQEType::ZZ, 8, 3}, {10, 3}}, - {{TQEType::XX, 10, 2}, {11, 7}}, {{TQEType::XY, 10, 2}, {11, 13}}, - {{TQEType::XZ, 10, 2}, {10, 8}}, {{TQEType::YX, 10, 2}, {9, 7}}, - {{TQEType::YY, 10, 2}, {9, 13}}, {{TQEType::YZ, 10, 2}, {10, 8}}, - {{TQEType::ZX, 10, 2}, {8, 2}}, {{TQEType::ZY, 10, 2}, {8, 2}}, - {{TQEType::ZZ, 10, 2}, {10, 2}}, {{TQEType::XX, 8, 2}, {9, 6}}, - {{TQEType::XY, 8, 2}, {9, 14}}, {{TQEType::XZ, 8, 2}, {8, 10}}, - {{TQEType::YX, 8, 2}, {11, 6}}, {{TQEType::YY, 8, 2}, {11, 14}}, - {{TQEType::YZ, 8, 2}, {8, 10}}, {{TQEType::ZX, 8, 2}, {10, 2}}, - {{TQEType::ZY, 8, 2}, {10, 2}}, {{TQEType::ZZ, 8, 2}, {8, 2}}, - {{TQEType::XX, 10, 0}, {10, 5}}, {{TQEType::XY, 10, 0}, {10, 15}}, - {{TQEType::XZ, 10, 0}, {10, 10}}, {{TQEType::YX, 10, 0}, {10, 5}}, - {{TQEType::YY, 10, 0}, {10, 15}}, {{TQEType::YZ, 10, 0}, {10, 10}}, - {{TQEType::ZX, 10, 0}, {10, 0}}, {{TQEType::ZY, 10, 0}, {10, 0}}, - {{TQEType::ZZ, 10, 0}, {10, 0}}, {{TQEType::XX, 8, 0}, {8, 4}}, - {{TQEType::XY, 8, 0}, {8, 12}}, {{TQEType::XZ, 8, 0}, {8, 8}}, - {{TQEType::YX, 8, 0}, {8, 4}}, {{TQEType::YY, 8, 0}, {8, 12}}, - {{TQEType::YZ, 8, 0}, {8, 8}}, {{TQEType::ZX, 8, 0}, {8, 0}}, - {{TQEType::ZY, 8, 0}, {8, 0}}, {{TQEType::ZZ, 8, 0}, {8, 0}}, - {{TQEType::XX, 1, 1}, {1, 1}}, {{TQEType::XY, 1, 1}, {0, 1}}, - {{TQEType::XZ, 1, 1}, {0, 1}}, {{TQEType::YX, 1, 1}, {1, 0}}, - {{TQEType::YY, 1, 1}, {2, 2}}, {{TQEType::YZ, 1, 1}, {2, 3}}, - {{TQEType::ZX, 1, 1}, {1, 0}}, {{TQEType::ZY, 1, 1}, {3, 2}}, - {{TQEType::ZZ, 1, 1}, {3, 3}}, {{TQEType::XX, 3, 1}, {3, 0}}, - {{TQEType::XY, 3, 1}, {2, 2}}, {{TQEType::XZ, 3, 1}, {2, 3}}, - {{TQEType::YX, 3, 1}, {3, 1}}, {{TQEType::YY, 3, 1}, {0, 1}}, - {{TQEType::YZ, 3, 1}, {0, 1}}, {{TQEType::ZX, 3, 1}, {3, 0}}, - {{TQEType::ZY, 3, 1}, {1, 2}}, {{TQEType::ZZ, 3, 1}, {1, 3}}, - {{TQEType::XX, 2, 1}, {2, 0}}, {{TQEType::XY, 2, 1}, {3, 2}}, - {{TQEType::XZ, 2, 1}, {3, 3}}, {{TQEType::YX, 2, 1}, {2, 0}}, - {{TQEType::YY, 2, 1}, {1, 2}}, {{TQEType::YZ, 2, 1}, {1, 3}}, - {{TQEType::ZX, 2, 1}, {2, 1}}, {{TQEType::ZY, 2, 1}, {0, 1}}, - {{TQEType::ZZ, 2, 1}, {0, 1}}, {{TQEType::XX, 0, 1}, {0, 1}}, - {{TQEType::XY, 0, 1}, {1, 1}}, {{TQEType::XZ, 0, 1}, {1, 1}}, - {{TQEType::YX, 0, 1}, {0, 1}}, {{TQEType::YY, 0, 1}, {3, 1}}, - {{TQEType::YZ, 0, 1}, {3, 1}}, {{TQEType::ZX, 0, 1}, {0, 1}}, - {{TQEType::ZY, 0, 1}, {2, 1}}, {{TQEType::ZZ, 0, 1}, {2, 1}}, - {{TQEType::XX, 1, 3}, {0, 3}}, {{TQEType::XY, 1, 3}, {1, 3}}, - {{TQEType::XZ, 1, 3}, {0, 3}}, {{TQEType::YX, 1, 3}, {2, 2}}, - {{TQEType::YY, 1, 3}, {1, 0}}, {{TQEType::YZ, 1, 3}, {2, 1}}, - {{TQEType::ZX, 1, 3}, {3, 2}}, {{TQEType::ZY, 1, 3}, {1, 0}}, - {{TQEType::ZZ, 1, 3}, {3, 1}}, {{TQEType::XX, 3, 3}, {2, 2}}, - {{TQEType::XY, 3, 3}, {3, 0}}, {{TQEType::XZ, 3, 3}, {2, 1}}, - {{TQEType::YX, 3, 3}, {0, 3}}, {{TQEType::YY, 3, 3}, {3, 3}}, - {{TQEType::YZ, 3, 3}, {0, 3}}, {{TQEType::ZX, 3, 3}, {1, 2}}, - {{TQEType::ZY, 3, 3}, {3, 0}}, {{TQEType::ZZ, 3, 3}, {1, 1}}, - {{TQEType::XX, 2, 3}, {3, 2}}, {{TQEType::XY, 2, 3}, {2, 0}}, - {{TQEType::XZ, 2, 3}, {3, 1}}, {{TQEType::YX, 2, 3}, {1, 2}}, - {{TQEType::YY, 2, 3}, {2, 0}}, {{TQEType::YZ, 2, 3}, {1, 1}}, - {{TQEType::ZX, 2, 3}, {0, 3}}, {{TQEType::ZY, 2, 3}, {2, 3}}, - {{TQEType::ZZ, 2, 3}, {0, 3}}, {{TQEType::XX, 0, 3}, {1, 3}}, - {{TQEType::XY, 0, 3}, {0, 3}}, {{TQEType::XZ, 0, 3}, {1, 3}}, - {{TQEType::YX, 0, 3}, {3, 3}}, {{TQEType::YY, 0, 3}, {0, 3}}, - {{TQEType::YZ, 0, 3}, {3, 3}}, {{TQEType::ZX, 0, 3}, {2, 3}}, - {{TQEType::ZY, 0, 3}, {0, 3}}, {{TQEType::ZZ, 0, 3}, {2, 3}}, - {{TQEType::XX, 1, 2}, {0, 2}}, {{TQEType::XY, 1, 2}, {0, 2}}, - {{TQEType::XZ, 1, 2}, {1, 2}}, {{TQEType::YX, 1, 2}, {2, 3}}, - {{TQEType::YY, 1, 2}, {2, 1}}, {{TQEType::YZ, 1, 2}, {1, 0}}, - {{TQEType::ZX, 1, 2}, {3, 3}}, {{TQEType::ZY, 1, 2}, {3, 1}}, - {{TQEType::ZZ, 1, 2}, {1, 0}}, {{TQEType::XX, 3, 2}, {2, 3}}, - {{TQEType::XY, 3, 2}, {2, 1}}, {{TQEType::XZ, 3, 2}, {3, 0}}, - {{TQEType::YX, 3, 2}, {0, 2}}, {{TQEType::YY, 3, 2}, {0, 2}}, - {{TQEType::YZ, 3, 2}, {3, 2}}, {{TQEType::ZX, 3, 2}, {1, 3}}, - {{TQEType::ZY, 3, 2}, {1, 1}}, {{TQEType::ZZ, 3, 2}, {3, 0}}, - {{TQEType::XX, 2, 2}, {3, 3}}, {{TQEType::XY, 2, 2}, {3, 1}}, - {{TQEType::XZ, 2, 2}, {2, 0}}, {{TQEType::YX, 2, 2}, {1, 3}}, - {{TQEType::YY, 2, 2}, {1, 1}}, {{TQEType::YZ, 2, 2}, {2, 0}}, - {{TQEType::ZX, 2, 2}, {0, 2}}, {{TQEType::ZY, 2, 2}, {0, 2}}, - {{TQEType::ZZ, 2, 2}, {2, 2}}, {{TQEType::XX, 0, 2}, {1, 2}}, - {{TQEType::XY, 0, 2}, {1, 2}}, {{TQEType::XZ, 0, 2}, {0, 2}}, - {{TQEType::YX, 0, 2}, {3, 2}}, {{TQEType::YY, 0, 2}, {3, 2}}, - {{TQEType::YZ, 0, 2}, {0, 2}}, {{TQEType::ZX, 0, 2}, {2, 2}}, - {{TQEType::ZY, 0, 2}, {2, 2}}, {{TQEType::ZZ, 0, 2}, {0, 2}}, - {{TQEType::XX, 1, 0}, {1, 0}}, {{TQEType::XY, 1, 0}, {1, 0}}, - {{TQEType::XZ, 1, 0}, {1, 0}}, {{TQEType::YX, 1, 0}, {1, 1}}, - {{TQEType::YY, 1, 0}, {1, 3}}, {{TQEType::YZ, 1, 0}, {1, 2}}, - {{TQEType::ZX, 1, 0}, {1, 1}}, {{TQEType::ZY, 1, 0}, {1, 3}}, - {{TQEType::ZZ, 1, 0}, {1, 2}}, {{TQEType::XX, 3, 0}, {3, 1}}, - {{TQEType::XY, 3, 0}, {3, 3}}, {{TQEType::XZ, 3, 0}, {3, 2}}, - {{TQEType::YX, 3, 0}, {3, 0}}, {{TQEType::YY, 3, 0}, {3, 0}}, - {{TQEType::YZ, 3, 0}, {3, 0}}, {{TQEType::ZX, 3, 0}, {3, 1}}, - {{TQEType::ZY, 3, 0}, {3, 3}}, {{TQEType::ZZ, 3, 0}, {3, 2}}, - {{TQEType::XX, 2, 0}, {2, 1}}, {{TQEType::XY, 2, 0}, {2, 3}}, - {{TQEType::XZ, 2, 0}, {2, 2}}, {{TQEType::YX, 2, 0}, {2, 1}}, - {{TQEType::YY, 2, 0}, {2, 3}}, {{TQEType::YZ, 2, 0}, {2, 2}}, - {{TQEType::ZX, 2, 0}, {2, 0}}, {{TQEType::ZY, 2, 0}, {2, 0}}, - {{TQEType::ZZ, 2, 0}, {2, 0}}, {{TQEType::XX, 0, 0}, {0, 0}}, - {{TQEType::XY, 0, 0}, {0, 0}}, {{TQEType::XZ, 0, 0}, {0, 0}}, - {{TQEType::YX, 0, 0}, {0, 0}}, {{TQEType::YY, 0, 0}, {0, 0}}, - {{TQEType::YZ, 0, 0}, {0, 0}}, {{TQEType::ZX, 0, 0}, {0, 0}}, - {{TQEType::ZY, 0, 0}, {0, 0}}, {{TQEType::ZZ, 0, 0}, {0, 0}}}; + std::pair, std::vector, hash_pauli_pauli> + TQE_REDUCTION_MAP = { + {{Pauli::X, Pauli::X}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::ZX}}, + {{Pauli::X, Pauli::Y}, + {TQEType::XX, TQEType::XZ, TQEType::YY, TQEType::ZY}}, + {{Pauli::X, Pauli::Z}, + {TQEType::XX, TQEType::XY, TQEType::YZ, TQEType::ZZ}}, + {{Pauli::Y, Pauli::X}, + {TQEType::XX, TQEType::YY, TQEType::YZ, TQEType::ZX}}, + {{Pauli::Y, Pauli::Y}, + {TQEType::XY, TQEType::YX, TQEType::YZ, TQEType::ZY}}, + {{Pauli::Y, Pauli::Z}, + {TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZZ}}, + {{Pauli::Z, Pauli::X}, + {TQEType::XX, TQEType::YX, TQEType::ZY, TQEType::ZZ}}, + {{Pauli::Z, Pauli::Y}, + {TQEType::XY, TQEType::YY, TQEType::ZX, TQEType::ZZ}}, + {{Pauli::Z, Pauli::Z}, + {TQEType::XZ, TQEType::YZ, TQEType::ZX, TQEType::ZY}}}; /** - * @brief Maps a pair of week suppotrs in a factor support vector to a set of - * TQE gates that will transform them to a pair that only has one weak (not - * always possible) + * @brief Given (P(0), P(1), Q(0), Q(1)), + * where both pairs (P(0), Q(0)) and (P(1), Q(1)) non-trivially commute, + * return the TQE gates that can map one pair to identities. */ const static std::unordered_map< - std::pair, std::vector, hash_pair> - FACTOR_PAIR_WW_TO_WN_OR_NW_TQES = { - {{5, 5}, {TQEType::XY, TQEType::ZX, TQEType::YX, TQEType::XZ}}, - {{4, 5}, {}}, - {{5, 4}, {}}, - {{4, 4}, {TQEType::XY, TQEType::ZX, TQEType::YX, TQEType::XZ}}, - {{15, 5}, {TQEType::ZX, TQEType::YZ, TQEType::XX, TQEType::YY}}, - {{12, 5}, {}}, - {{15, 4}, {}}, - {{12, 4}, {TQEType::ZX, TQEType::YZ, TQEType::XX, TQEType::YY}}, - {{10, 5}, {TQEType::ZZ, TQEType::YX, TQEType::ZY, TQEType::XX}}, - {{8, 5}, {}}, - {{10, 4}, {}}, - {{8, 4}, {TQEType::ZZ, TQEType::YX, TQEType::ZY, TQEType::XX}}, - {{1, 5}, {}}, - {{3, 5}, {}}, - {{2, 5}, {}}, - {{1, 4}, {}}, - {{3, 4}, {}}, - {{2, 4}, {}}, - {{5, 15}, {TQEType::ZY, TQEType::XZ, TQEType::XX, TQEType::YY}}, - {{4, 15}, {}}, - {{5, 12}, {}}, - {{4, 12}, {TQEType::ZY, TQEType::XZ, TQEType::XX, TQEType::YY}}, - {{15, 15}, {TQEType::XY, TQEType::ZY, TQEType::YX, TQEType::YZ}}, - {{12, 15}, {}}, - {{15, 12}, {}}, - {{12, 12}, {TQEType::XY, TQEType::ZY, TQEType::YX, TQEType::YZ}}, - {{10, 15}, {TQEType::XY, TQEType::ZX, TQEType::ZZ, TQEType::YY}}, - {{8, 15}, {}}, - {{10, 12}, {}}, - {{8, 12}, {TQEType::XY, TQEType::ZX, TQEType::ZZ, TQEType::YY}}, - {{1, 15}, {}}, - {{3, 15}, {}}, - {{2, 15}, {}}, - {{1, 12}, {}}, - {{3, 12}, {}}, - {{2, 12}, {}}, - {{5, 10}, {TQEType::XY, TQEType::ZZ, TQEType::YZ, TQEType::XX}}, - {{4, 10}, {}}, - {{5, 8}, {}}, - {{4, 8}, {TQEType::XY, TQEType::ZZ, TQEType::YZ, TQEType::XX}}, - {{15, 10}, {TQEType::ZZ, TQEType::YX, TQEType::XZ, TQEType::YY}}, - {{12, 10}, {}}, - {{15, 8}, {}}, - {{12, 8}, {TQEType::ZZ, TQEType::YX, TQEType::XZ, TQEType::YY}}, - {{10, 10}, {TQEType::ZX, TQEType::YZ, TQEType::ZY, TQEType::XZ}}, - {{8, 10}, {}}, - {{10, 8}, {}}, - {{8, 8}, {TQEType::ZX, TQEType::YZ, TQEType::ZY, TQEType::XZ}}, - {{1, 10}, {}}, - {{3, 10}, {}}, - {{2, 10}, {}}, - {{1, 8}, {}}, - {{3, 8}, {}}, - {{2, 8}, {}}, - {{5, 1}, {}}, - {{4, 1}, {}}, - {{5, 3}, {}}, - {{4, 3}, {}}, - {{5, 2}, {}}, - {{4, 2}, {}}, - {{15, 1}, {}}, - {{12, 1}, {}}, - {{15, 3}, {}}, - {{12, 3}, {}}, - {{15, 2}, {}}, - {{12, 2}, {}}, - {{10, 1}, {}}, - {{8, 1}, {}}, - {{10, 3}, {}}, - {{8, 3}, {}}, - {{10, 2}, {}}, - {{8, 2}, {}}, - {{1, 1}, {TQEType::XY, TQEType::ZX, TQEType::YX, TQEType::XZ}}, - {{3, 1}, {TQEType::ZX, TQEType::YZ, TQEType::XX, TQEType::YY}}, - {{2, 1}, {TQEType::ZZ, TQEType::YX, TQEType::ZY, TQEType::XX}}, - {{1, 3}, {TQEType::ZY, TQEType::XZ, TQEType::XX, TQEType::YY}}, - {{3, 3}, {TQEType::XY, TQEType::ZY, TQEType::YX, TQEType::YZ}}, - {{2, 3}, {TQEType::XY, TQEType::ZX, TQEType::ZZ, TQEType::YY}}, - {{1, 2}, {TQEType::XY, TQEType::ZZ, TQEType::YZ, TQEType::XX}}, - {{3, 2}, {TQEType::ZZ, TQEType::YX, TQEType::XZ, TQEType::YY}}, - {{2, 2}, {TQEType::ZX, TQEType::YZ, TQEType::ZY, TQEType::XZ}}}; + std::tuple, std::vector, + hash_quadruple> + CC_TO_IC_OR_CI_MAP = { + {{Pauli::X, Pauli::X, Pauli::X, Pauli::X}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::ZX}}, + {{Pauli::X, Pauli::X, Pauli::X, Pauli::I}, {}}, + {{Pauli::X, Pauli::X, Pauli::I, Pauli::X}, {}}, + {{Pauli::X, Pauli::X, Pauli::I, Pauli::I}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::ZX}}, + {{Pauli::X, Pauli::Y, Pauli::X, Pauli::Y}, + {TQEType::XX, TQEType::XZ, TQEType::YY, TQEType::ZY}}, + {{Pauli::X, Pauli::Y, Pauli::X, Pauli::I}, {}}, + {{Pauli::X, Pauli::Y, Pauli::I, Pauli::Y}, {}}, + {{Pauli::X, Pauli::Y, Pauli::I, Pauli::I}, + {TQEType::XX, TQEType::XZ, TQEType::YY, TQEType::ZY}}, + {{Pauli::X, Pauli::Z, Pauli::X, Pauli::Z}, + {TQEType::XX, TQEType::XY, TQEType::YZ, TQEType::ZZ}}, + {{Pauli::X, Pauli::Z, Pauli::X, Pauli::I}, {}}, + {{Pauli::X, Pauli::Z, Pauli::I, Pauli::Z}, {}}, + {{Pauli::X, Pauli::Z, Pauli::I, Pauli::I}, + {TQEType::XX, TQEType::XY, TQEType::YZ, TQEType::ZZ}}, + {{Pauli::X, Pauli::I, Pauli::X, Pauli::X}, {}}, + {{Pauli::X, Pauli::I, Pauli::X, Pauli::Y}, {}}, + {{Pauli::X, Pauli::I, Pauli::X, Pauli::Z}, {}}, + {{Pauli::X, Pauli::I, Pauli::I, Pauli::X}, {}}, + {{Pauli::X, Pauli::I, Pauli::I, Pauli::Y}, {}}, + {{Pauli::X, Pauli::I, Pauli::I, Pauli::Z}, {}}, + {{Pauli::Y, Pauli::X, Pauli::Y, Pauli::X}, + {TQEType::XX, TQEType::YY, TQEType::YZ, TQEType::ZX}}, + {{Pauli::Y, Pauli::X, Pauli::Y, Pauli::I}, {}}, + {{Pauli::Y, Pauli::X, Pauli::I, Pauli::X}, {}}, + {{Pauli::Y, Pauli::X, Pauli::I, Pauli::I}, + {TQEType::XX, TQEType::YY, TQEType::YZ, TQEType::ZX}}, + {{Pauli::Y, Pauli::Y, Pauli::Y, Pauli::Y}, + {TQEType::XY, TQEType::YX, TQEType::YZ, TQEType::ZY}}, + {{Pauli::Y, Pauli::Y, Pauli::Y, Pauli::I}, {}}, + {{Pauli::Y, Pauli::Y, Pauli::I, Pauli::Y}, {}}, + {{Pauli::Y, Pauli::Y, Pauli::I, Pauli::I}, + {TQEType::XY, TQEType::YX, TQEType::YZ, TQEType::ZY}}, + {{Pauli::Y, Pauli::Z, Pauli::Y, Pauli::Z}, + {TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZZ}}, + {{Pauli::Y, Pauli::Z, Pauli::Y, Pauli::I}, {}}, + {{Pauli::Y, Pauli::Z, Pauli::I, Pauli::Z}, {}}, + {{Pauli::Y, Pauli::Z, Pauli::I, Pauli::I}, + {TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZZ}}, + {{Pauli::Y, Pauli::I, Pauli::Y, Pauli::X}, {}}, + {{Pauli::Y, Pauli::I, Pauli::Y, Pauli::Y}, {}}, + {{Pauli::Y, Pauli::I, Pauli::Y, Pauli::Z}, {}}, + {{Pauli::Y, Pauli::I, Pauli::I, Pauli::X}, {}}, + {{Pauli::Y, Pauli::I, Pauli::I, Pauli::Y}, {}}, + {{Pauli::Y, Pauli::I, Pauli::I, Pauli::Z}, {}}, + {{Pauli::Z, Pauli::X, Pauli::Z, Pauli::X}, + {TQEType::XX, TQEType::YX, TQEType::ZY, TQEType::ZZ}}, + {{Pauli::Z, Pauli::X, Pauli::Z, Pauli::I}, {}}, + {{Pauli::Z, Pauli::X, Pauli::I, Pauli::X}, {}}, + {{Pauli::Z, Pauli::X, Pauli::I, Pauli::I}, + {TQEType::XX, TQEType::YX, TQEType::ZY, TQEType::ZZ}}, + {{Pauli::Z, Pauli::Y, Pauli::Z, Pauli::Y}, + {TQEType::XY, TQEType::YY, TQEType::ZX, TQEType::ZZ}}, + {{Pauli::Z, Pauli::Y, Pauli::Z, Pauli::I}, {}}, + {{Pauli::Z, Pauli::Y, Pauli::I, Pauli::Y}, {}}, + {{Pauli::Z, Pauli::Y, Pauli::I, Pauli::I}, + {TQEType::XY, TQEType::YY, TQEType::ZX, TQEType::ZZ}}, + {{Pauli::Z, Pauli::Z, Pauli::Z, Pauli::Z}, + {TQEType::XZ, TQEType::YZ, TQEType::ZX, TQEType::ZY}}, + {{Pauli::Z, Pauli::Z, Pauli::Z, Pauli::I}, {}}, + {{Pauli::Z, Pauli::Z, Pauli::I, Pauli::Z}, {}}, + {{Pauli::Z, Pauli::Z, Pauli::I, Pauli::I}, + {TQEType::XZ, TQEType::YZ, TQEType::ZX, TQEType::ZY}}, + {{Pauli::Z, Pauli::I, Pauli::Z, Pauli::X}, {}}, + {{Pauli::Z, Pauli::I, Pauli::Z, Pauli::Y}, {}}, + {{Pauli::Z, Pauli::I, Pauli::Z, Pauli::Z}, {}}, + {{Pauli::Z, Pauli::I, Pauli::I, Pauli::X}, {}}, + {{Pauli::Z, Pauli::I, Pauli::I, Pauli::Y}, {}}, + {{Pauli::Z, Pauli::I, Pauli::I, Pauli::Z}, {}}, + {{Pauli::I, Pauli::X, Pauli::X, Pauli::X}, {}}, + {{Pauli::I, Pauli::X, Pauli::X, Pauli::I}, {}}, + {{Pauli::I, Pauli::X, Pauli::Y, Pauli::X}, {}}, + {{Pauli::I, Pauli::X, Pauli::Y, Pauli::I}, {}}, + {{Pauli::I, Pauli::X, Pauli::Z, Pauli::X}, {}}, + {{Pauli::I, Pauli::X, Pauli::Z, Pauli::I}, {}}, + {{Pauli::I, Pauli::Y, Pauli::X, Pauli::Y}, {}}, + {{Pauli::I, Pauli::Y, Pauli::X, Pauli::I}, {}}, + {{Pauli::I, Pauli::Y, Pauli::Y, Pauli::Y}, {}}, + {{Pauli::I, Pauli::Y, Pauli::Y, Pauli::I}, {}}, + {{Pauli::I, Pauli::Y, Pauli::Z, Pauli::Y}, {}}, + {{Pauli::I, Pauli::Y, Pauli::Z, Pauli::I}, {}}, + {{Pauli::I, Pauli::Z, Pauli::X, Pauli::Z}, {}}, + {{Pauli::I, Pauli::Z, Pauli::X, Pauli::I}, {}}, + {{Pauli::I, Pauli::Z, Pauli::Y, Pauli::Z}, {}}, + {{Pauli::I, Pauli::Z, Pauli::Y, Pauli::I}, {}}, + {{Pauli::I, Pauli::Z, Pauli::Z, Pauli::Z}, {}}, + {{Pauli::I, Pauli::Z, Pauli::Z, Pauli::I}, {}}, + {{Pauli::I, Pauli::I, Pauli::X, Pauli::X}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::ZX}}, + {{Pauli::I, Pauli::I, Pauli::X, Pauli::Y}, + {TQEType::XX, TQEType::XZ, TQEType::YY, TQEType::ZY}}, + {{Pauli::I, Pauli::I, Pauli::X, Pauli::Z}, + {TQEType::XX, TQEType::XY, TQEType::YZ, TQEType::ZZ}}, + {{Pauli::I, Pauli::I, Pauli::Y, Pauli::X}, + {TQEType::XX, TQEType::YY, TQEType::YZ, TQEType::ZX}}, + {{Pauli::I, Pauli::I, Pauli::Y, Pauli::Y}, + {TQEType::XY, TQEType::YX, TQEType::YZ, TQEType::ZY}}, + {{Pauli::I, Pauli::I, Pauli::Y, Pauli::Z}, + {TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZZ}}, + {{Pauli::I, Pauli::I, Pauli::Z, Pauli::X}, + {TQEType::XX, TQEType::YX, TQEType::ZY, TQEType::ZZ}}, + {{Pauli::I, Pauli::I, Pauli::Z, Pauli::Y}, + {TQEType::XY, TQEType::YY, TQEType::ZX, TQEType::ZZ}}, + {{Pauli::I, Pauli::I, Pauli::Z, Pauli::Z}, + {TQEType::XZ, TQEType::YZ, TQEType::ZX, TQEType::ZY}}}; /** - * @brief Maps a pair of strong supports in a factor support vector - * to a set of TQE gates that will transform them to a pair of weak supports + * @brief Given (P(0), P(1), Q(0), Q(1)), + * where both pairs (P(0), Q(0)) and (P(1), Q(1)) anti-commute, + * return the TQE gates that can map both to non-trivial commuting pairs. */ const static std::unordered_map< - std::pair, std::vector, hash_pair> - FACTOR_PAIR_SS_TO_WW_TQES = { - {{7, 7}, - {TQEType::YX, TQEType::ZY, TQEType::XY, TQEType::ZX, TQEType::YZ, - TQEType::XZ}}, - {{6, 7}, - {TQEType::YX, TQEType::YY, TQEType::XY, TQEType::ZX, TQEType::ZZ, - TQEType::XZ}}, - {{7, 6}, - {TQEType::YX, TQEType::YY, TQEType::XY, TQEType::ZX, TQEType::ZZ, - TQEType::XZ}}, - {{6, 6}, - {TQEType::YX, TQEType::ZY, TQEType::XY, TQEType::ZX, TQEType::YZ, - TQEType::XZ}}, - {{13, 7}, - {TQEType::ZY, TQEType::YY, TQEType::ZX, TQEType::YZ, TQEType::XZ, - TQEType::XX}}, - {{14, 7}, - {TQEType::YY, TQEType::XY, TQEType::ZX, TQEType::YZ, TQEType::ZZ, - TQEType::XX}}, - {{13, 6}, - {TQEType::YY, TQEType::XY, TQEType::ZX, TQEType::YZ, TQEType::ZZ, - TQEType::XX}}, - {{14, 6}, - {TQEType::ZY, TQEType::YY, TQEType::ZX, TQEType::YZ, TQEType::XZ, - TQEType::XX}}, - {{9, 7}, - {TQEType::YX, TQEType::ZY, TQEType::YY, TQEType::ZZ, TQEType::XZ, - TQEType::XX}}, - {{11, 7}, - {TQEType::YX, TQEType::ZY, TQEType::XY, TQEType::YZ, TQEType::ZZ, - TQEType::XX}}, - {{9, 6}, - {TQEType::YX, TQEType::ZY, TQEType::XY, TQEType::YZ, TQEType::ZZ, - TQEType::XX}}, - {{11, 6}, - {TQEType::YX, TQEType::ZY, TQEType::YY, TQEType::ZZ, TQEType::XZ, - TQEType::XX}}, - {{7, 13}, - {TQEType::ZY, TQEType::YY, TQEType::ZX, TQEType::YZ, TQEType::XZ, - TQEType::XX}}, - {{6, 13}, - {TQEType::YX, TQEType::ZY, TQEType::YY, TQEType::ZZ, TQEType::XZ, - TQEType::XX}}, - {{7, 14}, - {TQEType::YX, TQEType::ZY, TQEType::YY, TQEType::ZZ, TQEType::XZ, - TQEType::XX}}, - {{6, 14}, - {TQEType::ZY, TQEType::YY, TQEType::ZX, TQEType::YZ, TQEType::XZ, - TQEType::XX}}, - {{13, 13}, - {TQEType::YX, TQEType::ZY, TQEType::XY, TQEType::ZX, TQEType::YZ, - TQEType::XZ}}, - {{14, 13}, - {TQEType::YX, TQEType::ZY, TQEType::XY, TQEType::YZ, TQEType::ZZ, - TQEType::XX}}, - {{13, 14}, - {TQEType::YX, TQEType::ZY, TQEType::XY, TQEType::YZ, TQEType::ZZ, - TQEType::XX}}, - {{14, 14}, - {TQEType::YX, TQEType::ZY, TQEType::XY, TQEType::ZX, TQEType::YZ, - TQEType::XZ}}, - {{9, 13}, - {TQEType::YX, TQEType::YY, TQEType::XY, TQEType::ZX, TQEType::ZZ, - TQEType::XZ}}, - {{11, 13}, - {TQEType::YY, TQEType::XY, TQEType::ZX, TQEType::YZ, TQEType::ZZ, - TQEType::XX}}, - {{9, 14}, - {TQEType::YY, TQEType::XY, TQEType::ZX, TQEType::YZ, TQEType::ZZ, - TQEType::XX}}, - {{11, 14}, - {TQEType::YX, TQEType::YY, TQEType::XY, TQEType::ZX, TQEType::ZZ, - TQEType::XZ}}, - {{7, 9}, - {TQEType::YY, TQEType::XY, TQEType::ZX, TQEType::YZ, TQEType::ZZ, - TQEType::XX}}, - {{6, 9}, - {TQEType::YX, TQEType::ZY, TQEType::XY, TQEType::YZ, TQEType::ZZ, - TQEType::XX}}, - {{7, 11}, - {TQEType::YX, TQEType::ZY, TQEType::XY, TQEType::YZ, TQEType::ZZ, - TQEType::XX}}, - {{6, 11}, - {TQEType::YY, TQEType::XY, TQEType::ZX, TQEType::YZ, TQEType::ZZ, - TQEType::XX}}, - {{13, 9}, - {TQEType::YX, TQEType::YY, TQEType::XY, TQEType::ZX, TQEType::ZZ, - TQEType::XZ}}, - {{14, 9}, - {TQEType::YX, TQEType::ZY, TQEType::YY, TQEType::ZZ, TQEType::XZ, - TQEType::XX}}, - {{13, 11}, - {TQEType::YX, TQEType::ZY, TQEType::YY, TQEType::ZZ, TQEType::XZ, - TQEType::XX}}, - {{14, 11}, - {TQEType::YX, TQEType::YY, TQEType::XY, TQEType::ZX, TQEType::ZZ, - TQEType::XZ}}, - {{9, 9}, - {TQEType::YX, TQEType::ZY, TQEType::XY, TQEType::ZX, TQEType::YZ, - TQEType::XZ}}, - {{11, 9}, - {TQEType::ZY, TQEType::YY, TQEType::ZX, TQEType::YZ, TQEType::XZ, - TQEType::XX}}, - {{9, 11}, - {TQEType::ZY, TQEType::YY, TQEType::ZX, TQEType::YZ, TQEType::XZ, - TQEType::XX}}, - {{11, 11}, - {TQEType::YX, TQEType::ZY, TQEType::XY, TQEType::ZX, TQEType::YZ, - TQEType::XZ}}}; + std::tuple, std::vector, + hash_quadruple> + AA_TO_CC_MAP = { + {{Pauli::X, Pauli::X, Pauli::Y, Pauli::Y}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::YZ, TQEType::ZX, + TQEType::ZY}}, + {{Pauli::X, Pauli::X, Pauli::Y, Pauli::Z}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZX, + TQEType::ZZ}}, + {{Pauli::X, Pauli::X, Pauli::Z, Pauli::Y}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZX, + TQEType::ZZ}}, + {{Pauli::X, Pauli::X, Pauli::Z, Pauli::Z}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::YZ, TQEType::ZX, + TQEType::ZY}}, + {{Pauli::X, Pauli::Y, Pauli::Y, Pauli::X}, + {TQEType::XX, TQEType::XZ, TQEType::YY, TQEType::YZ, TQEType::ZX, + TQEType::ZY}}, + {{Pauli::X, Pauli::Y, Pauli::Y, Pauli::Z}, + {TQEType::XX, TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZY, + TQEType::ZZ}}, + {{Pauli::X, Pauli::Y, Pauli::Z, Pauli::X}, + {TQEType::XX, TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZY, + TQEType::ZZ}}, + {{Pauli::X, Pauli::Y, Pauli::Z, Pauli::Z}, + {TQEType::XX, TQEType::XZ, TQEType::YY, TQEType::YZ, TQEType::ZX, + TQEType::ZY}}, + {{Pauli::X, Pauli::Z, Pauli::Y, Pauli::X}, + {TQEType::XX, TQEType::XY, TQEType::YY, TQEType::YZ, TQEType::ZX, + TQEType::ZZ}}, + {{Pauli::X, Pauli::Z, Pauli::Y, Pauli::Y}, + {TQEType::XX, TQEType::XY, TQEType::YX, TQEType::YZ, TQEType::ZY, + TQEType::ZZ}}, + {{Pauli::X, Pauli::Z, Pauli::Z, Pauli::X}, + {TQEType::XX, TQEType::XY, TQEType::YX, TQEType::YZ, TQEType::ZY, + TQEType::ZZ}}, + {{Pauli::X, Pauli::Z, Pauli::Z, Pauli::Y}, + {TQEType::XX, TQEType::XY, TQEType::YY, TQEType::YZ, TQEType::ZX, + TQEType::ZZ}}, + {{Pauli::Y, Pauli::X, Pauli::X, Pauli::Y}, + {TQEType::XX, TQEType::XZ, TQEType::YY, TQEType::YZ, TQEType::ZX, + TQEType::ZY}}, + {{Pauli::Y, Pauli::X, Pauli::X, Pauli::Z}, + {TQEType::XX, TQEType::XY, TQEType::YY, TQEType::YZ, TQEType::ZX, + TQEType::ZZ}}, + {{Pauli::Y, Pauli::X, Pauli::Z, Pauli::Y}, + {TQEType::XX, TQEType::XY, TQEType::YY, TQEType::YZ, TQEType::ZX, + TQEType::ZZ}}, + {{Pauli::Y, Pauli::X, Pauli::Z, Pauli::Z}, + {TQEType::XX, TQEType::XZ, TQEType::YY, TQEType::YZ, TQEType::ZX, + TQEType::ZY}}, + {{Pauli::Y, Pauli::Y, Pauli::X, Pauli::X}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::YZ, TQEType::ZX, + TQEType::ZY}}, + {{Pauli::Y, Pauli::Y, Pauli::X, Pauli::Z}, + {TQEType::XX, TQEType::XY, TQEType::YX, TQEType::YZ, TQEType::ZY, + TQEType::ZZ}}, + {{Pauli::Y, Pauli::Y, Pauli::Z, Pauli::X}, + {TQEType::XX, TQEType::XY, TQEType::YX, TQEType::YZ, TQEType::ZY, + TQEType::ZZ}}, + {{Pauli::Y, Pauli::Y, Pauli::Z, Pauli::Z}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::YZ, TQEType::ZX, + TQEType::ZY}}, + {{Pauli::Y, Pauli::Z, Pauli::X, Pauli::X}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZX, + TQEType::ZZ}}, + {{Pauli::Y, Pauli::Z, Pauli::X, Pauli::Y}, + {TQEType::XX, TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZY, + TQEType::ZZ}}, + {{Pauli::Y, Pauli::Z, Pauli::Z, Pauli::X}, + {TQEType::XX, TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZY, + TQEType::ZZ}}, + {{Pauli::Y, Pauli::Z, Pauli::Z, Pauli::Y}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZX, + TQEType::ZZ}}, + {{Pauli::Z, Pauli::X, Pauli::X, Pauli::Y}, + {TQEType::XX, TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZY, + TQEType::ZZ}}, + {{Pauli::Z, Pauli::X, Pauli::X, Pauli::Z}, + {TQEType::XX, TQEType::XY, TQEType::YX, TQEType::YZ, TQEType::ZY, + TQEType::ZZ}}, + {{Pauli::Z, Pauli::X, Pauli::Y, Pauli::Y}, + {TQEType::XX, TQEType::XY, TQEType::YX, TQEType::YZ, TQEType::ZY, + TQEType::ZZ}}, + {{Pauli::Z, Pauli::X, Pauli::Y, Pauli::Z}, + {TQEType::XX, TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZY, + TQEType::ZZ}}, + {{Pauli::Z, Pauli::Y, Pauli::X, Pauli::X}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZX, + TQEType::ZZ}}, + {{Pauli::Z, Pauli::Y, Pauli::X, Pauli::Z}, + {TQEType::XX, TQEType::XY, TQEType::YY, TQEType::YZ, TQEType::ZX, + TQEType::ZZ}}, + {{Pauli::Z, Pauli::Y, Pauli::Y, Pauli::X}, + {TQEType::XX, TQEType::XY, TQEType::YY, TQEType::YZ, TQEType::ZX, + TQEType::ZZ}}, + {{Pauli::Z, Pauli::Y, Pauli::Y, Pauli::Z}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::YY, TQEType::ZX, + TQEType::ZZ}}, + {{Pauli::Z, Pauli::Z, Pauli::X, Pauli::X}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::YZ, TQEType::ZX, + TQEType::ZY}}, + {{Pauli::Z, Pauli::Z, Pauli::X, Pauli::Y}, + {TQEType::XX, TQEType::XZ, TQEType::YY, TQEType::YZ, TQEType::ZX, + TQEType::ZY}}, + {{Pauli::Z, Pauli::Z, Pauli::Y, Pauli::X}, + {TQEType::XX, TQEType::XZ, TQEType::YY, TQEType::YZ, TQEType::ZX, + TQEType::ZY}}, + {{Pauli::Z, Pauli::Z, Pauli::Y, Pauli::Y}, + {TQEType::XY, TQEType::XZ, TQEType::YX, TQEType::YZ, TQEType::ZX, + TQEType::ZY}}}; /** - * @brief Maps a strong support and a weak support in a factor support vector - * to a set of TQE gates that will transform the weak support to no support + * @brief Given (P(0), P(1), Q(0), Q(1)), + * where P(0), Q(0) anti-commute and P(1), Q(1) non-trivially commute (not both + * identity), return the TQE gate that maps P(1), Q(1) to identities. */ const static std::unordered_map< - std::pair, std::vector, hash_pair> - FACTOR_PAIR_SW_TO_SN_TQES = { - {{7, 5}, {TQEType::ZX}}, {{6, 5}, {TQEType::YX}}, - {{7, 4}, {TQEType::YX}}, {{6, 4}, {TQEType::ZX}}, - {{13, 5}, {TQEType::ZX}}, {{14, 5}, {TQEType::XX}}, - {{13, 4}, {TQEType::XX}}, {{14, 4}, {TQEType::ZX}}, - {{9, 5}, {TQEType::YX}}, {{11, 5}, {TQEType::XX}}, - {{9, 4}, {TQEType::XX}}, {{11, 4}, {TQEType::YX}}, - {{7, 15}, {TQEType::ZY}}, {{6, 15}, {TQEType::YY}}, - {{7, 12}, {TQEType::YY}}, {{6, 12}, {TQEType::ZY}}, - {{13, 15}, {TQEType::ZY}}, {{14, 15}, {TQEType::XY}}, - {{13, 12}, {TQEType::XY}}, {{14, 12}, {TQEType::ZY}}, - {{9, 15}, {TQEType::YY}}, {{11, 15}, {TQEType::XY}}, - {{9, 12}, {TQEType::XY}}, {{11, 12}, {TQEType::YY}}, - {{7, 10}, {TQEType::ZZ}}, {{6, 10}, {TQEType::YZ}}, - {{7, 8}, {TQEType::YZ}}, {{6, 8}, {TQEType::ZZ}}, - {{13, 10}, {TQEType::ZZ}}, {{14, 10}, {TQEType::XZ}}, - {{13, 8}, {TQEType::XZ}}, {{14, 8}, {TQEType::ZZ}}, - {{9, 10}, {TQEType::YZ}}, {{11, 10}, {TQEType::XZ}}, - {{9, 8}, {TQEType::XZ}}, {{11, 8}, {TQEType::YZ}}, - {{7, 1}, {TQEType::XX}}, {{6, 1}, {TQEType::XX}}, - {{7, 3}, {TQEType::XY}}, {{6, 3}, {TQEType::XY}}, - {{7, 2}, {TQEType::XZ}}, {{6, 2}, {TQEType::XZ}}, - {{13, 1}, {TQEType::YX}}, {{14, 1}, {TQEType::YX}}, - {{13, 3}, {TQEType::YY}}, {{14, 3}, {TQEType::YY}}, - {{13, 2}, {TQEType::YZ}}, {{14, 2}, {TQEType::YZ}}, - {{9, 1}, {TQEType::ZX}}, {{11, 1}, {TQEType::ZX}}, - {{9, 3}, {TQEType::ZY}}, {{11, 3}, {TQEType::ZY}}, - {{9, 2}, {TQEType::ZZ}}, {{11, 2}, {TQEType::ZZ}}}; + std::tuple, std::vector, + hash_quadruple> + AC_TO_AI_MAP = { + {{Pauli::X, Pauli::X, Pauli::Y, Pauli::X}, {TQEType::ZX}}, + {{Pauli::X, Pauli::X, Pauli::Y, Pauli::I}, {TQEType::YX}}, + {{Pauli::X, Pauli::X, Pauli::Z, Pauli::X}, {TQEType::YX}}, + {{Pauli::X, Pauli::X, Pauli::Z, Pauli::I}, {TQEType::ZX}}, + {{Pauli::X, Pauli::Y, Pauli::Y, Pauli::Y}, {TQEType::ZY}}, + {{Pauli::X, Pauli::Y, Pauli::Y, Pauli::I}, {TQEType::YY}}, + {{Pauli::X, Pauli::Y, Pauli::Z, Pauli::Y}, {TQEType::YY}}, + {{Pauli::X, Pauli::Y, Pauli::Z, Pauli::I}, {TQEType::ZY}}, + {{Pauli::X, Pauli::Z, Pauli::Y, Pauli::Z}, {TQEType::ZZ}}, + {{Pauli::X, Pauli::Z, Pauli::Y, Pauli::I}, {TQEType::YZ}}, + {{Pauli::X, Pauli::Z, Pauli::Z, Pauli::Z}, {TQEType::YZ}}, + {{Pauli::X, Pauli::Z, Pauli::Z, Pauli::I}, {TQEType::ZZ}}, + {{Pauli::X, Pauli::I, Pauli::Y, Pauli::X}, {TQEType::XX}}, + {{Pauli::X, Pauli::I, Pauli::Y, Pauli::Y}, {TQEType::XY}}, + {{Pauli::X, Pauli::I, Pauli::Y, Pauli::Z}, {TQEType::XZ}}, + {{Pauli::X, Pauli::I, Pauli::Z, Pauli::X}, {TQEType::XX}}, + {{Pauli::X, Pauli::I, Pauli::Z, Pauli::Y}, {TQEType::XY}}, + {{Pauli::X, Pauli::I, Pauli::Z, Pauli::Z}, {TQEType::XZ}}, + {{Pauli::Y, Pauli::X, Pauli::X, Pauli::X}, {TQEType::ZX}}, + {{Pauli::Y, Pauli::X, Pauli::X, Pauli::I}, {TQEType::XX}}, + {{Pauli::Y, Pauli::X, Pauli::Z, Pauli::X}, {TQEType::XX}}, + {{Pauli::Y, Pauli::X, Pauli::Z, Pauli::I}, {TQEType::ZX}}, + {{Pauli::Y, Pauli::Y, Pauli::X, Pauli::Y}, {TQEType::ZY}}, + {{Pauli::Y, Pauli::Y, Pauli::X, Pauli::I}, {TQEType::XY}}, + {{Pauli::Y, Pauli::Y, Pauli::Z, Pauli::Y}, {TQEType::XY}}, + {{Pauli::Y, Pauli::Y, Pauli::Z, Pauli::I}, {TQEType::ZY}}, + {{Pauli::Y, Pauli::Z, Pauli::X, Pauli::Z}, {TQEType::ZZ}}, + {{Pauli::Y, Pauli::Z, Pauli::X, Pauli::I}, {TQEType::XZ}}, + {{Pauli::Y, Pauli::Z, Pauli::Z, Pauli::Z}, {TQEType::XZ}}, + {{Pauli::Y, Pauli::Z, Pauli::Z, Pauli::I}, {TQEType::ZZ}}, + {{Pauli::Y, Pauli::I, Pauli::X, Pauli::X}, {TQEType::YX}}, + {{Pauli::Y, Pauli::I, Pauli::X, Pauli::Y}, {TQEType::YY}}, + {{Pauli::Y, Pauli::I, Pauli::X, Pauli::Z}, {TQEType::YZ}}, + {{Pauli::Y, Pauli::I, Pauli::Z, Pauli::X}, {TQEType::YX}}, + {{Pauli::Y, Pauli::I, Pauli::Z, Pauli::Y}, {TQEType::YY}}, + {{Pauli::Y, Pauli::I, Pauli::Z, Pauli::Z}, {TQEType::YZ}}, + {{Pauli::Z, Pauli::X, Pauli::X, Pauli::X}, {TQEType::YX}}, + {{Pauli::Z, Pauli::X, Pauli::X, Pauli::I}, {TQEType::XX}}, + {{Pauli::Z, Pauli::X, Pauli::Y, Pauli::X}, {TQEType::XX}}, + {{Pauli::Z, Pauli::X, Pauli::Y, Pauli::I}, {TQEType::YX}}, + {{Pauli::Z, Pauli::Y, Pauli::X, Pauli::Y}, {TQEType::YY}}, + {{Pauli::Z, Pauli::Y, Pauli::X, Pauli::I}, {TQEType::XY}}, + {{Pauli::Z, Pauli::Y, Pauli::Y, Pauli::Y}, {TQEType::XY}}, + {{Pauli::Z, Pauli::Y, Pauli::Y, Pauli::I}, {TQEType::YY}}, + {{Pauli::Z, Pauli::Z, Pauli::X, Pauli::Z}, {TQEType::YZ}}, + {{Pauli::Z, Pauli::Z, Pauli::X, Pauli::I}, {TQEType::XZ}}, + {{Pauli::Z, Pauli::Z, Pauli::Y, Pauli::Z}, {TQEType::XZ}}, + {{Pauli::Z, Pauli::Z, Pauli::Y, Pauli::I}, {TQEType::YZ}}, + {{Pauli::Z, Pauli::I, Pauli::X, Pauli::X}, {TQEType::ZX}}, + {{Pauli::Z, Pauli::I, Pauli::X, Pauli::Y}, {TQEType::ZY}}, + {{Pauli::Z, Pauli::I, Pauli::X, Pauli::Z}, {TQEType::ZZ}}, + {{Pauli::Z, Pauli::I, Pauli::Y, Pauli::X}, {TQEType::ZX}}, + {{Pauli::Z, Pauli::I, Pauli::Y, Pauli::Y}, {TQEType::ZY}}, + {{Pauli::Z, Pauli::I, Pauli::Y, Pauli::Z}, {TQEType::ZZ}}}; } // namespace GreedyPauliSimp From 8d3f9f310d71db74d6524b5aafcfaf2630b0413c Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 24 Sep 2024 16:36:59 +0100 Subject: [PATCH 02/57] Refactor node types --- .../GreedyPauliOptimisation.hpp | 182 ++++++--- .../GreedyPauliOptimisation.cpp | 357 ++++++++++-------- 2 files changed, 334 insertions(+), 205 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index ac4c0e10e2..7534a4f67a 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -23,6 +23,12 @@ namespace Transforms { namespace GreedyPauliSimp { +class GreedyPauliSimpError : public std::logic_error { + public: + explicit GreedyPauliSimpError(const std::string& message) + : std::logic_error(message) {} +}; + /** * @brief Types of 2-qubit entangled Clifford gates * @@ -39,18 +45,26 @@ enum class TQEType : unsigned { ZZ, }; +enum class PauliNodeType { + // Pauli rotation + Rotation, + // Defines how a Pauli X and a Pauli Z on the same qubit + // get propagated from right to left through a Clifford operator. + Propagation, +} + /** * @brief The type of a pair of Pauli letters defined by their commutation relation - * + * */ -enum class COMMUTE_TYPE : unsigned { - // Both are identity - Identity, - // Anti-commute - AntiCommute, - // Commute and not both identity - Commute, +enum class CommuteType : unsigned { + // Both are (I)dentity + I, + // (A)nti-commute + A, + // (C)ommute and not both identity + C, }; /** @@ -59,26 +73,40 @@ enum class COMMUTE_TYPE : unsigned { */ using TQE = std::tuple; +// Forwards declarations +class PauliNode; +typedef std::shared_ptr PauliNode_ptr; + +class PauliNode { + public: + PauliNodeType get_type() const const = 0; + virtual unsigned tqe_cost() const = 0; + virtual int tqe_cost_increase(const TQE& tqe) const = 0; + virtual void update(const TQE& tqe) = 0; + virtual void update(const OpType& sq_cliff, const unsigned& a); + virtual void swap(const unsigned& a, const unsigned& b); + virtual std::vector reduction_tqes() const = 0; + virtual ~PauliNode(); +} + /** - * @brief A Pauli exponential defined by a padded Pauli string - * and a rotation angle + * @brief A node defined by a single Pauli string */ -class PauliRotation { +class SingleNode : public PauliNode { public: /** - * @brief Construct a new PauliRotation object. + * @brief Construct a new SinglePauliNode object. * * @param string the Pauli string - * @param theta the rotation angle in half-turns */ - PauliRotation(std::vector string, Expr theta); + SingleNode(std::vector string, bool sign); /** * @brief Number of TQEs required to reduce the weight to 1 * * @return unsigned */ - unsigned tqe_cost() const { return weight_; } + unsigned tqe_cost() const override; /** * @brief Number of TQEs would required to reduce the weight to 1 @@ -86,23 +114,21 @@ class PauliRotation { * * @return unsigned */ - int tqe_cost_increase(const TQE& tqe) const; + int tqe_cost_increase(const TQE& tqe) const override; /** - * @brief Update the support vector with a TQE gate + * @brief Update the Pauli string with a TQE gate * * @param tqe */ - void update(const TQE& tqe); - - Expr theta() const { return theta_; }; + void update(const TQE& tqe) override; /** * @brief Return all possible TQE gates that will reduce the tqe cost by 1 * * @return std::vector> */ - std::vector reduction_tqes() const; + std::vector reduction_tqes() const override; /** * @brief Return the index and value of the first non-identity @@ -111,40 +137,36 @@ class PauliRotation { */ std::pair first_support() const; - private: + protected: std::vector string_; - Expr theta_; + bool sign_; // extra cached data used by greedy synthesis unsigned weight_; }; /** - * @brief Defines how a Pauli X and a Pauli Z on the same qubit - * get propagated from right to left through a Clifford operator. - * A n-qubit Clifford operator is completely defined by n such propagations - * with one on each qubit. A PauliPropagation also corresponds to a row in - * a Clifford tableau + * @brief Node consists of a pair of anti-commuting Pauli strings */ -class PauliPropagation { +class ACPairNode : public PauliNode { public: - /** - * @brief Construct a new PauliPropagation object - * - * @param z_propagation - * @param x_propagation - * @param z_sign - * @param x_sign - * @param qubit_index + * @brief Construct a new ACPairNode object + * + * @param z_propagation + * @param x_propagation + * @param z_sign + * @param x_sign */ - PauliPropagation(std::vector z_propagation,std::vector x_propagation, bool z_sign, bool x_sign, unsigned qubit_index); + ACPairNode( + std::vector z_propagation, std::vector x_propagation, + bool z_sign, bool x_sign); /** * @brief Number of TQEs required to reduce the weight to 1 * * @return unsigned */ - unsigned tqe_cost() const { return tqe_cost_; }; + unsigned tqe_cost() const override; /** * @brief Number of TQEs would required to reduce the weight to 1 @@ -152,53 +174,103 @@ class PauliPropagation { * * @return unsigned */ - int tqe_cost_increase(const TQE& tqe) const; + int tqe_cost_increase(const TQE& tqe) const override; /** * @brief Update the support vector with a TQE gate - * - * @param tqe + * + * @param tqe */ - void update(const TQE& tqe); + void update(const TQE& tqe) override; /** * @brief Update the support vector with a single-qubit Clifford gate - * - * @param tqe + * + * @param tqe */ - void update(const OpType& sq_cliff, const unsigned& a); + void update(const OpType& sq_cliff, const unsigned& a) override; /** * @brief Update the support vector with a SWAP gate - * - * @param tqe + * + * @param tqe */ - void swap(const unsigned& a, const unsigned& a); + void swap(const unsigned& a, const unsigned& b) override; /** * @brief Return all possible TQE gates that will reduce the tqe cost - * - * @return std::vector + * + * @return std::vector */ - std::vector reduction_tqes() const; + std::vector reduction_tqes() const override; /** * @brief Return the index and value of the first anti-commute entry */ std::tuple first_support() const; - private: + protected: std::vector z_propagation_; std::vector x_propagation_; bool z_sign_; bool x_sign_; - unsigned qubit_index_; // extra cached data used by greedy synthesis - std::vector commute_type_vec_; + std::vector commute_type_vec_; unsigned n_commute_entries_; unsigned n_anti_commute_entries_; }; +/** + * @brief A Pauli exponential defined by a padded Pauli string + * and a rotation angle + */ +class PauliRotation : public SingleNode { + public: + /** + * @brief Construct a new PauliRotation object. + * + * @param string the Pauli string + * @param theta the rotation angle in half-turns + */ + PauliRotation(std::vector string, Expr theta); + + PauliNodeType get_type() const { return PauliNodeType::Rotation; }; + + Expr theta() const { return theta_; }; + + private: + Expr theta_; +}; + +/** + * @brief Defines how a Pauli X and a Pauli Z on the same qubit + * get propagated from right to left through a Clifford operator. + * A n-qubit Clifford operator is completely defined by n such propagations + * with one on each qubit. A PauliPropagation also corresponds to a row in + * a Clifford tableau + */ +class PauliPropagation : public ACPairNode { + public: + /** + * @brief Construct a new PauliPropagation object + * + * @param z_propagation + * @param x_propagation + * @param z_sign + * @param x_sign + * @param qubit_index + */ + PauliPropagation( + std::vector z_propagation, std::vector x_propagation, + bool z_sign, bool x_sign, unsigned qubit_index); + + PauliNodeType get_type() const { return PauliNodeType::Propagation; }; + + unsigned qubit_index() const { return qubit_index_; }; + + private: + unsigned qubit_index_; +}; /** * @brief Given a circuit consists of PauliExpBoxes followed by clifford gates, diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 9515ddbbc5..6d0b6e30cd 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -71,88 +71,82 @@ static void apply_tqe_to_circ(const TQE& tqe, Circuit& circ) { } } -static void apply_tqe_to_tableau(const TQE& tqe, UnitaryRevTableau& tab) { - auto [gate_type, a_int, b_int] = tqe; - Qubit a(a_int); - Qubit b(b_int); - switch (gate_type) { - case TQEType::XX: - tab.apply_gate_at_end(OpType::H, {a}); - tab.apply_gate_at_end(OpType::CX, {a, b}); - tab.apply_gate_at_end(OpType::H, {a}); - break; - case TQEType::XY: - tab.apply_gate_at_end(OpType::H, {a}); - tab.apply_gate_at_end(OpType::CY, {a, b}); - tab.apply_gate_at_end(OpType::H, {a}); - break; - case TQEType::XZ: - tab.apply_gate_at_end(OpType::CX, {b, a}); - break; - case TQEType::YX: - tab.apply_gate_at_end(OpType::H, {b}); - tab.apply_gate_at_end(OpType::CY, {b, a}); - tab.apply_gate_at_end(OpType::H, {b}); - break; - case TQEType::YY: - tab.apply_gate_at_end(OpType::V, {a}); - tab.apply_gate_at_end(OpType::CY, {a, b}); - tab.apply_gate_at_end(OpType::Vdg, {a}); - break; - case TQEType::YZ: - tab.apply_gate_at_end(OpType::CY, {b, a}); - break; - case TQEType::ZX: - tab.apply_gate_at_end(OpType::CX, {a, b}); - break; - case TQEType::ZY: - tab.apply_gate_at_end(OpType::CY, {a, b}); - break; - case TQEType::ZZ: - tab.apply_gate_at_end(OpType::CZ, {a, b}); - break; +static CommuteType get_pauli_pair_commute_type( + const Pauli& p0, const Pauli& p1) { + if (p0 == Pauli::I && p1 == Pauli::I) { + return CommuteType.I; } + if (p0 == p1 || p0 == Pauli::I || p1 == Pauli::I) { + return CommuteType.C; + } + return CommuteType.A; } -PauliExpNode::PauliExpNode(std::vector support_vec, Expr theta) - : support_vec_(support_vec), theta_(theta) { - tqe_cost_ = support_vec_.size() - - std::count(support_vec_.begin(), support_vec_.end(), 0) - 1; + + +/******************************************************************************* + * Nodes implementation + ******************************************************************************/ + +// PauliNode abstract class + +PauliNodeType PauliNode::get_type() const { return type_; } + +PauliNode::~PauliNode() {} + +PauliNode::PauliNode(PauliNodeType type) : type_(type) {} + +PauliNode::update(const OpType& /*sq_cliff*/, const unsigned& /*a*/) { + throw GreedyPauliSimpError("Single qubit Clifford update not implemented"); +} + +PauliNode::swap(const unsigned& /*a*/, const unsigned& /*b*/) { + throw GreedyPauliSimpError("SWAP update not implemented"); +} + +// SingleNode + +SingleNode::SingleNode(std::vector string, bool sign) + : string_(string), sign_(sign) { + weight_ = + string_.size() - std::count(string_.begin(), string_.end(), Pauli.I); } -int PauliExpNode::tqe_cost_increase(const TQE& tqe) const { - unsigned supp0 = support_vec_[std::get<1>(tqe)]; - unsigned supp1 = support_vec_[std::get<2>(tqe)]; - unsigned new_supp0, new_supp1; - std::tie(new_supp0, new_supp1) = - SINGLET_PAIR_TRANSFORMATION_MAP.at({std::get<0>(tqe), supp0, supp1}); - return (new_supp0 > 0) + (new_supp1 > 0) - (supp0 > 0) - (supp1 > 0); +unsigned PauliRotation::tqe_cost() const { return weight_ - 1; } + +int SingleNode::tqe_cost_increase(const TQE& tqe) const { + auto [g, a, b] = tqe; + Pauli p0 = string_[a]; + Pauli p1 = string_[b]; + auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); + return (p0 == Pauli::I) + (p1 == Pauli::I) - (new_p0 == Pauli.I) - + (new_p1 == Pauli::I); } -void PauliExpNode::update(const TQE& tqe) { - unsigned a = std::get<1>(tqe); - unsigned b = std::get<2>(tqe); - unsigned supp0 = support_vec_[a]; - unsigned supp1 = support_vec_[b]; - unsigned new_supp0, new_supp1; - std::tie(new_supp0, new_supp1) = - SINGLET_PAIR_TRANSFORMATION_MAP.at({std::get<0>(tqe), supp0, supp1}); - support_vec_[a] = new_supp0; - support_vec_[b] = new_supp1; - tqe_cost_ += (new_supp0 > 0) + (new_supp1 > 0) - (supp0 > 0) - (supp1 > 0); +void SingleNode::update(const TQE& tqe) { + auto [g, a, b] = tqe; + unsigned p0 = string_[a]; + unsigned p1 = string_[b]; + auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); + string_[a] = new_p0; + string_[b] = new_p1; + weight_ += (p0 == Pauli::I) + (p1 == Pauli::I) - (new_p0 == Pauli.I) - + (new_p1 == Pauli::I); + if (!sign): + sign_ = ! sign_; } -std::vector PauliExpNode::reduction_tqes() const { +std::vector SingleNode::reduction_tqes() const { std::vector tqes; // qubits with support std::vector sqs; - for (unsigned i = 0; i < support_vec_.size(); i++) { - if (support_vec_[i] > 0) sqs.push_back(i); + for (unsigned i = 0; i < string_.size(); i++) { + if (string_[i] != Pauli.I) sqs.push_back(i); } for (unsigned i = 0; i < sqs.size() - 1; i++) { for (unsigned j = i + 1; j < sqs.size(); j++) { - std::vector tqe_types = SINGLET_PAIR_REDUCTION_TQES.at( - {support_vec_[sqs[i]], support_vec_[sqs[j]]}); + std::vector tqe_types = + TQE_REDUCTION_MAP.at({string_[sqs[i]], string_[sqs[j]]}); for (const TQEType& tt : tqe_types) { tqes.push_back({tt, sqs[i], sqs[j]}); } @@ -161,116 +155,163 @@ std::vector PauliExpNode::reduction_tqes() const { return tqes; } -std::pair PauliExpNode::first_support() const { - for (unsigned i = 0; i < support_vec_.size(); i++) { - if (support_vec_[i] > 0) { - return {i, support_vec_[i]}; +std::pair SingleNode::first_support() const { + for (unsigned i = 0; i < string_.size(); i++) { + if (string_[i] != Pauli.I) { + return {i, string_[i]}; } } // Should be impossible to reach here TKET_ASSERT(false); } -TableauRowNode::TableauRowNode(std::vector support_vec) - : support_vec_(support_vec) { - n_weaks_ = 0; - n_strongs_ = 0; - for (const unsigned& supp : support_vec_) { - SupportType st = FACTOR_WEAKNESS_MAP.at(supp); - if (st == SupportType::Strong) { - n_strongs_++; - } else if (st == SupportType::Weak) { - n_weaks_++; +// ACPairNode + +ACPairNode::ACPairNode( + std::vector z_propagation, std::vector x_propagation, + bool z_sign, bool x_sign) + : z_propagation_(z_propagation), + x_propagation_(x_propagation), + z_sign_(z_sign), + x_sign_(x_sign) { + n_commute_entries_ = 0; + n_anti_commute_entries_ = 0; + for (unsigned i = 0; i < z_propagation_.size(); i++) { + CommuteType commute_type = + get_pauli_pair_commute_type(z_propagation_[i], x_propagation_[i]); + commute_type_vec_.push_back(commute_type); + if (commute_type == CommuteType.C) { + n_commute_entries_ += 1; } + if (commute_type == CommuteType.A) { + n_anti_commute_entries_ += 1; + } + } +} + +unsigned ACPairNode::tqe_cost() const { + return static_cast( + 1.5 * (n_anti_commute_entries_ - 1) + n_commute_entries_); +} + +int ACPairNode::tqe_cost_increase(const TQE& tqe) const { + auto [g, a, b] = tqe; + unsigned z_p0 = z_propagation_[a]; + unsigned z_p1 = z_propagation_[b]; + unsigned x_p0 = x_propagation_[a]; + unsigned x_p1 = x_propagation_[b]; + auto [new_z_p0, new_z_p1, z_sign] = TQE_PAULI_MAP.at({g, z_p0, z_p1}); + auto [new_x_p0, new_x_p1, x_sign] = TQE_PAULI_MAP.at({g, x_p0, x_p1}); + CommuteType new_a_type = get_pauli_pair_commute_type(new_z_p0, new_x_p0); + CommuteType new_b_type = get_pauli_pair_commute_type(new_z_p1, new_x_p1); + unsigned old_anti_commutes = (commute_type_vec_[a] == CommuteType::A) + + (commute_type_vec_[b] == CommuteType::A); + unsigned old_commutes = (commute_type_vec_[a] == CommuteType::C) + + (commute_type_vec_[b] == CommuteType::C); + unsigned new_anti_commutes = + (new_a_type == CommuteType::A) + (new_b_type == CommuteType::A); + unsigned new_commutes = + (new_a_type == CommuteType::C) + (new_b_type == CommuteType::C); + int anti_commute_increase = new_anti_commutes - old_anti_commutes; + int commute_increase = new_commutes - old_commutes; + return static_cast(1.5 * anti_commute_increase + commute_increase); +} + +void ACPairNode::update(const TQE& tqe) { + auto [g, a, b] = tqe; + unsigned z_p0 = z_propagation_[a]; + unsigned z_p1 = z_propagation_[b]; + unsigned x_p0 = x_propagation_[a]; + unsigned x_p1 = x_propagation_[b]; + auto [new_z_p0, new_z_p1, z_sign] = TQE_PAULI_MAP.at({g, z_p0, z_p1}); + auto [new_x_p0, new_x_p1, x_sign] = TQE_PAULI_MAP.at({g, x_p0, x_p1}); + CommuteType new_a_type = get_pauli_pair_commute_type(new_z_p0, new_x_p0); + CommuteType new_b_type = get_pauli_pair_commute_type(new_z_p1, new_x_p1); + unsigned old_anti_commutes = (commute_type_vec_[a] == CommuteType::A) + + (commute_type_vec_[b] == CommuteType::A); + unsigned old_commutes = (commute_type_vec_[a] == CommuteType::C) + + (commute_type_vec_[b] == CommuteType::C); + unsigned new_anti_commutes = + (new_a_type == CommuteType::A) + (new_b_type == CommuteType::A); + unsigned new_commutes = + (new_a_type == CommuteType::C) + (new_b_type == CommuteType::C); + int anti_commute_increase = new_anti_commutes - old_anti_commutes; + int commute_increase = new_commutes - old_commutes; + n_anti_commute_entries_ += anti_commute_increase; + n_commute_entries_ += commute_increase; + commute_type_vec_[a] = new_a_type; + commute_type_vec_[b] = new_b_type; + z_propagation_[a] = new_z_p0; + z_propagation_[b] = new_z_p1; + x_propagation_[a] = new_x_p0; + x_propagation_[b] = new_x_p1; + if (!z_sign) { + z_sign_ = !z_sign_; + } + if (!x_sign) { + x_sign_ = !x_sign_; } - tqe_cost_ = static_cast(1.5 * (n_strongs_ - 1) + n_weaks_); } -int TableauRowNode::tqe_cost_increase(const TQE& tqe) const { - unsigned supp0 = support_vec_[std::get<1>(tqe)]; - unsigned supp1 = support_vec_[std::get<2>(tqe)]; - unsigned new_supp0, new_supp1; - std::tie(new_supp0, new_supp1) = - FACTOR_PAIR_TRANSFORMATION_MAP.at({std::get<0>(tqe), supp0, supp1}); - SupportType st_supp0 = FACTOR_WEAKNESS_MAP.at(supp0); - SupportType st_supp1 = FACTOR_WEAKNESS_MAP.at(supp1); - SupportType st_new_supp0 = FACTOR_WEAKNESS_MAP.at(new_supp0); - SupportType st_new_supp1 = FACTOR_WEAKNESS_MAP.at(new_supp1); - unsigned old_strongs = - (st_supp0 == SupportType::Strong) + (st_supp1 == SupportType::Strong); - unsigned old_weaks = - (st_supp0 == SupportType::Weak) + (st_supp1 == SupportType::Weak); - unsigned new_strongs = (st_new_supp0 == SupportType::Strong) + - (st_new_supp1 == SupportType::Strong); - unsigned new_weaks = - (st_new_supp0 == SupportType::Weak) + (st_new_supp1 == SupportType::Weak); - int strong_increase = new_strongs - old_strongs; - int weak_increase = new_weaks - old_weaks; - return static_cast(1.5 * strong_increase + weak_increase); +void ACPairNode::update(const OpType& sq_cliff, const unsigned& a) { + auto [new_z_p, z_sign] = + SQ_CLIFF_MAP.at({sq_cliff, z_propagation_[a]}) auto [new_x_p, x_sign] = + SQ_CLIFF_MAP.at({sq_cliff, x_propagation_[a]}) z_propagation_[a] = + new_z_p x_propagation_[a] = new_x_p if (!z_sign) { + z_sign_ = !z_sign_; + } + if (!x_sign) { + x_sign_ = !x_sign_; + } } -void TableauRowNode::update(const TQE& tqe) { - unsigned a = std::get<1>(tqe); - unsigned b = std::get<2>(tqe); - unsigned supp0 = support_vec_[a]; - unsigned supp1 = support_vec_[b]; - unsigned new_supp0, new_supp1; - std::tie(new_supp0, new_supp1) = - FACTOR_PAIR_TRANSFORMATION_MAP.at({std::get<0>(tqe), supp0, supp1}); - support_vec_[a] = new_supp0; - support_vec_[b] = new_supp1; - SupportType st_supp0 = FACTOR_WEAKNESS_MAP.at(supp0); - SupportType st_supp1 = FACTOR_WEAKNESS_MAP.at(supp1); - SupportType st_new_supp0 = FACTOR_WEAKNESS_MAP.at(new_supp0); - SupportType st_new_supp1 = FACTOR_WEAKNESS_MAP.at(new_supp1); - unsigned old_strongs = - (st_supp0 == SupportType::Strong) + (st_supp1 == SupportType::Strong); - unsigned old_weaks = - (st_supp0 == SupportType::Weak) + (st_supp1 == SupportType::Weak); - unsigned new_strongs = (st_new_supp0 == SupportType::Strong) + - (st_new_supp1 == SupportType::Strong); - unsigned new_weaks = - (st_new_supp0 == SupportType::Weak) + (st_new_supp1 == SupportType::Weak); - n_strongs_ += new_strongs - old_strongs; - n_weaks_ += new_weaks - old_weaks; - tqe_cost_ = static_cast(1.5 * (n_strongs_ - 1) + n_weaks_); +void ACPairNode::swap(const unsigned& a, const unsigned& b) { + std::swap(z_propagation_[a], z_propagation_[b]); + std::swap(x_propagation_[a], x_propagation_[b]); + std::swap(commute_type_vec_[a], commute_type_vec_[b]); } -std::vector TableauRowNode::reduction_tqes() const { +std::vector ACPairNode::reduction_tqes() const { std::vector tqes; // qubits with support std::vector sqs; - for (unsigned i = 0; i < support_vec_.size(); i++) { - if (support_vec_[i] > 0) sqs.push_back(i); + for (unsigned i = 0; i < commute_type_vec_.size(); i++) { + if (commute_type_vec_[i] != CommuteType::I) sqs.push_back(i); } for (unsigned i = 0; i < sqs.size() - 1; i++) { for (unsigned j = i + 1; j < sqs.size(); j++) { std::vector tqe_types; unsigned a = sqs[i]; unsigned b = sqs[j]; - unsigned supp0 = support_vec_[a]; - unsigned supp1 = support_vec_[b]; - SupportType st_supp0 = FACTOR_WEAKNESS_MAP.at(supp0); - SupportType st_supp1 = FACTOR_WEAKNESS_MAP.at(supp1); - if (st_supp0 == SupportType::Strong) { - if (st_supp1 == SupportType::Strong) { - // TQEs that transform a SS pair to WW - tqe_types = FACTOR_PAIR_SS_TO_WW_TQES.at({supp0, supp1}); + CommuteType ctype0 = commute_type_vec_[a]; + CommuteType ctype1 = commute_type_vec_[b]; + if (ctype0 == CommuteType::A) { + if (ctype1 == CommuteType::A) { + // TQEs that transform a AA pair to CC + tqe_types = AA_TO_CC_MAP.at( + {z_propagation_[a], z_propagation_[b], x_propagation_[a], + x_propagation_[b]}); } else { - // TQEs that transform a SW pair to a single strong - tqe_types = FACTOR_PAIR_SW_TO_SN_TQES.at({supp0, supp1}); + // TQEs that transform a AC pair to AI + tqe_types = AC_TO_AI_MAP.at( + {z_propagation_[a], z_propagation_[b], x_propagation_[a], + x_propagation_[b]}); } } else { - if (st_supp1 == SupportType::Strong) { - // TQEs that transform a WS pair to a single strong - tqe_types = FACTOR_PAIR_SW_TO_SN_TQES.at({supp1, supp0}); + if (ctype1 == CommuteType::A) { + // TQEs that transform a CA pair to a IA + tqe_types = AC_TO_AI_MAP.at( + {z_propagation_[b], z_propagation_[a], x_propagation_[b], + x_propagation_[a]}); // flip qubits a = sqs[j]; b = sqs[i]; } else { - // TQEs that transform a WW pair to a single weak, not always + // TQEs that transform a CC pair to CI or IC, not always // possible - tqe_types = FACTOR_PAIR_WW_TO_WN_OR_NW_TQES.at({supp0, supp1}); + tqe_types = CC_TO_IC_OR_CI_MAP.at( + {z_propagation_[a], z_propagation_[b], x_propagation_[a], + x_propagation_[b]}); } } for (const TQEType& tt : tqe_types) { @@ -281,16 +322,27 @@ std::vector TableauRowNode::reduction_tqes() const { return tqes; } -std::pair TableauRowNode::first_support() const { - for (unsigned i = 0; i < support_vec_.size(); i++) { - if (support_vec_[i] > 0) { - return {i, support_vec_[i]}; +std::tuple ACPairNode::first_support() const { + for (unsigned i = 0; i < commute_type_vec_.size(); i++) { + if (commute_type_vec_[i] != CommuteType::I) { + return {i, z_propagation_[i], x_propagation_[i]}; } } // Should be impossible to reach here TKET_ASSERT(false); } +// PauliRotation +PauliRotation::PauliRotation(std::vector string, Expr theta) + : SingleNode(string, true), theta_(theta) {} + +// PauliPropagation +PauliPropagation::PauliPropagation( + std::vector z_propagation, std::vector x_propagation, + bool z_sign, bool x_sign, unsigned qubit_index) + : ACPairNode(z_propagation, x_propagation, z_sign, x_sign), + qubit_index_(qubit_index) {} + // return the sum of the cost increases on remaining tableau nodes static double default_tableau_tqe_cost( const std::vector& rows, @@ -302,6 +354,11 @@ static double default_tableau_tqe_cost( return cost; } + +/******************************************************************************* + * Synthesis + ******************************************************************************/ + // return the weighted sum of the cost increases on remaining nodes // we discount the weight after each set static double default_pauliexp_tqe_cost( From 4c41f1c5fd49c8a407cb9ce2e152be53d97a0e08 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 24 Sep 2024 16:40:09 +0100 Subject: [PATCH 03/57] Move nodes definition into a new file --- tket/CMakeLists.txt | 1 + tket/src/Transformations/GreedyPauliOps.cpp | 312 ++++++++++++++++++ .../GreedyPauliOptimisation.cpp | 288 ---------------- 3 files changed, 313 insertions(+), 288 deletions(-) create mode 100644 tket/src/Transformations/GreedyPauliOps.cpp diff --git a/tket/CMakeLists.txt b/tket/CMakeLists.txt index 2d5a40a0d3..e9ef5f60c9 100644 --- a/tket/CMakeLists.txt +++ b/tket/CMakeLists.txt @@ -269,6 +269,7 @@ target_sources(tket src/Transformations/PhaseOptimisation.cpp src/Transformations/Decomposition.cpp src/Transformations/GreedyPauliOptimisation.cpp + src/Transformations/GreedyPauliOps.cpp src/Transformations/Replacement.cpp src/Transformations/MeasurePass.cpp src/Transformations/ContextualReduction.cpp diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp new file mode 100644 index 0000000000..124f1275c8 --- /dev/null +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -0,0 +1,312 @@ +// Copyright 2019-2024 Cambridge Quantum Computing +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tket/Transformations/GreedyPauliOptimisation.hpp" + +#include + +#include "tket/Circuit/PauliExpBoxes.hpp" +#include "tket/Converters/Converters.hpp" +#include "tket/OpType/OpType.hpp" +#include "tket/PauliGraph/PauliGraph.hpp" +#include "tket/Transformations/CliffordOptimisation.hpp" +#include "tket/Transformations/GreedyPauliOptimisationLookupTables.hpp" +#include "tket/Transformations/Transform.hpp" + +namespace tket { + +namespace Transforms { + +namespace GreedyPauliSimp { + +static CommuteType get_pauli_pair_commute_type( + const Pauli& p0, const Pauli& p1) { + if (p0 == Pauli::I && p1 == Pauli::I) { + return CommuteType.I; + } + if (p0 == p1 || p0 == Pauli::I || p1 == Pauli::I) { + return CommuteType.C; + } + return CommuteType.A; +} + +// PauliNode abstract class + +PauliNodeType PauliNode::get_type() const { return type_; } + +PauliNode::~PauliNode() {} + +PauliNode::PauliNode(PauliNodeType type) : type_(type) {} + +PauliNode::update(const OpType& /*sq_cliff*/, const unsigned& /*a*/) { + throw GreedyPauliSimpError("Single qubit Clifford update not implemented"); +} + +PauliNode::swap(const unsigned& /*a*/, const unsigned& /*b*/) { + throw GreedyPauliSimpError("SWAP update not implemented"); +} + +// SingleNode + +SingleNode::SingleNode(std::vector string, bool sign) + : string_(string), sign_(sign) { + weight_ = + string_.size() - std::count(string_.begin(), string_.end(), Pauli.I); +} + +unsigned PauliRotation::tqe_cost() const { return weight_ - 1; } + +int SingleNode::tqe_cost_increase(const TQE& tqe) const { + auto [g, a, b] = tqe; + Pauli p0 = string_[a]; + Pauli p1 = string_[b]; + auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); + return (p0 == Pauli::I) + (p1 == Pauli::I) - (new_p0 == Pauli.I) - + (new_p1 == Pauli::I); +} + +void SingleNode::update(const TQE& tqe) { + auto [g, a, b] = tqe; + unsigned p0 = string_[a]; + unsigned p1 = string_[b]; + auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); + string_[a] = new_p0; + string_[b] = new_p1; + weight_ += (p0 == Pauli::I) + (p1 == Pauli::I) - (new_p0 == Pauli.I) - + (new_p1 == Pauli::I); + if (!sign): + sign_ = ! sign_; +} + +std::vector SingleNode::reduction_tqes() const { + std::vector tqes; + // qubits with support + std::vector sqs; + for (unsigned i = 0; i < string_.size(); i++) { + if (string_[i] != Pauli.I) sqs.push_back(i); + } + for (unsigned i = 0; i < sqs.size() - 1; i++) { + for (unsigned j = i + 1; j < sqs.size(); j++) { + std::vector tqe_types = + TQE_REDUCTION_MAP.at({string_[sqs[i]], string_[sqs[j]]}); + for (const TQEType& tt : tqe_types) { + tqes.push_back({tt, sqs[i], sqs[j]}); + } + } + } + return tqes; +} + +std::pair SingleNode::first_support() const { + for (unsigned i = 0; i < string_.size(); i++) { + if (string_[i] != Pauli.I) { + return {i, string_[i]}; + } + } + // Should be impossible to reach here + TKET_ASSERT(false); +} + +// ACPairNode + +ACPairNode::ACPairNode( + std::vector z_propagation, std::vector x_propagation, + bool z_sign, bool x_sign) + : z_propagation_(z_propagation), + x_propagation_(x_propagation), + z_sign_(z_sign), + x_sign_(x_sign) { + n_commute_entries_ = 0; + n_anti_commute_entries_ = 0; + for (unsigned i = 0; i < z_propagation_.size(); i++) { + CommuteType commute_type = + get_pauli_pair_commute_type(z_propagation_[i], x_propagation_[i]); + commute_type_vec_.push_back(commute_type); + if (commute_type == CommuteType.C) { + n_commute_entries_ += 1; + } + if (commute_type == CommuteType.A) { + n_anti_commute_entries_ += 1; + } + } +} + +unsigned ACPairNode::tqe_cost() const { + return static_cast( + 1.5 * (n_anti_commute_entries_ - 1) + n_commute_entries_); +} + +int ACPairNode::tqe_cost_increase(const TQE& tqe) const { + auto [g, a, b] = tqe; + unsigned z_p0 = z_propagation_[a]; + unsigned z_p1 = z_propagation_[b]; + unsigned x_p0 = x_propagation_[a]; + unsigned x_p1 = x_propagation_[b]; + auto [new_z_p0, new_z_p1, z_sign] = TQE_PAULI_MAP.at({g, z_p0, z_p1}); + auto [new_x_p0, new_x_p1, x_sign] = TQE_PAULI_MAP.at({g, x_p0, x_p1}); + CommuteType new_a_type = get_pauli_pair_commute_type(new_z_p0, new_x_p0); + CommuteType new_b_type = get_pauli_pair_commute_type(new_z_p1, new_x_p1); + unsigned old_anti_commutes = (commute_type_vec_[a] == CommuteType::A) + + (commute_type_vec_[b] == CommuteType::A); + unsigned old_commutes = (commute_type_vec_[a] == CommuteType::C) + + (commute_type_vec_[b] == CommuteType::C); + unsigned new_anti_commutes = + (new_a_type == CommuteType::A) + (new_b_type == CommuteType::A); + unsigned new_commutes = + (new_a_type == CommuteType::C) + (new_b_type == CommuteType::C); + int anti_commute_increase = new_anti_commutes - old_anti_commutes; + int commute_increase = new_commutes - old_commutes; + return static_cast(1.5 * anti_commute_increase + commute_increase); +} + +void ACPairNode::update(const TQE& tqe) { + auto [g, a, b] = tqe; + unsigned z_p0 = z_propagation_[a]; + unsigned z_p1 = z_propagation_[b]; + unsigned x_p0 = x_propagation_[a]; + unsigned x_p1 = x_propagation_[b]; + auto [new_z_p0, new_z_p1, z_sign] = TQE_PAULI_MAP.at({g, z_p0, z_p1}); + auto [new_x_p0, new_x_p1, x_sign] = TQE_PAULI_MAP.at({g, x_p0, x_p1}); + CommuteType new_a_type = get_pauli_pair_commute_type(new_z_p0, new_x_p0); + CommuteType new_b_type = get_pauli_pair_commute_type(new_z_p1, new_x_p1); + unsigned old_anti_commutes = (commute_type_vec_[a] == CommuteType::A) + + (commute_type_vec_[b] == CommuteType::A); + unsigned old_commutes = (commute_type_vec_[a] == CommuteType::C) + + (commute_type_vec_[b] == CommuteType::C); + unsigned new_anti_commutes = + (new_a_type == CommuteType::A) + (new_b_type == CommuteType::A); + unsigned new_commutes = + (new_a_type == CommuteType::C) + (new_b_type == CommuteType::C); + int anti_commute_increase = new_anti_commutes - old_anti_commutes; + int commute_increase = new_commutes - old_commutes; + n_anti_commute_entries_ += anti_commute_increase; + n_commute_entries_ += commute_increase; + commute_type_vec_[a] = new_a_type; + commute_type_vec_[b] = new_b_type; + z_propagation_[a] = new_z_p0; + z_propagation_[b] = new_z_p1; + x_propagation_[a] = new_x_p0; + x_propagation_[b] = new_x_p1; + if (!z_sign) { + z_sign_ = !z_sign_; + } + if (!x_sign) { + x_sign_ = !x_sign_; + } +} + +void ACPairNode::update(const OpType& sq_cliff, const unsigned& a) { + auto [new_z_p, z_sign] = + SQ_CLIFF_MAP.at({sq_cliff, z_propagation_[a]}) auto [new_x_p, x_sign] = + SQ_CLIFF_MAP.at({sq_cliff, x_propagation_[a]}) z_propagation_[a] = + new_z_p x_propagation_[a] = new_x_p if (!z_sign) { + z_sign_ = !z_sign_; + } + if (!x_sign) { + x_sign_ = !x_sign_; + } +} + +void ACPairNode::swap(const unsigned& a, const unsigned& b) { + std::swap(z_propagation_[a], z_propagation_[b]); + std::swap(x_propagation_[a], x_propagation_[b]); + std::swap(commute_type_vec_[a], commute_type_vec_[b]); +} + +std::vector ACPairNode::reduction_tqes() const { + std::vector tqes; + // qubits with support + std::vector sqs; + for (unsigned i = 0; i < commute_type_vec_.size(); i++) { + if (commute_type_vec_[i] != CommuteType::I) sqs.push_back(i); + } + for (unsigned i = 0; i < sqs.size() - 1; i++) { + for (unsigned j = i + 1; j < sqs.size(); j++) { + std::vector tqe_types; + unsigned a = sqs[i]; + unsigned b = sqs[j]; + CommuteType ctype0 = commute_type_vec_[a]; + CommuteType ctype1 = commute_type_vec_[b]; + if (ctype0 == CommuteType::A) { + if (ctype1 == CommuteType::A) { + // TQEs that transform a AA pair to CC + tqe_types = AA_TO_CC_MAP.at( + {z_propagation_[a], z_propagation_[b], x_propagation_[a], + x_propagation_[b]}); + } else { + // TQEs that transform a AC pair to AI + tqe_types = AC_TO_AI_MAP.at( + {z_propagation_[a], z_propagation_[b], x_propagation_[a], + x_propagation_[b]}); + } + } else { + if (ctype1 == CommuteType::A) { + // TQEs that transform a CA pair to a IA + tqe_types = AC_TO_AI_MAP.at( + {z_propagation_[b], z_propagation_[a], x_propagation_[b], + x_propagation_[a]}); + // flip qubits + a = sqs[j]; + b = sqs[i]; + } else { + // TQEs that transform a CC pair to CI or IC, not always + // possible + tqe_types = CC_TO_IC_OR_CI_MAP.at( + {z_propagation_[a], z_propagation_[b], x_propagation_[a], + x_propagation_[b]}); + } + } + for (const TQEType& tt : tqe_types) { + tqes.push_back({tt, a, b}); + } + } + } + return tqes; +} + +std::tuple ACPairNode::first_support() const { + for (unsigned i = 0; i < commute_type_vec_.size(); i++) { + if (commute_type_vec_[i] != CommuteType::I) { + return {i, z_propagation_[i], x_propagation_[i]}; + } + } + // Should be impossible to reach here + TKET_ASSERT(false); +} + +// PauliRotation +PauliRotation::PauliRotation(std::vector string, Expr theta) + : SingleNode(string, true), theta_(theta) {} + +// PauliPropagation +PauliPropagation::PauliPropagation( + std::vector z_propagation, std::vector x_propagation, + bool z_sign, bool x_sign, unsigned qubit_index) + : ACPairNode(z_propagation, x_propagation, z_sign, x_sign), + qubit_index_(qubit_index) {} + +// return the sum of the cost increases on remaining tableau nodes +static double default_tableau_tqe_cost( + const std::vector& rows, + const std::vector& remaining_indices, const TQE& tqe) { + double cost = 0; + for (const unsigned& index : remaining_indices) { + cost += rows[index].tqe_cost_increase(tqe); + } + return cost; +} + +} // namespace Transforms + +} // namespace tket diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 6d0b6e30cd..f0958b491a 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -71,294 +71,6 @@ static void apply_tqe_to_circ(const TQE& tqe, Circuit& circ) { } } -static CommuteType get_pauli_pair_commute_type( - const Pauli& p0, const Pauli& p1) { - if (p0 == Pauli::I && p1 == Pauli::I) { - return CommuteType.I; - } - if (p0 == p1 || p0 == Pauli::I || p1 == Pauli::I) { - return CommuteType.C; - } - return CommuteType.A; -} - - - -/******************************************************************************* - * Nodes implementation - ******************************************************************************/ - -// PauliNode abstract class - -PauliNodeType PauliNode::get_type() const { return type_; } - -PauliNode::~PauliNode() {} - -PauliNode::PauliNode(PauliNodeType type) : type_(type) {} - -PauliNode::update(const OpType& /*sq_cliff*/, const unsigned& /*a*/) { - throw GreedyPauliSimpError("Single qubit Clifford update not implemented"); -} - -PauliNode::swap(const unsigned& /*a*/, const unsigned& /*b*/) { - throw GreedyPauliSimpError("SWAP update not implemented"); -} - -// SingleNode - -SingleNode::SingleNode(std::vector string, bool sign) - : string_(string), sign_(sign) { - weight_ = - string_.size() - std::count(string_.begin(), string_.end(), Pauli.I); -} - -unsigned PauliRotation::tqe_cost() const { return weight_ - 1; } - -int SingleNode::tqe_cost_increase(const TQE& tqe) const { - auto [g, a, b] = tqe; - Pauli p0 = string_[a]; - Pauli p1 = string_[b]; - auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); - return (p0 == Pauli::I) + (p1 == Pauli::I) - (new_p0 == Pauli.I) - - (new_p1 == Pauli::I); -} - -void SingleNode::update(const TQE& tqe) { - auto [g, a, b] = tqe; - unsigned p0 = string_[a]; - unsigned p1 = string_[b]; - auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); - string_[a] = new_p0; - string_[b] = new_p1; - weight_ += (p0 == Pauli::I) + (p1 == Pauli::I) - (new_p0 == Pauli.I) - - (new_p1 == Pauli::I); - if (!sign): - sign_ = ! sign_; -} - -std::vector SingleNode::reduction_tqes() const { - std::vector tqes; - // qubits with support - std::vector sqs; - for (unsigned i = 0; i < string_.size(); i++) { - if (string_[i] != Pauli.I) sqs.push_back(i); - } - for (unsigned i = 0; i < sqs.size() - 1; i++) { - for (unsigned j = i + 1; j < sqs.size(); j++) { - std::vector tqe_types = - TQE_REDUCTION_MAP.at({string_[sqs[i]], string_[sqs[j]]}); - for (const TQEType& tt : tqe_types) { - tqes.push_back({tt, sqs[i], sqs[j]}); - } - } - } - return tqes; -} - -std::pair SingleNode::first_support() const { - for (unsigned i = 0; i < string_.size(); i++) { - if (string_[i] != Pauli.I) { - return {i, string_[i]}; - } - } - // Should be impossible to reach here - TKET_ASSERT(false); -} - -// ACPairNode - -ACPairNode::ACPairNode( - std::vector z_propagation, std::vector x_propagation, - bool z_sign, bool x_sign) - : z_propagation_(z_propagation), - x_propagation_(x_propagation), - z_sign_(z_sign), - x_sign_(x_sign) { - n_commute_entries_ = 0; - n_anti_commute_entries_ = 0; - for (unsigned i = 0; i < z_propagation_.size(); i++) { - CommuteType commute_type = - get_pauli_pair_commute_type(z_propagation_[i], x_propagation_[i]); - commute_type_vec_.push_back(commute_type); - if (commute_type == CommuteType.C) { - n_commute_entries_ += 1; - } - if (commute_type == CommuteType.A) { - n_anti_commute_entries_ += 1; - } - } -} - -unsigned ACPairNode::tqe_cost() const { - return static_cast( - 1.5 * (n_anti_commute_entries_ - 1) + n_commute_entries_); -} - -int ACPairNode::tqe_cost_increase(const TQE& tqe) const { - auto [g, a, b] = tqe; - unsigned z_p0 = z_propagation_[a]; - unsigned z_p1 = z_propagation_[b]; - unsigned x_p0 = x_propagation_[a]; - unsigned x_p1 = x_propagation_[b]; - auto [new_z_p0, new_z_p1, z_sign] = TQE_PAULI_MAP.at({g, z_p0, z_p1}); - auto [new_x_p0, new_x_p1, x_sign] = TQE_PAULI_MAP.at({g, x_p0, x_p1}); - CommuteType new_a_type = get_pauli_pair_commute_type(new_z_p0, new_x_p0); - CommuteType new_b_type = get_pauli_pair_commute_type(new_z_p1, new_x_p1); - unsigned old_anti_commutes = (commute_type_vec_[a] == CommuteType::A) + - (commute_type_vec_[b] == CommuteType::A); - unsigned old_commutes = (commute_type_vec_[a] == CommuteType::C) + - (commute_type_vec_[b] == CommuteType::C); - unsigned new_anti_commutes = - (new_a_type == CommuteType::A) + (new_b_type == CommuteType::A); - unsigned new_commutes = - (new_a_type == CommuteType::C) + (new_b_type == CommuteType::C); - int anti_commute_increase = new_anti_commutes - old_anti_commutes; - int commute_increase = new_commutes - old_commutes; - return static_cast(1.5 * anti_commute_increase + commute_increase); -} - -void ACPairNode::update(const TQE& tqe) { - auto [g, a, b] = tqe; - unsigned z_p0 = z_propagation_[a]; - unsigned z_p1 = z_propagation_[b]; - unsigned x_p0 = x_propagation_[a]; - unsigned x_p1 = x_propagation_[b]; - auto [new_z_p0, new_z_p1, z_sign] = TQE_PAULI_MAP.at({g, z_p0, z_p1}); - auto [new_x_p0, new_x_p1, x_sign] = TQE_PAULI_MAP.at({g, x_p0, x_p1}); - CommuteType new_a_type = get_pauli_pair_commute_type(new_z_p0, new_x_p0); - CommuteType new_b_type = get_pauli_pair_commute_type(new_z_p1, new_x_p1); - unsigned old_anti_commutes = (commute_type_vec_[a] == CommuteType::A) + - (commute_type_vec_[b] == CommuteType::A); - unsigned old_commutes = (commute_type_vec_[a] == CommuteType::C) + - (commute_type_vec_[b] == CommuteType::C); - unsigned new_anti_commutes = - (new_a_type == CommuteType::A) + (new_b_type == CommuteType::A); - unsigned new_commutes = - (new_a_type == CommuteType::C) + (new_b_type == CommuteType::C); - int anti_commute_increase = new_anti_commutes - old_anti_commutes; - int commute_increase = new_commutes - old_commutes; - n_anti_commute_entries_ += anti_commute_increase; - n_commute_entries_ += commute_increase; - commute_type_vec_[a] = new_a_type; - commute_type_vec_[b] = new_b_type; - z_propagation_[a] = new_z_p0; - z_propagation_[b] = new_z_p1; - x_propagation_[a] = new_x_p0; - x_propagation_[b] = new_x_p1; - if (!z_sign) { - z_sign_ = !z_sign_; - } - if (!x_sign) { - x_sign_ = !x_sign_; - } -} - -void ACPairNode::update(const OpType& sq_cliff, const unsigned& a) { - auto [new_z_p, z_sign] = - SQ_CLIFF_MAP.at({sq_cliff, z_propagation_[a]}) auto [new_x_p, x_sign] = - SQ_CLIFF_MAP.at({sq_cliff, x_propagation_[a]}) z_propagation_[a] = - new_z_p x_propagation_[a] = new_x_p if (!z_sign) { - z_sign_ = !z_sign_; - } - if (!x_sign) { - x_sign_ = !x_sign_; - } -} - -void ACPairNode::swap(const unsigned& a, const unsigned& b) { - std::swap(z_propagation_[a], z_propagation_[b]); - std::swap(x_propagation_[a], x_propagation_[b]); - std::swap(commute_type_vec_[a], commute_type_vec_[b]); -} - -std::vector ACPairNode::reduction_tqes() const { - std::vector tqes; - // qubits with support - std::vector sqs; - for (unsigned i = 0; i < commute_type_vec_.size(); i++) { - if (commute_type_vec_[i] != CommuteType::I) sqs.push_back(i); - } - for (unsigned i = 0; i < sqs.size() - 1; i++) { - for (unsigned j = i + 1; j < sqs.size(); j++) { - std::vector tqe_types; - unsigned a = sqs[i]; - unsigned b = sqs[j]; - CommuteType ctype0 = commute_type_vec_[a]; - CommuteType ctype1 = commute_type_vec_[b]; - if (ctype0 == CommuteType::A) { - if (ctype1 == CommuteType::A) { - // TQEs that transform a AA pair to CC - tqe_types = AA_TO_CC_MAP.at( - {z_propagation_[a], z_propagation_[b], x_propagation_[a], - x_propagation_[b]}); - } else { - // TQEs that transform a AC pair to AI - tqe_types = AC_TO_AI_MAP.at( - {z_propagation_[a], z_propagation_[b], x_propagation_[a], - x_propagation_[b]}); - } - } else { - if (ctype1 == CommuteType::A) { - // TQEs that transform a CA pair to a IA - tqe_types = AC_TO_AI_MAP.at( - {z_propagation_[b], z_propagation_[a], x_propagation_[b], - x_propagation_[a]}); - // flip qubits - a = sqs[j]; - b = sqs[i]; - } else { - // TQEs that transform a CC pair to CI or IC, not always - // possible - tqe_types = CC_TO_IC_OR_CI_MAP.at( - {z_propagation_[a], z_propagation_[b], x_propagation_[a], - x_propagation_[b]}); - } - } - for (const TQEType& tt : tqe_types) { - tqes.push_back({tt, a, b}); - } - } - } - return tqes; -} - -std::tuple ACPairNode::first_support() const { - for (unsigned i = 0; i < commute_type_vec_.size(); i++) { - if (commute_type_vec_[i] != CommuteType::I) { - return {i, z_propagation_[i], x_propagation_[i]}; - } - } - // Should be impossible to reach here - TKET_ASSERT(false); -} - -// PauliRotation -PauliRotation::PauliRotation(std::vector string, Expr theta) - : SingleNode(string, true), theta_(theta) {} - -// PauliPropagation -PauliPropagation::PauliPropagation( - std::vector z_propagation, std::vector x_propagation, - bool z_sign, bool x_sign, unsigned qubit_index) - : ACPairNode(z_propagation, x_propagation, z_sign, x_sign), - qubit_index_(qubit_index) {} - -// return the sum of the cost increases on remaining tableau nodes -static double default_tableau_tqe_cost( - const std::vector& rows, - const std::vector& remaining_indices, const TQE& tqe) { - double cost = 0; - for (const unsigned& index : remaining_indices) { - cost += rows[index].tqe_cost_increase(tqe); - } - return cost; -} - - -/******************************************************************************* - * Synthesis - ******************************************************************************/ - // return the weighted sum of the cost increases on remaining nodes // we discount the weight after each set static double default_pauliexp_tqe_cost( From 0fa70b8e624b65f6cf11ed8ab7ddb0881df7c7cf Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Wed, 25 Sep 2024 12:11:15 +0100 Subject: [PATCH 04/57] Refactor synthesis --- .../GreedyPauliOptimisation.hpp | 13 +- tket/src/Transformations/GreedyPauliOps.cpp | 3 +- .../GreedyPauliOptimisation.cpp | 380 +++++++----------- 3 files changed, 148 insertions(+), 248 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 7534a4f67a..cfc91d72a1 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -73,10 +73,6 @@ enum class CommuteType : unsigned { */ using TQE = std::tuple; -// Forwards declarations -class PauliNode; -typedef std::shared_ptr PauliNode_ptr; - class PauliNode { public: PauliNodeType get_type() const const = 0; @@ -89,6 +85,9 @@ class PauliNode { virtual ~PauliNode(); } +typedef std::shared_ptr + PauliNode_ptr; + /** * @brief A node defined by a single Pauli string */ @@ -137,6 +136,8 @@ class SingleNode : public PauliNode { */ std::pair first_support() const; + bool sign() const { return sign_ }; + protected: std::vector string_; bool sign_; @@ -209,6 +210,10 @@ class ACPairNode : public PauliNode { */ std::tuple first_support() const; + bool z_sign() const { return z_sign_ }; + + bool x_sign() const { return x_sign_ }; + protected: std::vector z_propagation_; std::vector x_propagation_; diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index 124f1275c8..7b75b6f368 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "tket/Transformations/GreedyPauliOptimisation.hpp" - #include #include "tket/Circuit/PauliExpBoxes.hpp" @@ -21,6 +19,7 @@ #include "tket/OpType/OpType.hpp" #include "tket/PauliGraph/PauliGraph.hpp" #include "tket/Transformations/CliffordOptimisation.hpp" +#include "tket/Transformations/GreedyPauliOptimisation.hpp" #include "tket/Transformations/GreedyPauliOptimisationLookupTables.hpp" #include "tket/Transformations/Transform.hpp" diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index f0958b491a..c2d3b60ab0 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -75,20 +75,20 @@ static void apply_tqe_to_circ(const TQE& tqe, Circuit& circ) { // we discount the weight after each set static double default_pauliexp_tqe_cost( const double discount_rate, - const std::vector>& rotation_sets, - const std::vector& rows, const TQE& tqe) { + const std::vector>& rotation_sets, + const std::vector& rows, const TQE& tqe) { double discount = 1 / (1 + discount_rate); double weight = 1; double exp_cost = 0; double tab_cost = 0; - for (const std::vector& rotation_set : rotation_sets) { - for (const PauliExpNode& node : rotation_set) { - exp_cost += weight * node.tqe_cost_increase(tqe); + for (const std::vector& rotation_set : rotation_sets) { + for (const PauliNode_ptr& node : rotation_set) { + exp_cost += weight * node->tqe_cost_increase(tqe); } weight *= discount; } - for (const TableauRowNode& node : rows) { - tab_cost += weight * node.tqe_cost_increase(tqe); + for (const PauliNode_ptr& node : rows) { + tab_cost += weight * node->tqe_cost_increase(tqe); } return exp_cost + tab_cost; } @@ -209,57 +209,32 @@ struct DepthTracker { * permutation, and signs, transform it to exact identity and adding gates to * a circuit */ -static void tableau_cleanup( - std::vector& rows, UnitaryRevTableau& tab, Circuit& circ) { +static void tableau_cleanup(std::vector& rows, Circuit& circ) { // apply local Cliffords - for (const TableauRowNode& node : rows) { - unsigned q_index, supp; - std::tie(q_index, supp) = node.first_support(); - Qubit q(q_index); - std::vector local_cliffords = - FACTOR_STRONG_TO_LOCALS.at(supp); - for (const LocalCliffordType& lc : local_cliffords) { - switch (lc) { - case LocalCliffordType::H: - tab.apply_gate_at_end(OpType::H, {q}); - circ.add_op(OpType::H, {q}); - break; - case LocalCliffordType::S: - tab.apply_gate_at_end(OpType::S, {q}); - circ.add_op(OpType::S, {q}); - break; - case LocalCliffordType::V: - tab.apply_gate_at_end(OpType::V, {q}); - circ.add_op(OpType::V, {q}); - break; - } - } - } - // remove signs - for (const Qubit& q : circ.all_qubits()) { - if (cast_coeff(tab.get_xrow(q).coeff) != 1.) { - tab.apply_gate_at_end(OpType::Z, {q}); - circ.add_op(OpType::Z, {q}); - } - if (cast_coeff(tab.get_zrow(q).coeff) != 1.) { - tab.apply_gate_at_end(OpType::X, {q}); - circ.add_op(OpType::X, {q}); - } - } - // remove permutations - // 1. find perm unsigned n_qubits = circ.n_qubits(); - std::vector perm(n_qubits); - for (unsigned i = 0; i < n_qubits; i++) { - QubitPauliMap z_row_string = tab.get_zrow(Qubit(i)).string; - for (auto it = z_row_string.begin(); it != z_row_string.end(); it++) { - if (it->second == Pauli::Z) { - perm[it->first.index()[0]] = i; - break; - } - } + std::map perm; + for (PauliNode_ptr& node_ptr : rows) { + PauliPropagation& node = dynamic_cast(*node_ptr); + auto [q_index, supp_z, supp_x] = node.first_support(); + // transform supp_z,supp_x to Z,X + std::vector optype_list = AA_TO_ZX.at(supp_z, supp_x); + for (auto it = optype_list.rbegin(); it != optype_list.rend(); ++it) { + circ.add_op(*it, {q_index}); + node.update(*it, q_index); + } + // remove signs + if (!node.z_sign()) { + circ.add_op(OpType::X, {q_index}); + node.update(OpType::X, q_index); + } + if (!node.x_sign()) { + circ.add_op(OpType::Z, {q_index}); + node.update(OpType::Z, q_index); + } + perm[q_index] = node_ptr; } - // 2. traverse transpositions + // remove permutations + // traverse transpositions std::unordered_set done; for (unsigned k = 0; k < n_qubits; k++) { if (done.find(k) != done.end()) { @@ -267,41 +242,47 @@ static void tableau_cleanup( } unsigned head = k; unsigned current = k; - unsigned next = perm[k]; + PauliPropagation& curr_node = + dynamic_cast(*perm[current]); + unsigned next = curr_node.qubit_index(); + PauliPropagation& next_node = dynamic_cast(*perm[next]); while (true) { if (next == head) { done.insert(current); break; } // the SWAP gates will be later converted to wire swaps - tab.apply_gate_at_end(OpType::SWAP, {Qubit(current), Qubit(next)}); circ.add_op(OpType::SWAP, {current, next}); + curr_node.swap(current, next); + next_node.swap(current, next); done.insert(current); current = next; - next = perm[current]; + current_node = next_node; + next = current_node.qubit_index(); + next_node = dynamic_cast(*perm[next]); } } } /** - * @brief Synthesise a vector of TableauRowNode + * @brief Synthesise a vector of PauliPropagation */ static void tableau_row_nodes_synthesis( - std::vector& rows, UnitaryRevTableau& tab, Circuit& circ, - double depth_weight, DepthTracker& depth_tracker) { + std::vector& rows, Circuit& circ, double depth_weight, + DepthTracker& depth_tracker) { // only consider nodes with a non-zero cost std::vector remaining_indices; for (unsigned i = 0; i < rows.size(); i++) { - if (rows[i].tqe_cost() > 0) { + if (rows[i]->tqe_cost() > 0) { remaining_indices.push_back(i); } } while (remaining_indices.size() != 0) { // get nodes with min cost std::vector min_nodes_indices = {remaining_indices[0]}; - unsigned min_cost = rows[remaining_indices[0]].tqe_cost(); + unsigned min_cost = rows[remaining_indices[0]]->tqe_cost(); for (unsigned i = 1; i < remaining_indices.size(); i++) { - unsigned node_cost = rows[remaining_indices[i]].tqe_cost(); + unsigned node_cost = rows[remaining_indices[i]]->tqe_cost(); if (node_cost == min_cost) { min_nodes_indices.push_back(remaining_indices[i]); } else if (node_cost < min_cost) { @@ -314,7 +295,7 @@ static void tableau_row_nodes_synthesis( std::set tqe_candidates; TKET_ASSERT(min_nodes_indices.size() > 0); for (const unsigned& index : min_nodes_indices) { - std::vector node_reducing_tqes = rows[index].reduction_tqes(); + std::vector node_reducing_tqes = rows[index]->reduction_tqes(); tqe_candidates.insert( node_reducing_tqes.begin(), node_reducing_tqes.end()); } @@ -334,88 +315,70 @@ static void tableau_row_nodes_synthesis( TQE selected_tqe = select_tableau_tqe(tqe_candidates_cost, depth_weight); // apply TQE apply_tqe_to_circ(selected_tqe, circ); - apply_tqe_to_tableau(selected_tqe, tab); // update depth tracker depth_tracker.add_2q_gate( std::get<1>(selected_tqe), std::get<2>(selected_tqe)); // remove finished nodes for (unsigned i = remaining_indices.size(); i-- > 0;) { unsigned node_index = remaining_indices[i]; - rows[node_index].update(selected_tqe); - if (rows[node_index].tqe_cost() == 0) { + rows[node_index]->update(selected_tqe); + if (rows[node_index]->tqe_cost() == 0) { remaining_indices.erase(remaining_indices.begin() + i); } } } - tableau_cleanup(rows, tab, circ); + tableau_cleanup(rows, circ); } /** - * @brief Given a vector of sets of PauliExpNode, implement any node in the + * @brief Given a vector of sets of PauliRotation, implement any node in the * first set where the tqe_cost is zero. Remove implemented nodes and the first * set if empty. * * @param rotation_sets - * @param tab * @param circ * @return true if the first set is now empty and removed * @return false */ static bool consume_available_rotations( - std::vector>& rotation_sets, - UnitaryRevTableau& tab, Circuit& circ, DepthTracker& depth_tracker) { + std::vector>& rotation_sets, Circuit& circ, + DepthTracker& depth_tracker) { std::vector bin; if (rotation_sets.size() == 0) { return false; } - std::vector& first_set = rotation_sets[0]; + std::vector& first_set = rotation_sets[0]; for (unsigned i = 0; i < first_set.size(); i++) { - PauliExpNode& node = first_set[i]; + TKET_ASSERT(first_set[i]->get_type() == PauliNodeType::Rotation); + PauliRotation& node = dynamic_cast(*first_set[i]); if (node.tqe_cost() > 0) continue; - unsigned q_index, supp; - std::tie(q_index, supp) = node.first_support(); + auto [q_index, supp] = node.first_support(); Qubit q(q_index); depth_tracker.add_1q_gate(q_index); + OpType rot_type; switch (supp) { - case 3: { - // we apply S gate only to the frame, then check the sign, then Sdg - // if + apply f.Sdg; circ.Ry(-a) - // if - apply f.Sdg; circ.Ry(a) - tab.apply_gate_at_end(OpType::S, {q}); - Complex x_coeff = - cast_coeff(tab.get_xrow(q).coeff); - tab.apply_gate_at_end(OpType::Sdg, {q}); - if (x_coeff == 1.) { - circ.add_op(OpType::Ry, -node.theta(), {q}); - } else { - circ.add_op(OpType::Ry, node.theta(), {q}); - } + case Pauli::Y: { + rot_type = OpType::Ry; break; } - case 1: { - Complex z_coeff = - cast_coeff(tab.get_zrow(q).coeff); - if (z_coeff == 1.) { - circ.add_op(OpType::Rz, node.theta(), {q}); - } else { - circ.add_op(OpType::Rz, -node.theta(), {q}); - } + case Pauli::Z: { + rot_type = OpType::Rz; break; } - case 2: { - Complex x_coeff = - cast_coeff(tab.get_xrow(q).coeff); - if (x_coeff == 1.) { - circ.add_op(OpType::Rx, node.theta(), {q}); - } else { - circ.add_op(OpType::Rx, -node.theta(), {q}); - } + case Pauli::X: { + rot_type = OpType::Rx; break; } default: // support can't be Pauli::I TKET_ASSERT(false); } + if (node.sign()) { + circ.add_op(rot_type, node.theta(), {q}); + } else { + circ.add_op(rot_type, -node.theta(), {q}); + } + bin.push_back(i); } if (bin.size() == 0) return false; @@ -435,19 +398,19 @@ static bool consume_available_rotations( * @brief Synthesise a vector of unordered rotation sets */ static void pauli_exps_synthesis( - std::vector>& rotation_sets, - std::vector& rows, UnitaryRevTableau& tab, Circuit& circ, - double discount_rate, double depth_weight, DepthTracker& depth_tracker) { + std::vector>& rotation_sets, + std::vector& rows, Circuit& circ, double discount_rate, + double depth_weight, DepthTracker& depth_tracker) { while (true) { while (consume_available_rotations( - rotation_sets, tab, circ, depth_tracker)); // do nothing + rotation_sets, circ, depth_tracker)); // do nothing if (rotation_sets.size() == 0) break; - std::vector& first_set = rotation_sets[0]; + std::vector& first_set = rotation_sets[0]; // get nodes with min cost std::vector min_nodes_indices = {0}; - unsigned min_cost = first_set[0].tqe_cost(); + unsigned min_cost = first_set[0]->tqe_cost(); for (unsigned i = 1; i < first_set.size(); i++) { - unsigned node_cost = first_set[i].tqe_cost(); + unsigned node_cost = first_set[i]->tqe_cost(); if (node_cost == min_cost) { min_nodes_indices.push_back(i); } else if (node_cost < min_cost) { @@ -457,7 +420,7 @@ static void pauli_exps_synthesis( } std::set tqe_candidates; for (const unsigned& index : min_nodes_indices) { - std::vector node_reducing_tqes = first_set[index].reduction_tqes(); + std::vector node_reducing_tqes = first_set[index]->reduction_tqes(); tqe_candidates.insert( node_reducing_tqes.begin(), node_reducing_tqes.end()); } @@ -474,52 +437,54 @@ static void pauli_exps_synthesis( TQE selected_tqe = select_pauliexp_tqe(tqe_candidates_cost, depth_weight); // apply TQE apply_tqe_to_circ(selected_tqe, circ); - apply_tqe_to_tableau(selected_tqe, tab); depth_tracker.add_2q_gate( std::get<1>(selected_tqe), std::get<2>(selected_tqe)); - for (std::vector& rotation_set : rotation_sets) { - for (PauliExpNode& node : rotation_set) { - node.update(selected_tqe); + for (std::vector& rotation_set : rotation_sets) { + for (PauliNode_ptr& node : rotation_set) { + node->update(selected_tqe); } } - for (TableauRowNode& row : rows) { - row.update(selected_tqe); + for (PauliNode_ptr& row : rows) { + row->update(selected_tqe); } } } -// convert a Pauli exponential to a PauliExpNode -static PauliExpNode get_node_from_exp( +// convert a Pauli exponential to a PauliNode_ptr +static PauliNode_ptr get_node_from_exp( const std::vector& paulis, const Expr& theta, - const qubit_vector_t& args, unsigned n, const UnitaryTableau& forward_tab, - const UnitaryRevTableau& tab) { - std::map pauli_map; - for (unsigned i = 0; i < args.size(); i++) { - pauli_map.insert({args[i], paulis[i]}); - } - // this has the effect of bringing the final clifford - // forward past the Pauli exponential - SpPauliStabiliser pstab = - forward_tab.get_row_product(SpPauliStabiliser(pauli_map)); - Complex sign = cast_coeff(pstab.coeff); + const qubit_vector_t& args, unsigned n) { + // pad the Paulis + std::vector string(n, Pauli::I); + for (const Qubit& q : args) { + unsigned a = q.index()[0]; + string[a] = paulis[a]; + } + return std::make_shared(string, theta); +} - std::vector support_vec; - for (unsigned i = 0; i < n; i++) { - SpPauliStabiliser zrow = tab.get_zrow(Qubit(i)); - SpPauliStabiliser xrow = tab.get_xrow(Qubit(i)); - bool z_supp = !zrow.commutes_with(pstab); - bool x_supp = !xrow.commutes_with(pstab); - if (!z_supp && !x_supp) { - support_vec.push_back(0); - } else if (!z_supp && x_supp) { - support_vec.push_back(1); - } else if (z_supp && !x_supp) { - support_vec.push_back(2); - } else if (z_supp && x_supp) { - support_vec.push_back(3); +// convert a Clifford tableau to a vector of PauliNode_ptr +static std::vector get_nodes_from_tableau( + const UnitaryRevTableau& tab, unsigned n) { + std::vector rows; + for (unsigned i = 0; i < n_qubits; i++) { + Qubit q(i); + SpPauliStabiliser z_stab = tab.get_zrow(q); + SpPauliStabiliser x_stab = tab.get_xrow(q); + bool z_sign = + cast_coeff(z_stab.coeff) == 1. bool x_sign = + cast_coeff(x_stab.coeff) == + 1. TKET_ASSERT(z_stab.string.size() == n); + std::vector z_string; + std::vector x_string; + for (unsigned j = 0; j < n_qubits; j++) { + z_string.push_back(z_stab.string.at(Qubit(i))); + x_string.push_back(x_stab.string.at(Qubit(i))); } + rows.push_back(std::make_shared( + z_string, x_string, z_sign, x_sign, i)); } - return PauliExpNode(support_vec, sign.real() * theta); + return rows; } // detect trivial pauli exps, if true then return the global phase @@ -539,6 +504,7 @@ static std::pair is_trivial_pauliexp( } return {false, 0}; } + Circuit greedy_pauli_set_synthesis( const std::vector& unordered_set, double depth_weight) { if (unordered_set.size() == 0) { @@ -547,59 +513,20 @@ Circuit greedy_pauli_set_synthesis( unsigned n_qubits = unordered_set[0].string.size(); Circuit c(n_qubits); - std::vector> rotation_sets{{}}; - std::vector rows; + std::vector> rotation_sets{{}}; + for (auto& pauli : unordered_set) { - std::vector support_vec; TKET_ASSERT(pauli.string.size() == n_qubits); - for (unsigned i = 0; i < n_qubits; i++) { - if (pauli.string[i] == Pauli::I) { - support_vec.push_back(0); - } else if (pauli.string[i] == Pauli::Z) { - support_vec.push_back(1); - } else if (pauli.string[i] == Pauli::X) { - support_vec.push_back(2); - } else { - support_vec.push_back(3); - } - } - rotation_sets[0].push_back(PauliExpNode(support_vec, pauli.coeff)); + rotation_sets[0].push_back( + std::make_shared(pauli.string, pauli.coeff)); } UnitaryRevTableau tab(n_qubits); - // add identity TableauRowNodes - for (unsigned i = 0; i < n_qubits; i++) { - std::vector support_vec; - // identity rows - std::map p; - std::map q; - for (unsigned j = 0; j < n_qubits; j++) { - if (j == i) { - p.insert({Qubit(j), Pauli::Z}); - q.insert({Qubit(j), Pauli::X}); - } else { - p.insert({Qubit(j), Pauli::I}); - q.insert({Qubit(j), Pauli::I}); - } - } - SpPauliStabiliser stab_p(p); - SpPauliStabiliser stab_q(q); - for (unsigned row_index = 0; row_index < n_qubits; row_index++) { - SpPauliStabiliser zrow = tab.get_zrow(Qubit(row_index)); - SpPauliStabiliser xrow = tab.get_xrow(Qubit(row_index)); - bool lpx = !xrow.commutes_with(stab_p); - bool lpz = !zrow.commutes_with(stab_p); - bool lqx = !xrow.commutes_with(stab_q); - bool lqz = !zrow.commutes_with(stab_q); - support_vec.push_back(8 * lpx + 4 * lpz + 2 * lqx + lqz); - } - rows.push_back(TableauRowNode(support_vec)); - } + std::vector rows = get_nodes_from_tableau(tab, n_qubits); DepthTracker depth_tracker(n_qubits); // synthesise Pauli exps - pauli_exps_synthesis( - rotation_sets, rows, tab, c, 0, depth_weight, depth_tracker); + pauli_exps_synthesis(rotation_sets, rows, c, 0, depth_weight, depth_tracker); // synthesise the tableau - tableau_row_nodes_synthesis(rows, tab, c, depth_weight, depth_tracker); + tableau_row_nodes_synthesis(rows, c, depth_weight, depth_tracker); c.replace_SWAPs(); return c; } @@ -639,14 +566,10 @@ Circuit greedy_pauli_graph_synthesis( } } } - std::vector> rotation_sets; - std::vector rows; - // use forward Tableau to update the paulis by commuting the tableau to the - // front - UnitaryTableau forward_tab = circuit_to_unitary_tableau(cliff); - // Tableau used for tracking Cliffords throughout the synthesis - // TODO: this can be potentially made redundant - UnitaryRevTableau tab = circuit_to_unitary_rev_tableau(cliff).dagger(); + std::vector> rotation_sets; + UnitaryRevTableau tab = circuit_to_unitary_rev_tableau(cliff); + // convert the tableau into a set of nodes + std::vector rows = get_nodes_from_tableau(tab, n_qubits); unsigned n_qubits = c.n_qubits(); // extract the Pauli exps for (const Command& cmd : commands) { @@ -661,8 +584,8 @@ Circuit greedy_pauli_graph_synthesis( if (trivial) { c.add_phase(global_phase); } else { - rotation_sets.push_back({get_node_from_exp( - paulis, phase, cmd.get_qubits(), n_qubits, forward_tab, tab)}); + rotation_sets.push_back( + {get_node_from_exp(paulis, phase, cmd.get_qubits(), n_qubits)}); } break; } @@ -673,18 +596,18 @@ Circuit greedy_pauli_graph_synthesis( const auto [phase1, phase2] = pbox.get_phase_pair(); auto [trivial1, global_phase1] = is_trivial_pauliexp(paulis1, phase1); auto [trivial2, global_phase2] = is_trivial_pauliexp(paulis2, phase2); - std::vector rotation_set; + std::vector rotation_set; if (trivial1) { c.add_phase(global_phase1); } else { - rotation_set.push_back(get_node_from_exp( - paulis1, phase1, cmd.get_qubits(), n_qubits, forward_tab, tab)); + rotation_set.push_back( + get_node_from_exp(paulis1, phase1, cmd.get_qubits(), n_qubits)); } if (trivial2) { c.add_phase(global_phase2); } else { - rotation_set.push_back(get_node_from_exp( - paulis2, phase2, cmd.get_qubits(), n_qubits, forward_tab, tab)); + rotation_set.push_back( + get_node_from_exp(paulis2, phase2, cmd.get_qubits(), n_qubits)); } if (!rotation_set.empty()) { rotation_sets.push_back(rotation_set); @@ -695,7 +618,7 @@ Circuit greedy_pauli_graph_synthesis( const PauliExpCommutingSetBox& pbox = static_cast(*cmd.get_op_ptr()); const std::vector gadgets = pbox.get_pauli_gadgets(); - std::vector rotation_set; + std::vector rotation_set; for (const SymPauliTensor& pt : gadgets) { const std::vector paulis = pt.string; const Expr phase = pt.coeff; @@ -703,8 +626,8 @@ Circuit greedy_pauli_graph_synthesis( if (trivial) { c.add_phase(global_phase); } else { - rotation_set.push_back(get_node_from_exp( - paulis, phase, cmd.get_qubits(), n_qubits, forward_tab, tab)); + rotation_set.push_back( + get_node_from_exp(paulis, phase, cmd.get_qubits(), n_qubits)); } } if (rotation_set.size() > 0) { @@ -716,40 +639,13 @@ Circuit greedy_pauli_graph_synthesis( break; } } - // add identity TableauRowNodes - for (unsigned i = 0; i < n_qubits; i++) { - std::vector support_vec; - // identity rows - std::map p; - std::map q; - for (unsigned j = 0; j < n_qubits; j++) { - if (j == i) { - p.insert({Qubit(j), Pauli::Z}); - q.insert({Qubit(j), Pauli::X}); - } else { - p.insert({Qubit(j), Pauli::I}); - q.insert({Qubit(j), Pauli::I}); - } - } - SpPauliStabiliser stab_p(p); - SpPauliStabiliser stab_q(q); - for (unsigned row_index = 0; row_index < n_qubits; row_index++) { - SpPauliStabiliser zrow = tab.get_zrow(Qubit(row_index)); - SpPauliStabiliser xrow = tab.get_xrow(Qubit(row_index)); - bool lpx = !xrow.commutes_with(stab_p); - bool lpz = !zrow.commutes_with(stab_p); - bool lqx = !xrow.commutes_with(stab_q); - bool lqz = !zrow.commutes_with(stab_q); - support_vec.push_back(8 * lpx + 4 * lpz + 2 * lqx + lqz); - } - rows.push_back(TableauRowNode(support_vec)); - } + DepthTracker depth_tracker(n_qubits); // synthesise Pauli exps pauli_exps_synthesis( - rotation_sets, rows, tab, c, discount_rate, depth_weight, depth_tracker); + rotation_sets, rows, c, discount_rate, depth_weight, depth_tracker); // synthesise the tableau - tableau_row_nodes_synthesis(rows, tab, c, depth_weight, depth_tracker); + tableau_row_nodes_synthesis(rows, c, depth_weight, depth_tracker); unit_map_t rev_unit_map; for (const auto& pair : unit_map) { rev_unit_map.insert({pair.second, pair.first}); From 7f690ea5cccfee4ce0bbe54a3ebe9d05bc0a4fff Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Wed, 25 Sep 2024 13:43:16 +0100 Subject: [PATCH 05/57] Cleanup --- .../GreedyPauliOptimisation.hpp | 15 ++-- .../GreedyPauliOptimisationLookupTables.hpp | 19 +---- tket/src/Transformations/GreedyPauliOps.cpp | 75 ++++++++----------- .../GreedyPauliOptimisation.cpp | 28 ++++--- 4 files changed, 62 insertions(+), 75 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index cfc91d72a1..76aeec0c85 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -51,7 +51,7 @@ enum class PauliNodeType { // Defines how a Pauli X and a Pauli Z on the same qubit // get propagated from right to left through a Clifford operator. Propagation, -} +}; /** * @brief The type of a pair of Pauli letters defined by @@ -75,7 +75,7 @@ using TQE = std::tuple; class PauliNode { public: - PauliNodeType get_type() const const = 0; + virtual PauliNodeType get_type() const = 0; virtual unsigned tqe_cost() const = 0; virtual int tqe_cost_increase(const TQE& tqe) const = 0; virtual void update(const TQE& tqe) = 0; @@ -83,10 +83,9 @@ class PauliNode { virtual void swap(const unsigned& a, const unsigned& b); virtual std::vector reduction_tqes() const = 0; virtual ~PauliNode(); -} +}; -typedef std::shared_ptr - PauliNode_ptr; +typedef std::shared_ptr PauliNode_ptr; /** * @brief A node defined by a single Pauli string @@ -136,7 +135,7 @@ class SingleNode : public PauliNode { */ std::pair first_support() const; - bool sign() const { return sign_ }; + bool sign() const { return sign_; }; protected: std::vector string_; @@ -210,9 +209,9 @@ class ACPairNode : public PauliNode { */ std::tuple first_support() const; - bool z_sign() const { return z_sign_ }; + bool z_sign() const { return z_sign_; }; - bool x_sign() const { return x_sign_ }; + bool x_sign() const { return x_sign_; }; protected: std::vector z_propagation_; diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp index 78931cf5b9..a5d22a22b0 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp @@ -29,7 +29,7 @@ struct hash_pauli_pauli { }; struct hash_optype_pauli { size_t operator()(const std::pair& pair) const { - return pair.first * 10 + pair.second; + return static_cast(pair.first) * 10 + pair.second; } }; struct hash_triple { @@ -46,24 +46,12 @@ struct hash_quadruple { } }; -/** - * @brief Transform a pair of anti-commuting pauli letters at the - * right-hand-side to Z/X For example, Sdg; H; X/Y = Z/X; Sdg; H - * - */ -const static std::unordered_map AA_TO_ZX = { - {OpType::H, OpType::H}, - {OpType::S, OpType::Sdg}, - {OpType::Sdg, OpType::S}, - {OpType::V, OpType::Vdg}, - {OpType::Vdg, OpType::V}}; - /** * @brief Transform a pair of anti-commuting pauli letters at the * right-hand-side to Z/X For example, Sdg; H; X/Y = Z/X; Sdg; H */ const static std::unordered_map< - const std::pair & pair, std::vector, hash_pauli_pauli> + const std::pair, std::vector, hash_pauli_pauli> AA_TO_ZX = { {{Pauli::X, Pauli::Y}, {OpType::Sdg, OpType::H}}, {{Pauli::X, Pauli::Z}, {OpType::H}}, @@ -110,7 +98,8 @@ const static std::unordered_map< * TQE;P(0);Q(1) = k* P'(0);Q'(1);TQE */ const static std::unordered_map< - std::tuple, std::pair, hash_triple> + std::tuple, std::tuple, + hash_triple> TQE_PAULI_MAP = { {{TQEType::XX, Pauli::X, Pauli::X}, {Pauli::X, Pauli::X, true}}, {{TQEType::XY, Pauli::X, Pauli::X}, {Pauli::I, Pauli::X, true}}, diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index 7b75b6f368..d28ef8080d 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -32,27 +32,23 @@ namespace GreedyPauliSimp { static CommuteType get_pauli_pair_commute_type( const Pauli& p0, const Pauli& p1) { if (p0 == Pauli::I && p1 == Pauli::I) { - return CommuteType.I; + return CommuteType::I; } if (p0 == p1 || p0 == Pauli::I || p1 == Pauli::I) { - return CommuteType.C; + return CommuteType::C; } - return CommuteType.A; + return CommuteType::A; } // PauliNode abstract class -PauliNodeType PauliNode::get_type() const { return type_; } - PauliNode::~PauliNode() {} -PauliNode::PauliNode(PauliNodeType type) : type_(type) {} - -PauliNode::update(const OpType& /*sq_cliff*/, const unsigned& /*a*/) { +void PauliNode::update(const OpType& /*sq_cliff*/, const unsigned& /*a*/) { throw GreedyPauliSimpError("Single qubit Clifford update not implemented"); } -PauliNode::swap(const unsigned& /*a*/, const unsigned& /*b*/) { +void PauliNode::swap(const unsigned& /*a*/, const unsigned& /*b*/) { throw GreedyPauliSimpError("SWAP update not implemented"); } @@ -61,31 +57,32 @@ PauliNode::swap(const unsigned& /*a*/, const unsigned& /*b*/) { SingleNode::SingleNode(std::vector string, bool sign) : string_(string), sign_(sign) { weight_ = - string_.size() - std::count(string_.begin(), string_.end(), Pauli.I); + string_.size() - std::count(string_.begin(), string_.end(), Pauli::I); } -unsigned PauliRotation::tqe_cost() const { return weight_ - 1; } +unsigned SingleNode::tqe_cost() const { return weight_ - 1; } int SingleNode::tqe_cost_increase(const TQE& tqe) const { auto [g, a, b] = tqe; Pauli p0 = string_[a]; Pauli p1 = string_[b]; auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); - return (p0 == Pauli::I) + (p1 == Pauli::I) - (new_p0 == Pauli.I) - + return (p0 == Pauli::I) + (p1 == Pauli::I) - (new_p0 == Pauli::I) - (new_p1 == Pauli::I); } void SingleNode::update(const TQE& tqe) { auto [g, a, b] = tqe; - unsigned p0 = string_[a]; - unsigned p1 = string_[b]; + Pauli p0 = string_[a]; + Pauli p1 = string_[b]; auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); string_[a] = new_p0; string_[b] = new_p1; - weight_ += (p0 == Pauli::I) + (p1 == Pauli::I) - (new_p0 == Pauli.I) - + weight_ += (p0 == Pauli::I) + (p1 == Pauli::I) - (new_p0 == Pauli::I) - (new_p1 == Pauli::I); - if (!sign): - sign_ = ! sign_; + if (!sign) { + sign_ = !sign_; + } } std::vector SingleNode::reduction_tqes() const { @@ -93,7 +90,7 @@ std::vector SingleNode::reduction_tqes() const { // qubits with support std::vector sqs; for (unsigned i = 0; i < string_.size(); i++) { - if (string_[i] != Pauli.I) sqs.push_back(i); + if (string_[i] != Pauli::I) sqs.push_back(i); } for (unsigned i = 0; i < sqs.size() - 1; i++) { for (unsigned j = i + 1; j < sqs.size(); j++) { @@ -109,7 +106,7 @@ std::vector SingleNode::reduction_tqes() const { std::pair SingleNode::first_support() const { for (unsigned i = 0; i < string_.size(); i++) { - if (string_[i] != Pauli.I) { + if (string_[i] != Pauli::I) { return {i, string_[i]}; } } @@ -132,10 +129,10 @@ ACPairNode::ACPairNode( CommuteType commute_type = get_pauli_pair_commute_type(z_propagation_[i], x_propagation_[i]); commute_type_vec_.push_back(commute_type); - if (commute_type == CommuteType.C) { + if (commute_type == CommuteType::C) { n_commute_entries_ += 1; } - if (commute_type == CommuteType.A) { + if (commute_type == CommuteType::A) { n_anti_commute_entries_ += 1; } } @@ -148,10 +145,10 @@ unsigned ACPairNode::tqe_cost() const { int ACPairNode::tqe_cost_increase(const TQE& tqe) const { auto [g, a, b] = tqe; - unsigned z_p0 = z_propagation_[a]; - unsigned z_p1 = z_propagation_[b]; - unsigned x_p0 = x_propagation_[a]; - unsigned x_p1 = x_propagation_[b]; + Pauli z_p0 = z_propagation_[a]; + Pauli z_p1 = z_propagation_[b]; + Pauli x_p0 = x_propagation_[a]; + Pauli x_p1 = x_propagation_[b]; auto [new_z_p0, new_z_p1, z_sign] = TQE_PAULI_MAP.at({g, z_p0, z_p1}); auto [new_x_p0, new_x_p1, x_sign] = TQE_PAULI_MAP.at({g, x_p0, x_p1}); CommuteType new_a_type = get_pauli_pair_commute_type(new_z_p0, new_x_p0); @@ -171,10 +168,10 @@ int ACPairNode::tqe_cost_increase(const TQE& tqe) const { void ACPairNode::update(const TQE& tqe) { auto [g, a, b] = tqe; - unsigned z_p0 = z_propagation_[a]; - unsigned z_p1 = z_propagation_[b]; - unsigned x_p0 = x_propagation_[a]; - unsigned x_p1 = x_propagation_[b]; + Pauli z_p0 = z_propagation_[a]; + Pauli z_p1 = z_propagation_[b]; + Pauli x_p0 = x_propagation_[a]; + Pauli x_p1 = x_propagation_[b]; auto [new_z_p0, new_z_p1, z_sign] = TQE_PAULI_MAP.at({g, z_p0, z_p1}); auto [new_x_p0, new_x_p1, x_sign] = TQE_PAULI_MAP.at({g, x_p0, x_p1}); CommuteType new_a_type = get_pauli_pair_commute_type(new_z_p0, new_x_p0); @@ -206,10 +203,11 @@ void ACPairNode::update(const TQE& tqe) { } void ACPairNode::update(const OpType& sq_cliff, const unsigned& a) { - auto [new_z_p, z_sign] = - SQ_CLIFF_MAP.at({sq_cliff, z_propagation_[a]}) auto [new_x_p, x_sign] = - SQ_CLIFF_MAP.at({sq_cliff, x_propagation_[a]}) z_propagation_[a] = - new_z_p x_propagation_[a] = new_x_p if (!z_sign) { + auto [new_z_p, z_sign] = SQ_CLIFF_MAP.at({sq_cliff, z_propagation_[a]}); + auto [new_x_p, x_sign] = SQ_CLIFF_MAP.at({sq_cliff, x_propagation_[a]}); + z_propagation_[a] = new_z_p; + x_propagation_[a] = new_x_p; + if (!z_sign) { z_sign_ = !z_sign_; } if (!x_sign) { @@ -295,16 +293,7 @@ PauliPropagation::PauliPropagation( : ACPairNode(z_propagation, x_propagation, z_sign, x_sign), qubit_index_(qubit_index) {} -// return the sum of the cost increases on remaining tableau nodes -static double default_tableau_tqe_cost( - const std::vector& rows, - const std::vector& remaining_indices, const TQE& tqe) { - double cost = 0; - for (const unsigned& index : remaining_indices) { - cost += rows[index].tqe_cost_increase(tqe); - } - return cost; -} +} // namespace GreedyPauliSimp } // namespace Transforms diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index c2d3b60ab0..33a4cb4118 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -71,6 +71,17 @@ static void apply_tqe_to_circ(const TQE& tqe, Circuit& circ) { } } +// return the sum of the cost increases on remaining tableau nodes +static double default_tableau_tqe_cost( + const std::vector& rows, + const std::vector& remaining_indices, const TQE& tqe) { + double cost = 0; + for (const unsigned& index : remaining_indices) { + cost += rows[index]->tqe_cost_increase(tqe); + } + return cost; +} + // return the weighted sum of the cost increases on remaining nodes // we discount the weight after each set static double default_pauliexp_tqe_cost( @@ -217,7 +228,7 @@ static void tableau_cleanup(std::vector& rows, Circuit& circ) { PauliPropagation& node = dynamic_cast(*node_ptr); auto [q_index, supp_z, supp_x] = node.first_support(); // transform supp_z,supp_x to Z,X - std::vector optype_list = AA_TO_ZX.at(supp_z, supp_x); + std::vector optype_list = AA_TO_ZX.at({supp_z, supp_x}); for (auto it = optype_list.rbegin(); it != optype_list.rend(); ++it) { circ.add_op(*it, {q_index}); node.update(*it, q_index); @@ -257,8 +268,8 @@ static void tableau_cleanup(std::vector& rows, Circuit& circ) { next_node.swap(current, next); done.insert(current); current = next; - current_node = next_node; - next = current_node.qubit_index(); + curr_node = next_node; + next = curr_node.qubit_index(); next_node = dynamic_cast(*perm[next]); } } @@ -465,16 +476,15 @@ static PauliNode_ptr get_node_from_exp( // convert a Clifford tableau to a vector of PauliNode_ptr static std::vector get_nodes_from_tableau( - const UnitaryRevTableau& tab, unsigned n) { + const UnitaryRevTableau& tab, unsigned n_qubits) { std::vector rows; for (unsigned i = 0; i < n_qubits; i++) { Qubit q(i); SpPauliStabiliser z_stab = tab.get_zrow(q); SpPauliStabiliser x_stab = tab.get_xrow(q); - bool z_sign = - cast_coeff(z_stab.coeff) == 1. bool x_sign = - cast_coeff(x_stab.coeff) == - 1. TKET_ASSERT(z_stab.string.size() == n); + bool z_sign = cast_coeff(z_stab.coeff) == 1.; + bool x_sign = cast_coeff(x_stab.coeff) == 1.; + TKET_ASSERT(z_stab.string.size() == n_qubits); std::vector z_string; std::vector x_string; for (unsigned j = 0; j < n_qubits; j++) { @@ -567,10 +577,10 @@ Circuit greedy_pauli_graph_synthesis( } } std::vector> rotation_sets; + unsigned n_qubits = c.n_qubits(); UnitaryRevTableau tab = circuit_to_unitary_rev_tableau(cliff); // convert the tableau into a set of nodes std::vector rows = get_nodes_from_tableau(tab, n_qubits); - unsigned n_qubits = c.n_qubits(); // extract the Pauli exps for (const Command& cmd : commands) { OpType optype = cmd.get_op_ptr()->get_type(); From 0fcca68b8710abd728eca632868ac5c35d1a8cdf Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Wed, 25 Sep 2024 16:43:01 +0100 Subject: [PATCH 06/57] More refactoring --- .../GreedyPauliOptimisation.hpp | 4 + .../GreedyPauliOptimisationLookupTables.hpp | 7 + tket/src/Transformations/GreedyPauliOps.cpp | 16 +- .../GreedyPauliOptimisation.cpp | 184 +++++++----------- 4 files changed, 96 insertions(+), 115 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 76aeec0c85..607b6fb15c 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -213,6 +213,10 @@ class ACPairNode : public PauliNode { bool x_sign() const { return x_sign_; }; + std::vector z_propagation() const { return z_propagation_; }; + + std::vector x_propagation() const { return x_propagation_; }; + protected: std::vector z_propagation_; std::vector x_propagation_; diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp index a5d22a22b0..af8c144426 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp @@ -60,6 +60,13 @@ const static std::unordered_map< {{Pauli::Z, Pauli::X}, {}}, {{Pauli::Z, Pauli::Y}, {OpType::S}}}; +const static std::unordered_map SQ_CLIFF_DAGGER = { + {OpType::H, OpType::H}, + {OpType::S, OpType::Sdg}, + {OpType::Sdg, OpType::S}, + {OpType::V, OpType::Vdg}, + {OpType::Vdg, OpType::V}}; + /** * @brief Given a SQ Clifford gate g and a Pauli operator P, return Pauli * P', and sign k such that g;P; = k* P';g diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index d28ef8080d..89cb000163 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -45,19 +45,26 @@ static CommuteType get_pauli_pair_commute_type( PauliNode::~PauliNode() {} void PauliNode::update(const OpType& /*sq_cliff*/, const unsigned& /*a*/) { - throw GreedyPauliSimpError("Single qubit Clifford update not implemented"); + throw GreedyPauliSimpError("Single qubit Clifford update not implemented."); } void PauliNode::swap(const unsigned& /*a*/, const unsigned& /*b*/) { - throw GreedyPauliSimpError("SWAP update not implemented"); + throw GreedyPauliSimpError("SWAP update not implemented."); } // SingleNode SingleNode::SingleNode(std::vector string, bool sign) : string_(string), sign_(sign) { + if (string.empty()) { + throw GreedyPauliSimpError("SingleNode cannot have empty strings."); + } weight_ = string_.size() - std::count(string_.begin(), string_.end(), Pauli::I); + if (weight_ == 0) { + throw GreedyPauliSimpError( + "SingleNode cannot be constructed with identity strings."); + } } unsigned SingleNode::tqe_cost() const { return weight_ - 1; } @@ -92,6 +99,7 @@ std::vector SingleNode::reduction_tqes() const { for (unsigned i = 0; i < string_.size(); i++) { if (string_[i] != Pauli::I) sqs.push_back(i); } + TKET_ASSERT(!sqs.empty()); for (unsigned i = 0; i < sqs.size() - 1; i++) { for (unsigned j = i + 1; j < sqs.size(); j++) { std::vector tqe_types = @@ -123,6 +131,9 @@ ACPairNode::ACPairNode( x_propagation_(x_propagation), z_sign_(z_sign), x_sign_(x_sign) { + if (z_propagation.empty() || x_propagation.empty()) { + throw GreedyPauliSimpError("ACPairNode cannot have empty strings."); + } n_commute_entries_ = 0; n_anti_commute_entries_ = 0; for (unsigned i = 0; i < z_propagation_.size(); i++) { @@ -228,6 +239,7 @@ std::vector ACPairNode::reduction_tqes() const { for (unsigned i = 0; i < commute_type_vec_.size(); i++) { if (commute_type_vec_[i] != CommuteType::I) sqs.push_back(i); } + TKET_ASSERT(!sqs.empty()); for (unsigned i = 0; i < sqs.size() - 1; i++) { for (unsigned j = i + 1; j < sqs.size(); j++) { std::vector tqe_types; diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 33a4cb4118..cad435a5bb 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -215,66 +215,6 @@ struct DepthTracker { }; }; -/** - * @brief Given a tableau that is identity up to local Cliffords, qubit - * permutation, and signs, transform it to exact identity and adding gates to - * a circuit - */ -static void tableau_cleanup(std::vector& rows, Circuit& circ) { - // apply local Cliffords - unsigned n_qubits = circ.n_qubits(); - std::map perm; - for (PauliNode_ptr& node_ptr : rows) { - PauliPropagation& node = dynamic_cast(*node_ptr); - auto [q_index, supp_z, supp_x] = node.first_support(); - // transform supp_z,supp_x to Z,X - std::vector optype_list = AA_TO_ZX.at({supp_z, supp_x}); - for (auto it = optype_list.rbegin(); it != optype_list.rend(); ++it) { - circ.add_op(*it, {q_index}); - node.update(*it, q_index); - } - // remove signs - if (!node.z_sign()) { - circ.add_op(OpType::X, {q_index}); - node.update(OpType::X, q_index); - } - if (!node.x_sign()) { - circ.add_op(OpType::Z, {q_index}); - node.update(OpType::Z, q_index); - } - perm[q_index] = node_ptr; - } - // remove permutations - // traverse transpositions - std::unordered_set done; - for (unsigned k = 0; k < n_qubits; k++) { - if (done.find(k) != done.end()) { - continue; - } - unsigned head = k; - unsigned current = k; - PauliPropagation& curr_node = - dynamic_cast(*perm[current]); - unsigned next = curr_node.qubit_index(); - PauliPropagation& next_node = dynamic_cast(*perm[next]); - while (true) { - if (next == head) { - done.insert(current); - break; - } - // the SWAP gates will be later converted to wire swaps - circ.add_op(OpType::SWAP, {current, next}); - curr_node.swap(current, next); - next_node.swap(current, next); - done.insert(current); - current = next; - curr_node = next_node; - next = curr_node.qubit_index(); - next_node = dynamic_cast(*perm[next]); - } - } -} - /** * @brief Synthesise a vector of PauliPropagation */ @@ -338,7 +278,32 @@ static void tableau_row_nodes_synthesis( } } } - tableau_cleanup(rows, circ); + // apply local Cliffords + for (PauliNode_ptr& node_ptr : rows) { + PauliPropagation& node = dynamic_cast(*node_ptr); + auto [q_index, supp_z, supp_x] = node.first_support(); + // transform supp_z,supp_x to Z,X + std::vector optype_list = AA_TO_ZX.at({supp_z, supp_x}); + for (auto it = optype_list.rbegin(); it != optype_list.rend(); ++it) { + circ.add_op(SQ_CLIFF_DAGGER.at(*it), {q_index}); + node.update(*it, q_index); + } + // remove signs + if (!node.z_sign()) { + circ.add_op(OpType::X, {q_index}); + node.update(OpType::X, q_index); + } + if (!node.x_sign()) { + circ.add_op(OpType::Z, {q_index}); + node.update(OpType::Z, q_index); + } + if (q_index != node.qubit_index()) { + circ.add_op(OpType::SWAP, {q_index, node.qubit_index()}); + for (PauliNode_ptr& node_ptr2 : rows) { + node_ptr2->swap(q_index, node.qubit_index()); + } + } + } } /** @@ -351,58 +316,54 @@ static void tableau_row_nodes_synthesis( * @return true if the first set is now empty and removed * @return false */ -static bool consume_available_rotations( +static void consume_available_rotations( std::vector>& rotation_sets, Circuit& circ, DepthTracker& depth_tracker) { - std::vector bin; - if (rotation_sets.size() == 0) { - return false; + if (rotation_sets.empty()) { + return; } - std::vector& first_set = rotation_sets[0]; - for (unsigned i = 0; i < first_set.size(); i++) { - TKET_ASSERT(first_set[i]->get_type() == PauliNodeType::Rotation); - PauliRotation& node = dynamic_cast(*first_set[i]); - if (node.tqe_cost() > 0) continue; - auto [q_index, supp] = node.first_support(); - Qubit q(q_index); - depth_tracker.add_1q_gate(q_index); - OpType rot_type; - switch (supp) { - case Pauli::Y: { - rot_type = OpType::Ry; - break; - } - case Pauli::Z: { - rot_type = OpType::Rz; - break; + while (true) { + std::vector& first_set = rotation_sets[0]; + for (unsigned i = first_set.size(); i-- > 0;) { + TKET_ASSERT(first_set[i]->get_type() == PauliNodeType::Rotation); + PauliRotation& node = dynamic_cast(*first_set[i]); + if (node.tqe_cost() > 0) continue; + auto [q_index, supp] = node.first_support(); + depth_tracker.add_1q_gate(q_index); + OpType rot_type; + switch (supp) { + case Pauli::Y: { + rot_type = OpType::Ry; + break; + } + case Pauli::Z: { + rot_type = OpType::Rz; + break; + } + case Pauli::X: { + rot_type = OpType::Rx; + break; + } + default: + // support can't be Pauli::I + TKET_ASSERT(false); } - case Pauli::X: { - rot_type = OpType::Rx; - break; + if (node.sign()) { + circ.add_op(rot_type, node.theta(), {q_index}); + } else { + circ.add_op(rot_type, -node.theta(), {q_index}); } - default: - // support can't be Pauli::I - TKET_ASSERT(false); + first_set.erase(first_set.begin() + i); } - if (node.sign()) { - circ.add_op(rot_type, node.theta(), {q}); + if (first_set.empty()) { + rotation_sets.erase(rotation_sets.begin()); + if (rotation_sets.empty()) { + return; + } } else { - circ.add_op(rot_type, -node.theta(), {q}); + return; } - - bin.push_back(i); } - if (bin.size() == 0) return false; - // sort the bin so we remove elements from back to front - std::sort(bin.begin(), bin.end(), std::greater()); - for (const unsigned& index : bin) { - first_set.erase(first_set.begin() + index); - } - if (first_set.size() == 0) { - rotation_sets.erase(rotation_sets.begin()); - return true; - } - return false; } /** @@ -413,9 +374,8 @@ static void pauli_exps_synthesis( std::vector& rows, Circuit& circ, double discount_rate, double depth_weight, DepthTracker& depth_tracker) { while (true) { - while (consume_available_rotations( - rotation_sets, circ, depth_tracker)); // do nothing - if (rotation_sets.size() == 0) break; + consume_available_rotations(rotation_sets, circ, depth_tracker); + if (rotation_sets.empty()) break; std::vector& first_set = rotation_sets[0]; // get nodes with min cost std::vector min_nodes_indices = {0}; @@ -467,9 +427,8 @@ static PauliNode_ptr get_node_from_exp( const qubit_vector_t& args, unsigned n) { // pad the Paulis std::vector string(n, Pauli::I); - for (const Qubit& q : args) { - unsigned a = q.index()[0]; - string[a] = paulis[a]; + for (unsigned i = 0; i < args.size(); i++) { + string[args[i].index()[0]] = paulis[i]; } return std::make_shared(string, theta); } @@ -488,8 +447,8 @@ static std::vector get_nodes_from_tableau( std::vector z_string; std::vector x_string; for (unsigned j = 0; j < n_qubits; j++) { - z_string.push_back(z_stab.string.at(Qubit(i))); - x_string.push_back(x_stab.string.at(Qubit(i))); + z_string.push_back(z_stab.string.at(Qubit(j))); + x_string.push_back(x_stab.string.at(Qubit(j))); } rows.push_back(std::make_shared( z_string, x_string, z_sign, x_sign, i)); @@ -553,7 +512,6 @@ Circuit greedy_pauli_graph_synthesis( unit_map_t unit_map = c.flatten_registers(); Circuit measure_circ(c.n_qubits(), c.n_bits()); Circuit cliff(c.n_qubits()); - // circuit used to iterate the original commands with flattened registers Circuit circ_flat(circ); circ_flat.flatten_registers(); From 7a32bd1fadd2330394b3eff0456ecf85daf7b3e9 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Wed, 25 Sep 2024 18:03:43 +0100 Subject: [PATCH 07/57] Refactor pauli graph converters --- tket/CMakeLists.txt | 1 + .../GreedyPauliOptimisation.hpp | 31 +++ .../Transformations/GreedyPauliConverters.cpp | 223 ++++++++++++++++++ .../GreedyPauliOptimisation.cpp | 178 +------------- 4 files changed, 260 insertions(+), 173 deletions(-) create mode 100644 tket/src/Transformations/GreedyPauliConverters.cpp diff --git a/tket/CMakeLists.txt b/tket/CMakeLists.txt index e9ef5f60c9..9a1b7cb015 100644 --- a/tket/CMakeLists.txt +++ b/tket/CMakeLists.txt @@ -270,6 +270,7 @@ target_sources(tket src/Transformations/Decomposition.cpp src/Transformations/GreedyPauliOptimisation.cpp src/Transformations/GreedyPauliOps.cpp + src/Transformations/GreedyPauliConverters.cpp src/Transformations/Replacement.cpp src/Transformations/MeasurePass.cpp src/Transformations/ContextualReduction.cpp diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 607b6fb15c..391e653c10 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -280,6 +280,37 @@ class PauliPropagation : public ACPairNode { unsigned qubit_index_; }; +/** + * @brief Convert a unordered set of SymPauliTensor into a set of PauliRotations + * followed by a set of PauliPropagations + * + * @param unordered_set + * @return std::tuple, std::vector> + */ +std::tuple, std::vector> +gpg_from_unordered_set(const std::vector& unordered_set); + +/** + * @brief Transforms a circuit of PauliExpBoxes followed by Clifford gates and + * end-of-circuit measurements into a sequence of PauliRotations, + * PauliPropagations, and an end-of-circuit measurement circuit. + * + * The first returned object is an empty circuit onto which the synthesized + * circuit will be built. The final element of the returned tuple is a map from + * integers to the original UnitIDs, allowing the final circuit to use the + * orignal UnitIDs. + * + * @param circ + * @return std::tuple< + * Circuit, + * std::vector>, std::vector, + * Circuit, unit_map_t> + */ +std::tuple< + Circuit, std::vector>, + std::vector, Circuit, unit_map_t> +gpg_from_circuit(const Circuit& circ); + /** * @brief Given a circuit consists of PauliExpBoxes followed by clifford gates, * and end-of-circuit measurements, implement the PauliExpBoxes and the final diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp new file mode 100644 index 0000000000..625aab52ad --- /dev/null +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -0,0 +1,223 @@ +// Copyright 2019-2024 Cambridge Quantum Computing +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "tket/Circuit/PauliExpBoxes.hpp" +#include "tket/Converters/Converters.hpp" +#include "tket/OpType/OpType.hpp" +#include "tket/PauliGraph/PauliGraph.hpp" +#include "tket/Transformations/CliffordOptimisation.hpp" +#include "tket/Transformations/GreedyPauliOptimisation.hpp" +#include "tket/Transformations/GreedyPauliOptimisationLookupTables.hpp" +#include "tket/Transformations/Transform.hpp" + +namespace tket { + +namespace Transforms { + +namespace GreedyPauliSimp { + +// convert a Pauli exponential to a PauliNode_ptr +static PauliNode_ptr get_node_from_exp( + const std::vector& paulis, const Expr& theta, + const qubit_vector_t& args, unsigned n) { + // pad the Paulis + std::vector string(n, Pauli::I); + for (unsigned i = 0; i < args.size(); i++) { + string[args[i].index()[0]] = paulis[i]; + } + return std::make_shared(string, theta); +} + +// convert a Clifford tableau to a vector of PauliNode_ptr +static std::vector get_nodes_from_tableau( + const UnitaryRevTableau& tab, unsigned n_qubits) { + std::vector rows; + for (unsigned i = 0; i < n_qubits; i++) { + Qubit q(i); + SpPauliStabiliser z_stab = tab.get_zrow(q); + SpPauliStabiliser x_stab = tab.get_xrow(q); + bool z_sign = cast_coeff(z_stab.coeff) == 1.; + bool x_sign = cast_coeff(x_stab.coeff) == 1.; + TKET_ASSERT(z_stab.string.size() == n_qubits); + std::vector z_string; + std::vector x_string; + for (unsigned j = 0; j < n_qubits; j++) { + z_string.push_back(z_stab.string.at(Qubit(j))); + x_string.push_back(x_stab.string.at(Qubit(j))); + } + rows.push_back(std::make_shared( + z_string, x_string, z_sign, x_sign, i)); + } + return rows; +} + +// detect trivial pauli exps, if true then return the global phase +static std::pair is_trivial_pauliexp( + const std::vector& paulis, const Expr& theta) { + if (static_cast(std::count( + paulis.begin(), paulis.end(), Pauli::I)) == paulis.size()) { + // If all identity term + return {true, -theta / 2}; + } + if (equiv_0(theta, 2)) { + if (equiv_0(theta, 4)) { + return {true, 0}; + } else { + return {true, -1}; + } + } + return {false, 0}; +} + +std::tuple, std::vector> +gpg_from_unordered_set(const std::vector& unordered_set) { + std::vector rotation_set; + unsigned n_qubits = unordered_set[0].string.size(); + for (auto& pauli : unordered_set) { + TKET_ASSERT(pauli.string.size() == n_qubits); + rotation_set.push_back( + std::make_shared(pauli.string, pauli.coeff)); + } + UnitaryRevTableau tab(n_qubits); + std::vector rows = get_nodes_from_tableau(tab, n_qubits); + return {rotation_set, rows}; +} + +std::tuple< + Circuit, std::vector>, + std::vector, Circuit, unit_map_t> +gpg_from_circuit(const Circuit& circ) { + // circuit for conversion + Circuit circ_flat(circ); + unsigned n_qubits = circ_flat.n_qubits(); + unsigned n_bits = circ_flat.n_bits(); + // empty circuit + Circuit empty_circ(n_qubits, n_bits); + std::optional name = circ_flat.get_name(); + if (name != std::nullopt) { + empty_circ.set_name(name.value()); + } + empty_circ.add_phase(circ_flat.get_phase()); + // measurement circuit + Circuit measure_circ(n_qubits, n_bits); + + // flatten registers before process + unit_map_t unit_map = circ_flat.flatten_registers(); + unit_map_t rev_unit_map; + for (const auto& pair : unit_map) { + rev_unit_map.insert({pair.second, pair.first}); + } + + std::vector> rotation_sets; + std::vector commands = circ_flat.get_commands(); + Circuit cliff(n_qubits); + // extract the final clifford and the measurement circuits + for (const Command& cmd : commands) { + OpType optype = cmd.get_op_ptr()->get_type(); + switch (optype) { + case OpType::Measure: { + measure_circ.add_op(OpType::Measure, cmd.get_args()); + break; + } + default: { + if (optype == OpType::PauliExpBox || + optype == OpType::PauliExpPairBox || + optype == OpType::PauliExpCommutingSetBox) + break; + TKET_ASSERT(is_clifford_type(optype) && is_gate_type(optype)); + cliff.add_op(optype, cmd.get_args()); + } + } + } + UnitaryRevTableau tab = circuit_to_unitary_rev_tableau(cliff); + // convert the tableau into a set of nodes + std::vector rows = get_nodes_from_tableau(tab, n_qubits); + // extract the Pauli exps + for (const Command& cmd : commands) { + OpType optype = cmd.get_op_ptr()->get_type(); + switch (optype) { + case OpType::PauliExpBox: { + const PauliExpBox& pbox = + static_cast(*cmd.get_op_ptr()); + const Expr phase = pbox.get_phase(); + const std::vector paulis = pbox.get_paulis(); + auto [trivial, global_phase] = is_trivial_pauliexp(paulis, phase); + if (trivial) { + empty_circ.add_phase(global_phase); + } else { + rotation_sets.push_back( + {get_node_from_exp(paulis, phase, cmd.get_qubits(), n_qubits)}); + } + break; + } + case OpType::PauliExpPairBox: { + const PauliExpPairBox& pbox = + static_cast(*cmd.get_op_ptr()); + const auto [paulis1, paulis2] = pbox.get_paulis_pair(); + const auto [phase1, phase2] = pbox.get_phase_pair(); + auto [trivial1, global_phase1] = is_trivial_pauliexp(paulis1, phase1); + auto [trivial2, global_phase2] = is_trivial_pauliexp(paulis2, phase2); + std::vector rotation_set; + if (trivial1) { + empty_circ.add_phase(global_phase1); + } else { + rotation_set.push_back( + get_node_from_exp(paulis1, phase1, cmd.get_qubits(), n_qubits)); + } + if (trivial2) { + empty_circ.add_phase(global_phase2); + } else { + rotation_set.push_back( + get_node_from_exp(paulis2, phase2, cmd.get_qubits(), n_qubits)); + } + if (!rotation_set.empty()) { + rotation_sets.push_back(rotation_set); + } + break; + } + case OpType::PauliExpCommutingSetBox: { + const PauliExpCommutingSetBox& pbox = + static_cast(*cmd.get_op_ptr()); + const std::vector gadgets = pbox.get_pauli_gadgets(); + std::vector rotation_set; + for (const SymPauliTensor& pt : gadgets) { + const std::vector paulis = pt.string; + const Expr phase = pt.coeff; + auto [trivial, global_phase] = is_trivial_pauliexp(paulis, phase); + if (trivial) { + empty_circ.add_phase(global_phase); + } else { + rotation_set.push_back( + get_node_from_exp(paulis, phase, cmd.get_qubits(), n_qubits)); + } + } + if (rotation_set.size() > 0) { + rotation_sets.push_back(rotation_set); + } + break; + } + default: + break; + } + } + return {empty_circ, rotation_sets, rows, measure_circ, rev_unit_map}; +} + +} // namespace GreedyPauliSimp + +} // namespace Transforms + +} // namespace tket diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index cad435a5bb..5e1f959c59 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -421,76 +421,15 @@ static void pauli_exps_synthesis( } } -// convert a Pauli exponential to a PauliNode_ptr -static PauliNode_ptr get_node_from_exp( - const std::vector& paulis, const Expr& theta, - const qubit_vector_t& args, unsigned n) { - // pad the Paulis - std::vector string(n, Pauli::I); - for (unsigned i = 0; i < args.size(); i++) { - string[args[i].index()[0]] = paulis[i]; - } - return std::make_shared(string, theta); -} - -// convert a Clifford tableau to a vector of PauliNode_ptr -static std::vector get_nodes_from_tableau( - const UnitaryRevTableau& tab, unsigned n_qubits) { - std::vector rows; - for (unsigned i = 0; i < n_qubits; i++) { - Qubit q(i); - SpPauliStabiliser z_stab = tab.get_zrow(q); - SpPauliStabiliser x_stab = tab.get_xrow(q); - bool z_sign = cast_coeff(z_stab.coeff) == 1.; - bool x_sign = cast_coeff(x_stab.coeff) == 1.; - TKET_ASSERT(z_stab.string.size() == n_qubits); - std::vector z_string; - std::vector x_string; - for (unsigned j = 0; j < n_qubits; j++) { - z_string.push_back(z_stab.string.at(Qubit(j))); - x_string.push_back(x_stab.string.at(Qubit(j))); - } - rows.push_back(std::make_shared( - z_string, x_string, z_sign, x_sign, i)); - } - return rows; -} - -// detect trivial pauli exps, if true then return the global phase -static std::pair is_trivial_pauliexp( - const std::vector& paulis, const Expr& theta) { - if (static_cast(std::count( - paulis.begin(), paulis.end(), Pauli::I)) == paulis.size()) { - // If all identity term - return {true, -theta / 2}; - } - if (equiv_0(theta, 2)) { - if (equiv_0(theta, 4)) { - return {true, 0}; - } else { - return {true, -1}; - } - } - return {false, 0}; -} - Circuit greedy_pauli_set_synthesis( const std::vector& unordered_set, double depth_weight) { if (unordered_set.size() == 0) { return Circuit(); } unsigned n_qubits = unordered_set[0].string.size(); - Circuit c(n_qubits); - std::vector> rotation_sets{{}}; - - for (auto& pauli : unordered_set) { - TKET_ASSERT(pauli.string.size() == n_qubits); - rotation_sets[0].push_back( - std::make_shared(pauli.string, pauli.coeff)); - } - UnitaryRevTableau tab(n_qubits); - std::vector rows = get_nodes_from_tableau(tab, n_qubits); + auto [rotation_set, rows] = gpg_from_unordered_set(unordered_set); + std::vector> rotation_sets{rotation_set}; DepthTracker depth_tracker(n_qubits); // synthesise Pauli exps pauli_exps_synthesis(rotation_sets, rows, c, 0, depth_weight, depth_tracker); @@ -503,121 +442,14 @@ Circuit greedy_pauli_set_synthesis( Circuit greedy_pauli_graph_synthesis( const Circuit& circ, double discount_rate, double depth_weight) { // c is the circuit we are trying to build - Circuit c(circ.all_qubits(), circ.all_bits()); - std::optional name = circ.get_name(); - if (name != std::nullopt) { - c.set_name(name.value()); - } - c.add_phase(circ.get_phase()); - unit_map_t unit_map = c.flatten_registers(); - Circuit measure_circ(c.n_qubits(), c.n_bits()); - Circuit cliff(c.n_qubits()); - // circuit used to iterate the original commands with flattened registers - Circuit circ_flat(circ); - circ_flat.flatten_registers(); - std::vector commands = circ_flat.get_commands(); - // extract the final clifford and the measurement circuits - for (const Command& cmd : commands) { - OpType optype = cmd.get_op_ptr()->get_type(); - switch (optype) { - case OpType::Measure: { - measure_circ.add_op(OpType::Measure, cmd.get_args()); - break; - } - default: { - if (optype == OpType::PauliExpBox || - optype == OpType::PauliExpPairBox || - optype == OpType::PauliExpCommutingSetBox) - break; - TKET_ASSERT(is_clifford_type(optype) && is_gate_type(optype)); - cliff.add_op(optype, cmd.get_args()); - } - } - } - std::vector> rotation_sets; - unsigned n_qubits = c.n_qubits(); - UnitaryRevTableau tab = circuit_to_unitary_rev_tableau(cliff); - // convert the tableau into a set of nodes - std::vector rows = get_nodes_from_tableau(tab, n_qubits); - // extract the Pauli exps - for (const Command& cmd : commands) { - OpType optype = cmd.get_op_ptr()->get_type(); - switch (optype) { - case OpType::PauliExpBox: { - const PauliExpBox& pbox = - static_cast(*cmd.get_op_ptr()); - const Expr phase = pbox.get_phase(); - const std::vector paulis = pbox.get_paulis(); - auto [trivial, global_phase] = is_trivial_pauliexp(paulis, phase); - if (trivial) { - c.add_phase(global_phase); - } else { - rotation_sets.push_back( - {get_node_from_exp(paulis, phase, cmd.get_qubits(), n_qubits)}); - } - break; - } - case OpType::PauliExpPairBox: { - const PauliExpPairBox& pbox = - static_cast(*cmd.get_op_ptr()); - const auto [paulis1, paulis2] = pbox.get_paulis_pair(); - const auto [phase1, phase2] = pbox.get_phase_pair(); - auto [trivial1, global_phase1] = is_trivial_pauliexp(paulis1, phase1); - auto [trivial2, global_phase2] = is_trivial_pauliexp(paulis2, phase2); - std::vector rotation_set; - if (trivial1) { - c.add_phase(global_phase1); - } else { - rotation_set.push_back( - get_node_from_exp(paulis1, phase1, cmd.get_qubits(), n_qubits)); - } - if (trivial2) { - c.add_phase(global_phase2); - } else { - rotation_set.push_back( - get_node_from_exp(paulis2, phase2, cmd.get_qubits(), n_qubits)); - } - if (!rotation_set.empty()) { - rotation_sets.push_back(rotation_set); - } - break; - } - case OpType::PauliExpCommutingSetBox: { - const PauliExpCommutingSetBox& pbox = - static_cast(*cmd.get_op_ptr()); - const std::vector gadgets = pbox.get_pauli_gadgets(); - std::vector rotation_set; - for (const SymPauliTensor& pt : gadgets) { - const std::vector paulis = pt.string; - const Expr phase = pt.coeff; - auto [trivial, global_phase] = is_trivial_pauliexp(paulis, phase); - if (trivial) { - c.add_phase(global_phase); - } else { - rotation_set.push_back( - get_node_from_exp(paulis, phase, cmd.get_qubits(), n_qubits)); - } - } - if (rotation_set.size() > 0) { - rotation_sets.push_back(rotation_set); - } - break; - } - default: - break; - } - } - - DepthTracker depth_tracker(n_qubits); + auto [c, rotation_sets, rows, measure_circ, rev_unit_map] = + gpg_from_circuit(circ); + DepthTracker depth_tracker(c.n_qubits()); // synthesise Pauli exps pauli_exps_synthesis( rotation_sets, rows, c, discount_rate, depth_weight, depth_tracker); // synthesise the tableau tableau_row_nodes_synthesis(rows, c, depth_weight, depth_tracker); - unit_map_t rev_unit_map; - for (const auto& pair : unit_map) { - rev_unit_map.insert({pair.second, pair.first}); - } c.append(measure_circ); c.rename_units(rev_unit_map); c.replace_SWAPs(); From b8071a725a9646beee5c150d2400b60d300ff99c Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Wed, 2 Oct 2024 12:21:11 +0100 Subject: [PATCH 08/57] Initial implementation of GPGraph --- .../GreedyPauliOptimisation.hpp | 72 +++ .../Transformations/GreedyPauliConverters.cpp | 426 +++++++++++++++++- 2 files changed, 496 insertions(+), 2 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 391e653c10..c073625aee 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -16,6 +16,7 @@ #include "Transform.hpp" #include "tket/Circuit/Circuit.hpp" +#include "tket/Clifford/UnitaryTableau.hpp" namespace tket { @@ -137,6 +138,8 @@ class SingleNode : public PauliNode { bool sign() const { return sign_; }; + std::vector string() const { return string_; }; + protected: std::vector string_; bool sign_; @@ -280,6 +283,75 @@ class PauliPropagation : public ACPairNode { unsigned qubit_index_; }; +typedef boost::adjacency_list< + boost::listS, boost::listS, boost::bidirectionalS, + // indexing needed for algorithms such as topological sort + boost::property> + GPDAG; + +typedef boost::graph_traits::vertex_descriptor GPVert; +typedef boost::graph_traits::edge_descriptor GPEdge; + +typedef sequence_set_t GPVertSet; +typedef sequence_set_t GPEdgeSet; + +/** + * Pauli graph structure for GreedyPauliSimp. + * The dependency graph consists of Pauli rotations, mid-circuit measurements, + * resets, conditional Pauli rotations, and classical operations as internal + * nodes. Edges represent gate dependencies, where one node depends on another + * if their underlying Pauli strings do not commute or if they share a classical + * bit. + * + * End-of-circuit measurements are stored as an integer-to-integer map. These + * measurements are kept separately (i.e. after the final Clifford) so the + * optimisation around them can be later handed to CliffordPushThroughMeausres. + * + * The final Clifford operator is stored using a UnitaryRevTableau. Note that + * UnitaryRevTableau is chose over PauliPropagations because of the abundance of + * already existed updating methods. + * + */ +class GPGraph { + /** Construct an empty dependency graph */ + GPGraph(const unsigned& n_qubits, const unsigned& n_bits); + + GPVertSet get_successors(const GPVert& vert) const; + + GPVertSet get_predecessors(const GPVert& vert) const; + + /** + * Applies the given gate to the end of the circuit. + * Clifford gates transform the tableau. + * Non-Clifford gates and conditional Clifford gates are transformed + * into PauliNodes by the tableau and added + * to the graph. + */ + void apply_gate_at_end(const Command& cmd); + + private: + void apply_node_at_end(PauliNode_ptr& node); + + /** + * The dependency graph of Pauli nodes + * + * This is mutated by \ref vertices_in_order which indexes the vertices + * without changing the structure. + */ + mutable GPDAG graph_; + + [[maybe_unused]] const unsigned n_qubits_; + [[maybe_unused]] const unsigned n_bits_; + + /** The tableau of the Clifford effect of the circuit */ + UnitaryRevTableau cliff_; + /** The record of measurements at the very end of the circuit */ + boost::bimap measures_; + + GPVertSet start_line_; + GPVertSet end_line_; +}; + /** * @brief Convert a unordered set of SymPauliTensor into a set of PauliRotations * followed by a set of PauliPropagations diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index 625aab52ad..4fc6fa08f9 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -36,7 +36,7 @@ static PauliNode_ptr get_node_from_exp( // pad the Paulis std::vector string(n, Pauli::I); for (unsigned i = 0; i < args.size(); i++) { - string[args[i].index()[0]] = paulis[i]; + string[args[i].index().at(0)] = paulis[i]; } return std::make_shared(string, theta); } @@ -85,7 +85,7 @@ static std::pair is_trivial_pauliexp( std::tuple, std::vector> gpg_from_unordered_set(const std::vector& unordered_set) { std::vector rotation_set; - unsigned n_qubits = unordered_set[0].string.size(); + unsigned n_qubits = unordered_set.at(0).string.size(); for (auto& pauli : unordered_set) { TKET_ASSERT(pauli.string.size() == n_qubits); rotation_set.push_back( @@ -216,6 +216,428 @@ gpg_from_circuit(const Circuit& circ) { return {empty_circ, rotation_sets, rows, measure_circ, rev_unit_map}; } +// given a stabiliser Pauli string and an angle, return a dense string and an +// angle +static std::pair, Expr> dense_pauli( + const SpPauliStabiliser& pauli, const unsigned& n_qubits, + const Expr& angle) { + bool sign = cast_coeff(pauli.coeff) == 1.; + std::vector string(n_qubits, Pauli::I); + for (unsigned i = 0; i < n_qubits; i++) { + string[i] = pauli.string.at(Qubit(i)); + } + Expr new_angle = angle; + if (!sign) { + new_angle *= -1; + } + return {string, new_angle}; +} + +static bool strings_commute( + const std::vector& s1, const std::vector& s2) { + unsigned n_conflicts = 0; + TKET_ASSERT(s1.size() == s2.size()); + for (unsigned i = 0; i < s1.size(); ++i) { + Pauli p = s1[i]; + Pauli p2 = s2[i]; + if (p != Pauli::I && p2 != Pauli::I && p != p2) n_conflicts++; + } + return (n_conflicts % 2) == 0; +} + +static bool nodes_commute(const PauliNode_ptr& n1, const PauliNode_ptr& n2) { + if (n1->get_type() == PauliNodeType::Rotation && + n2->get_type() == PauliNodeType::Rotation) { + const PauliRotation& rot1 = dynamic_cast(*n1); + const PauliRotation& rot2 = dynamic_cast(*n2); + return strings_commute(rot1.string(), rot2.string()); + } else { + TKET_ASSERT(false); + } +} + +std::optional merge_nodes( + const PauliNode_ptr& n1, const PauliNode_ptr& n2) { + if (n1->get_type() == PauliNodeType::Rotation && + n2->get_type() == PauliNodeType::Rotation) { + const PauliRotation& rot1 = dynamic_cast(*n1); + const PauliRotation& rot2 = dynamic_cast(*n2); + if (rot1.string() == rot2.string()) { + Expr angle1 = rot1.sign() ? rot1.theta() : -rot1.theta(); + Expr angle2 = rot2.sign() ? rot2.theta() : -rot2.theta(); + return std::make_shared(rot1.string(), angle1 + angle2); + } + } + return std::nullopt; +} + +GPGraph::GPGraph(const unsigned& n_qubits, const unsigned& n_bits) + : n_qubits_(n_qubits), n_bits_(n_bits), cliff_(n_qubits) {} + +GPVertSet GPGraph::get_successors(const GPVert& vert) const { + GPVertSet succs; + for (auto iter = boost::adjacent_vertices(vert, graph_); + iter.first != iter.second; iter.first++) { + succs.insert(*iter.first); + } + return succs; +} + +GPVertSet GPGraph::get_predecessors(const GPVert& vert) const { + GPVertSet preds; + for (auto iter = boost::inv_adjacent_vertices(vert, graph_); + iter.first != iter.second; iter.first++) { + preds.insert(*iter.first); + } + return preds; +} + +void GPGraph::apply_node_at_end(PauliNode_ptr& node) { + GPVertSet to_search = end_line_; + GPVertSet commuted; + GPVert new_vert = boost::add_vertex(graph_); + graph_[new_vert] = node; + while (!to_search.empty()) { + // Get next candidate parent + GPVert to_compare = *to_search.begin(); + to_search.erase(to_search.begin()); + // Check that we have already commuted past all of its children + bool ready = true; + for (const PauliVert& child : get_successors(to_compare)) { + if (commuted.get().find(child) == commuted.get().end()) { + ready = false; + break; + } + } + if (!ready) continue; + // Check if we can commute past it + PauliNode_ptr compare_node = graph_[to_compare]; + if (nodes_commute(node, compare_node)) { + if (node->get_type() == PauliNodeType::Rotation && + compare_node->get_type() == PauliNodeType::Rotation) { + const PauliRotation& rot1 = dynamic_cast(*node); + const PauliRotation& rot2 = + dynamic_cast(*compare_node); + if (rot1.string() == rot2.string()) { + boost::clear_vertex(new_vert, graph_); + boost::remove_vertex(new_vert, graph_); + Expr merged_angle = (rot1.sign() ? rot1.theta() : -rot1.theta()) + + (rot2.sign() ? rot2.theta() : -rot2.theta()); + std::optional cl_ang = equiv_Clifford(merged_angle); + if (cl_ang) { + cliff_.apply_pauli_at_front( + SpPauliStabiliser(rot1.string()), *cl_ang); + start_line_.erase(to_compare); + for (const GPVert& v : get_predecessors(to_compare)) { + if (boost::out_degree(v, graph_) == 1) { + end_line_.insert(v); + } + } + end_line_.erase(to_compare); + boost::clear_vertex(to_compare, graph_); + boost::remove_vertex(to_compare, graph_); + } else { + graph_[to_compare] = + std::make_shared(rot1.string(), merged_angle); + } + return; + } + } + // Commute and continue searching + PauliVertSet preds = get_predecessors(to_compare); + to_search.insert(preds.begin(), preds.end()); + commuted.insert(to_compare); + } else { + // Does not commute - add dependency edge + boost::add_edge(to_compare, new_vert, graph_); + end_line_.erase(to_compare); + } + } + end_line_.insert(new_vert); + if (get_predecessors(new_vert).empty()) start_line_.insert(new_vert); +} + +void GPGraph::apply_gate_at_end(const Command& cmd) { + const Op_ptr op = cmd.get_op_ptr(); + unit_vector_t args = cmd.get_args(); + qubit_vector_t qbs = {args.begin(), args.end()}; + OpType type = op->get_type(); + + for (const UnitID& arg : args) { + if (arg.type() == UnitType::Qubit) { + if (measures_.left.find(arg.index().at(0)) != measures_.left.end()) { + throw MidCircuitMeasurementNotAllowed( + "PauliGraph does not support mid-circuit measurements " + "- cannot add gate after measure on qubit " + + arg.repr()); + } + } else if ( + measures_.right.find(arg.index().at(0)) != measures_.right.end()) { + throw MidCircuitMeasurementNotAllowed( + "PauliGraph does not support mid-circuit measurements - " + "cannot add gate after measure to bit " + + arg.repr()); + } + } + + switch (type) { + case OpType::Measure: { + measures_.insert({args.at(0).index().at(0), args.at(1).index().at(0)}); + break; + } + case OpType::Z: + case OpType::X: + case OpType::Y: + case OpType::S: + case OpType::Sdg: + case OpType::V: + case OpType::Vdg: + case OpType::H: + case OpType::CX: + case OpType::CY: + case OpType::CZ: + case OpType::SWAP: + case OpType::noop: + case OpType::Phase: { + cliff_.apply_gate_at_end(type, qbs); + break; + } + case OpType::Rz: { + SpPauliStabiliser pauli = cliff_.get_zrow(qbs.at(0)); + Expr angle = op->get_params().at(0); + std::optional cliff_angle = equiv_Clifford(angle); + if (cliff_angle) { + for (unsigned i = 0; i < cliff_angle.value(); i++) { + cliff_.apply_gate_at_end(OpType::S, qbs); + } + } else { + auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, angle); + PauliNode_ptr node = + std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + } + break; + } + case OpType::Rx: { + SpPauliStabiliser pauli = cliff_.get_xrow(qbs.at(0)); + Expr angle = op->get_params().at(0); + std::optional cliff_angle = equiv_Clifford(angle); + if (cliff_angle) { + for (unsigned i = 0; i < cliff_angle.value(); i++) { + cliff_.apply_gate_at_end(OpType::V, qbs); + } + } else { + auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, angle); + PauliNode_ptr node = + std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + } + break; + } + case OpType::Ry: { + Expr angle = op->get_params().at(0); + std::optional cliff_angle = equiv_Clifford(angle); + if (cliff_angle) { + if (cliff_angle.value() != 0) { + cliff_.apply_gate_at_end(OpType::V, qbs); + for (unsigned i = 0; i < cliff_angle.value(); i++) { + cliff_.apply_gate_at_end(OpType::S, qbs); + } + cliff_.apply_gate_at_end(OpType::Vdg, qbs); + } + } else { + SpPauliStabiliser ypauli = + cliff_.get_row_product(SpPauliStabiliser(qbs.at(0), Pauli::Y)); + auto [pauli_dense, theta] = dense_pauli(ypauli, n_qubits_, angle); + PauliNode_ptr node = + std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + } + break; + } + case OpType::PhasedX: { + Expr alpha = op->get_params().at(0); + Expr beta = op->get_params().at(1); + std::optional cliff_alpha = equiv_Clifford(alpha); + std::optional cliff_beta = equiv_Clifford(beta); + // Rz(-b) + if (cliff_beta) { + for (unsigned i = 0; i < cliff_beta.value(); i++) { + cliff_.apply_gate_at_end(OpType::Sdg, qbs); + } + } else { + SpPauliStabiliser zpauli = cliff_.get_zrow(qbs.at(0)); + auto [pauli_dense, theta] = dense_pauli(zpauli, n_qubits_, -beta); + PauliNode_ptr node = + std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + } + // Rx(a) + if (cliff_alpha) { + for (unsigned i = 0; i < cliff_alpha.value(); i++) { + cliff_.apply_gate_at_end(OpType::V, qbs); + } + } else { + SpPauliStabiliser xpauli = cliff_.get_xrow(qbs.at(0)); + auto [pauli_dense, theta] = dense_pauli(xpauli, n_qubits_, alpha); + PauliNode_ptr node = + std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + } + // Rz(b) + if (cliff_beta) { + for (unsigned i = 0; i < cliff_beta.value(); i++) { + cliff_.apply_gate_at_end(OpType::S, qbs); + } + } else { + SpPauliStabiliser zpauli = cliff_.get_zrow(qbs.at(0)); + auto [pauli_dense, theta] = dense_pauli(zpauli, n_qubits_, beta); + PauliNode_ptr node = + std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + } + break; + } + case OpType::T: { + SpPauliStabiliser pauli = cliff_.get_zrow(qbs.at(0)); + auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, 0.25); + PauliNode_ptr node = std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + break; + } + case OpType::Tdg: { + SpPauliStabiliser pauli = cliff_.get_zrow(qbs.at(0)); + auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, -0.25); + PauliNode_ptr node = std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + break; + } + case OpType::ZZMax: { + cliff_.apply_gate_at_end(OpType::S, {qbs.at(0)}); + cliff_.apply_gate_at_end(OpType::Z, {qbs.at(1)}); + cliff_.apply_gate_at_end(OpType::S, {qbs.at(1)}); + cliff_.apply_gate_at_end(OpType::V, {qbs.at(1)}); + cliff_.apply_gate_at_end(OpType::S, {qbs.at(1)}); + cliff_.apply_gate_at_end(OpType::CX, qbs); + cliff_.apply_gate_at_end(OpType::S, {qbs.at(1)}); + cliff_.apply_gate_at_end(OpType::V, {qbs.at(1)}); + break; + } + case OpType::PhaseGadget: + case OpType::ZZPhase: { + Expr angle = op->get_params().at(0); + std::optional cliff_angle = equiv_Clifford(angle); + if (cliff_angle) { + if (cliff_angle.value() != 0) { + QubitPauliMap qpm; + for (const Qubit& q : qbs) qpm.insert({q, Pauli::Z}); + cliff_.apply_pauli_at_end(SpPauliStabiliser(qpm), *cliff_angle); + } + } else { + QubitPauliMap qpm; + for (const Qubit& q : qbs) qpm.insert({q, Pauli::Z}); + SpPauliStabiliser pauli = + cliff_.get_row_product(SpPauliStabiliser(qpm)); + auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, angle); + PauliNode_ptr node = + std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + } + break; + } + case OpType::XXPhase: { + Expr angle = op->get_params().at(0); + std::optional cliff_angle = equiv_Clifford(angle); + if (cliff_angle) { + if (cliff_angle.value() != 0) { + cliff_.apply_pauli_at_end( + SpPauliStabiliser({{qbs.at(0), Pauli::X}, {qbs.at(1), Pauli::X}}), + *cliff_angle); + } + } else { + SpPauliStabiliser pauli = cliff_.get_row_product( + SpPauliStabiliser({{qbs.at(0), Pauli::X}, {qbs.at(1), Pauli::X}})); + auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, angle); + PauliNode_ptr node = + std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + } + break; + } + case OpType::YYPhase: { + Expr angle = op->get_params().at(0); + std::optional cliff_angle = equiv_Clifford(angle); + if (cliff_angle) { + if (cliff_angle.value() != 0) { + cliff_.apply_pauli_at_end( + SpPauliStabiliser({{qbs.at(0), Pauli::Y}, {qbs.at(1), Pauli::Y}}), + *cliff_angle); + } + } else { + SpPauliStabiliser pauli = cliff_.get_row_product( + SpPauliStabiliser({{qbs.at(0), Pauli::Y}, {qbs.at(1), Pauli::Y}})); + auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, angle); + PauliNode_ptr node = + std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + } + break; + } + case OpType::PauliExpBox: { + const PauliExpBox& peb = static_cast(*op); + std::vector paulis = peb.get_paulis(); + Expr phase = peb.get_phase(); + QubitPauliMap qpm; + for (unsigned i = 0; i != args.size(); ++i) + qpm.insert({Qubit(args[i]), paulis[i]}); + SpPauliStabiliser qpt = cliff_.get_row_product(SpPauliStabiliser(qpm)); + auto [pauli_dense, theta] = dense_pauli(qpt, n_qubits_, phase); + PauliNode_ptr node = std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + } + case OpType::PauliExpPairBox: { + const PauliExpPairBox& peb = static_cast(*op); + auto [paulis1, paulis2] = peb.get_paulis_pair(); + auto [phase1, phase2] = peb.get_phase_pair(); + QubitPauliMap qpm1, qpm2; + for (unsigned i = 0; i != args.size(); ++i) { + qpm1.insert({Qubit(args[i]), paulis1[i]}); + qpm2.insert({Qubit(args[i]), paulis2[i]}); + } + SpPauliStabiliser qpt1 = cliff_.get_row_product(SpPauliStabiliser(qpm1)); + SpPauliStabiliser qpt2 = cliff_.get_row_product(SpPauliStabiliser(qpm2)); + auto [pauli_dense1, theta1] = dense_pauli(qpt1, n_qubits_, phase1); + PauliNode_ptr node1 = + std::make_shared(pauli_dense1, theta1); + apply_node_at_end(node1); + auto [pauli_dense2, theta2] = dense_pauli(qpt2, n_qubits_, phase2); + PauliNode_ptr node2 = + std::make_shared(pauli_dense2, theta2); + apply_node_at_end(node2); + } + case OpType::PauliExpCommutingSetBox: { + const PauliExpCommutingSetBox& peb = + static_cast(*op); + for (const SymPauliTensor& pt : peb.get_pauli_gadgets()) { + const std::vector paulis = pt.string; + const Expr phase = pt.coeff; + QubitPauliMap qpm; + for (unsigned i = 0; i != args.size(); ++i) { + qpm.insert({Qubit(args[i]), paulis[i]}); + } + SpPauliStabiliser qpt = cliff_.get_row_product(SpPauliStabiliser(qpm)); + auto [pauli_dense, theta] = dense_pauli(qpt, n_qubits_, phase); + PauliNode_ptr node = + std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + } + } + default: { + throw BadOpType("Cannot add gate to GPGraph", type); + } + } +} + } // namespace GreedyPauliSimp } // namespace Transforms From 48c8b2c01bc559f832d6682c1383c54d6263a3eb Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Wed, 2 Oct 2024 23:43:38 +0100 Subject: [PATCH 09/57] Migrate to GPGraph --- .../GreedyPauliOptimisation.hpp | 57 +-- .../Transformations/GreedyPauliConverters.cpp | 324 ++++++------------ .../GreedyPauliOptimisation.cpp | 39 ++- tket/test/src/test_GreedyPauli.cpp | 5 +- 4 files changed, 159 insertions(+), 266 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index c073625aee..85b9678af0 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -295,6 +295,10 @@ typedef boost::graph_traits::edge_descriptor GPEdge; typedef sequence_set_t GPVertSet; typedef sequence_set_t GPEdgeSet; +typedef boost::adj_list_vertex_property_map< + GPDAG, int, int&, boost::vertex_index_t> + GPVIndex; + /** * Pauli graph structure for GreedyPauliSimp. * The dependency graph consists of Pauli rotations, mid-circuit measurements, @@ -313,13 +317,29 @@ typedef sequence_set_t GPEdgeSet; * */ class GPGraph { - /** Construct an empty dependency graph */ - GPGraph(const unsigned& n_qubits, const unsigned& n_bits); + public: + /** Construct an GPGraph from a circuit */ + GPGraph(const Circuit& circ); GPVertSet get_successors(const GPVert& vert) const; GPVertSet get_predecessors(const GPVert& vert) const; + /** + * All vertices of the DAG, topologically sorted. + * + * This method is "morally" const, but it sets the vertex indices in the DAG. + * + * @return vector of vertices in a topological (causal) order + */ + std::vector vertices_in_order() const; + + std::tuple< + std::vector>, std::vector, + boost::bimap, Expr> + get_sequence(); + + private: /** * Applies the given gate to the end of the circuit. * Clifford gates transform the tableau. @@ -329,7 +349,10 @@ class GPGraph { */ void apply_gate_at_end(const Command& cmd); - private: + void apply_pauli_at_end( + const std::vector& paulis, const Expr& angle, + const qubit_vector_t& qbs); + void apply_node_at_end(PauliNode_ptr& node); /** @@ -339,15 +362,16 @@ class GPGraph { * without changing the structure. */ mutable GPDAG graph_; - - [[maybe_unused]] const unsigned n_qubits_; - [[maybe_unused]] const unsigned n_bits_; + const unsigned n_qubits_; + const unsigned n_bits_; /** The tableau of the Clifford effect of the circuit */ UnitaryRevTableau cliff_; /** The record of measurements at the very end of the circuit */ boost::bimap measures_; + Expr global_phase_; + GPVertSet start_line_; GPVertSet end_line_; }; @@ -362,27 +386,6 @@ class GPGraph { std::tuple, std::vector> gpg_from_unordered_set(const std::vector& unordered_set); -/** - * @brief Transforms a circuit of PauliExpBoxes followed by Clifford gates and - * end-of-circuit measurements into a sequence of PauliRotations, - * PauliPropagations, and an end-of-circuit measurement circuit. - * - * The first returned object is an empty circuit onto which the synthesized - * circuit will be built. The final element of the returned tuple is a map from - * integers to the original UnitIDs, allowing the final circuit to use the - * orignal UnitIDs. - * - * @param circ - * @return std::tuple< - * Circuit, - * std::vector>, std::vector, - * Circuit, unit_map_t> - */ -std::tuple< - Circuit, std::vector>, - std::vector, Circuit, unit_map_t> -gpg_from_circuit(const Circuit& circ); - /** * @brief Given a circuit consists of PauliExpBoxes followed by clifford gates, * and end-of-circuit measurements, implement the PauliExpBoxes and the final diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index 4fc6fa08f9..af2ab63783 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -29,18 +29,6 @@ namespace Transforms { namespace GreedyPauliSimp { -// convert a Pauli exponential to a PauliNode_ptr -static PauliNode_ptr get_node_from_exp( - const std::vector& paulis, const Expr& theta, - const qubit_vector_t& args, unsigned n) { - // pad the Paulis - std::vector string(n, Pauli::I); - for (unsigned i = 0; i < args.size(); i++) { - string[args[i].index().at(0)] = paulis[i]; - } - return std::make_shared(string, theta); -} - // convert a Clifford tableau to a vector of PauliNode_ptr static std::vector get_nodes_from_tableau( const UnitaryRevTableau& tab, unsigned n_qubits) { @@ -96,126 +84,6 @@ gpg_from_unordered_set(const std::vector& unordered_set) { return {rotation_set, rows}; } -std::tuple< - Circuit, std::vector>, - std::vector, Circuit, unit_map_t> -gpg_from_circuit(const Circuit& circ) { - // circuit for conversion - Circuit circ_flat(circ); - unsigned n_qubits = circ_flat.n_qubits(); - unsigned n_bits = circ_flat.n_bits(); - // empty circuit - Circuit empty_circ(n_qubits, n_bits); - std::optional name = circ_flat.get_name(); - if (name != std::nullopt) { - empty_circ.set_name(name.value()); - } - empty_circ.add_phase(circ_flat.get_phase()); - // measurement circuit - Circuit measure_circ(n_qubits, n_bits); - - // flatten registers before process - unit_map_t unit_map = circ_flat.flatten_registers(); - unit_map_t rev_unit_map; - for (const auto& pair : unit_map) { - rev_unit_map.insert({pair.second, pair.first}); - } - - std::vector> rotation_sets; - std::vector commands = circ_flat.get_commands(); - Circuit cliff(n_qubits); - // extract the final clifford and the measurement circuits - for (const Command& cmd : commands) { - OpType optype = cmd.get_op_ptr()->get_type(); - switch (optype) { - case OpType::Measure: { - measure_circ.add_op(OpType::Measure, cmd.get_args()); - break; - } - default: { - if (optype == OpType::PauliExpBox || - optype == OpType::PauliExpPairBox || - optype == OpType::PauliExpCommutingSetBox) - break; - TKET_ASSERT(is_clifford_type(optype) && is_gate_type(optype)); - cliff.add_op(optype, cmd.get_args()); - } - } - } - UnitaryRevTableau tab = circuit_to_unitary_rev_tableau(cliff); - // convert the tableau into a set of nodes - std::vector rows = get_nodes_from_tableau(tab, n_qubits); - // extract the Pauli exps - for (const Command& cmd : commands) { - OpType optype = cmd.get_op_ptr()->get_type(); - switch (optype) { - case OpType::PauliExpBox: { - const PauliExpBox& pbox = - static_cast(*cmd.get_op_ptr()); - const Expr phase = pbox.get_phase(); - const std::vector paulis = pbox.get_paulis(); - auto [trivial, global_phase] = is_trivial_pauliexp(paulis, phase); - if (trivial) { - empty_circ.add_phase(global_phase); - } else { - rotation_sets.push_back( - {get_node_from_exp(paulis, phase, cmd.get_qubits(), n_qubits)}); - } - break; - } - case OpType::PauliExpPairBox: { - const PauliExpPairBox& pbox = - static_cast(*cmd.get_op_ptr()); - const auto [paulis1, paulis2] = pbox.get_paulis_pair(); - const auto [phase1, phase2] = pbox.get_phase_pair(); - auto [trivial1, global_phase1] = is_trivial_pauliexp(paulis1, phase1); - auto [trivial2, global_phase2] = is_trivial_pauliexp(paulis2, phase2); - std::vector rotation_set; - if (trivial1) { - empty_circ.add_phase(global_phase1); - } else { - rotation_set.push_back( - get_node_from_exp(paulis1, phase1, cmd.get_qubits(), n_qubits)); - } - if (trivial2) { - empty_circ.add_phase(global_phase2); - } else { - rotation_set.push_back( - get_node_from_exp(paulis2, phase2, cmd.get_qubits(), n_qubits)); - } - if (!rotation_set.empty()) { - rotation_sets.push_back(rotation_set); - } - break; - } - case OpType::PauliExpCommutingSetBox: { - const PauliExpCommutingSetBox& pbox = - static_cast(*cmd.get_op_ptr()); - const std::vector gadgets = pbox.get_pauli_gadgets(); - std::vector rotation_set; - for (const SymPauliTensor& pt : gadgets) { - const std::vector paulis = pt.string; - const Expr phase = pt.coeff; - auto [trivial, global_phase] = is_trivial_pauliexp(paulis, phase); - if (trivial) { - empty_circ.add_phase(global_phase); - } else { - rotation_set.push_back( - get_node_from_exp(paulis, phase, cmd.get_qubits(), n_qubits)); - } - } - if (rotation_set.size() > 0) { - rotation_sets.push_back(rotation_set); - } - break; - } - default: - break; - } - } - return {empty_circ, rotation_sets, rows, measure_circ, rev_unit_map}; -} - // given a stabiliser Pauli string and an angle, return a dense string and an // angle static std::pair, Expr> dense_pauli( @@ -223,14 +91,10 @@ static std::pair, Expr> dense_pauli( const Expr& angle) { bool sign = cast_coeff(pauli.coeff) == 1.; std::vector string(n_qubits, Pauli::I); - for (unsigned i = 0; i < n_qubits; i++) { - string[i] = pauli.string.at(Qubit(i)); - } - Expr new_angle = angle; - if (!sign) { - new_angle *= -1; + for (const auto& pair : pauli.string) { + string[pair.first.index().at(0)] = pair.second; } - return {string, new_angle}; + return {string, sign ? angle : -angle}; } static bool strings_commute( @@ -271,8 +135,24 @@ std::optional merge_nodes( return std::nullopt; } -GPGraph::GPGraph(const unsigned& n_qubits, const unsigned& n_bits) - : n_qubits_(n_qubits), n_bits_(n_bits), cliff_(n_qubits) {} +GPGraph::GPGraph(const Circuit& circ) + : n_qubits_(circ.n_qubits()), + n_bits_(circ.n_bits()), + global_phase_(circ.get_phase()) { + TKET_ASSERT(circ.is_simple()); + qubit_vector_t qubits = circ.all_qubits(); + bit_vector_t bits = circ.all_bits(); + for (const Qubit& q : qubits) { + TKET_ASSERT(q.index().at(0) < qubits.size()); + } + for (const Bit& b : bits) { + TKET_ASSERT(b.index().at(0) < bits.size()); + } + cliff_ = UnitaryRevTableau(n_qubits_); + for (const Command& cmd : circ.get_commands()) { + apply_gate_at_end(cmd); + } +} GPVertSet GPGraph::get_successors(const GPVert& vert) const { GPVertSet succs; @@ -357,10 +237,34 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) { if (get_predecessors(new_vert).empty()) start_line_.insert(new_vert); } +void GPGraph::apply_pauli_at_end( + const std::vector& paulis, const Expr& angle, + const qubit_vector_t& qbs) { + auto [trivial, global_phase] = is_trivial_pauliexp(paulis, angle); + if (trivial) { + global_phase_ += global_phase; + } else { + QubitPauliMap qpm; + for (unsigned i = 0; i != qbs.size(); ++i) + qpm.insert({Qubit(qbs[i]), paulis[i]}); + std::optional cliff_angle = equiv_Clifford(angle); + if (cliff_angle) { + if (cliff_angle.value() != 0) { + cliff_.apply_pauli_at_end(SpPauliStabiliser(qpm), *cliff_angle); + } + } else { + SpPauliStabiliser qpt = cliff_.get_row_product(SpPauliStabiliser(qpm)); + auto [pauli_dense, theta] = dense_pauli(qpt, n_qubits_, angle); + PauliNode_ptr node = std::make_shared(pauli_dense, theta); + apply_node_at_end(node); + } + } +} + void GPGraph::apply_gate_at_end(const Command& cmd) { const Op_ptr op = cmd.get_op_ptr(); unit_vector_t args = cmd.get_args(); - qubit_vector_t qbs = {args.begin(), args.end()}; + qubit_vector_t qbs = cmd.get_qubits(); OpType type = op->get_type(); for (const UnitID& arg : args) { @@ -526,111 +430,40 @@ void GPGraph::apply_gate_at_end(const Command& cmd) { case OpType::PhaseGadget: case OpType::ZZPhase: { Expr angle = op->get_params().at(0); - std::optional cliff_angle = equiv_Clifford(angle); - if (cliff_angle) { - if (cliff_angle.value() != 0) { - QubitPauliMap qpm; - for (const Qubit& q : qbs) qpm.insert({q, Pauli::Z}); - cliff_.apply_pauli_at_end(SpPauliStabiliser(qpm), *cliff_angle); - } - } else { - QubitPauliMap qpm; - for (const Qubit& q : qbs) qpm.insert({q, Pauli::Z}); - SpPauliStabiliser pauli = - cliff_.get_row_product(SpPauliStabiliser(qpm)); - auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, angle); - PauliNode_ptr node = - std::make_shared(pauli_dense, theta); - apply_node_at_end(node); - } + std::vector paulis(qbs.size(), Pauli::Z); + apply_pauli_at_end(paulis, angle, qbs); break; } case OpType::XXPhase: { Expr angle = op->get_params().at(0); - std::optional cliff_angle = equiv_Clifford(angle); - if (cliff_angle) { - if (cliff_angle.value() != 0) { - cliff_.apply_pauli_at_end( - SpPauliStabiliser({{qbs.at(0), Pauli::X}, {qbs.at(1), Pauli::X}}), - *cliff_angle); - } - } else { - SpPauliStabiliser pauli = cliff_.get_row_product( - SpPauliStabiliser({{qbs.at(0), Pauli::X}, {qbs.at(1), Pauli::X}})); - auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, angle); - PauliNode_ptr node = - std::make_shared(pauli_dense, theta); - apply_node_at_end(node); - } + apply_pauli_at_end({Pauli::X, Pauli::X}, angle, qbs); break; } case OpType::YYPhase: { Expr angle = op->get_params().at(0); - std::optional cliff_angle = equiv_Clifford(angle); - if (cliff_angle) { - if (cliff_angle.value() != 0) { - cliff_.apply_pauli_at_end( - SpPauliStabiliser({{qbs.at(0), Pauli::Y}, {qbs.at(1), Pauli::Y}}), - *cliff_angle); - } - } else { - SpPauliStabiliser pauli = cliff_.get_row_product( - SpPauliStabiliser({{qbs.at(0), Pauli::Y}, {qbs.at(1), Pauli::Y}})); - auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, angle); - PauliNode_ptr node = - std::make_shared(pauli_dense, theta); - apply_node_at_end(node); - } + apply_pauli_at_end({Pauli::Y, Pauli::Y}, angle, qbs); break; } case OpType::PauliExpBox: { const PauliExpBox& peb = static_cast(*op); - std::vector paulis = peb.get_paulis(); - Expr phase = peb.get_phase(); - QubitPauliMap qpm; - for (unsigned i = 0; i != args.size(); ++i) - qpm.insert({Qubit(args[i]), paulis[i]}); - SpPauliStabiliser qpt = cliff_.get_row_product(SpPauliStabiliser(qpm)); - auto [pauli_dense, theta] = dense_pauli(qpt, n_qubits_, phase); - PauliNode_ptr node = std::make_shared(pauli_dense, theta); - apply_node_at_end(node); + apply_pauli_at_end(peb.get_paulis(), peb.get_phase(), qbs); + break; } case OpType::PauliExpPairBox: { const PauliExpPairBox& peb = static_cast(*op); auto [paulis1, paulis2] = peb.get_paulis_pair(); auto [phase1, phase2] = peb.get_phase_pair(); - QubitPauliMap qpm1, qpm2; - for (unsigned i = 0; i != args.size(); ++i) { - qpm1.insert({Qubit(args[i]), paulis1[i]}); - qpm2.insert({Qubit(args[i]), paulis2[i]}); - } - SpPauliStabiliser qpt1 = cliff_.get_row_product(SpPauliStabiliser(qpm1)); - SpPauliStabiliser qpt2 = cliff_.get_row_product(SpPauliStabiliser(qpm2)); - auto [pauli_dense1, theta1] = dense_pauli(qpt1, n_qubits_, phase1); - PauliNode_ptr node1 = - std::make_shared(pauli_dense1, theta1); - apply_node_at_end(node1); - auto [pauli_dense2, theta2] = dense_pauli(qpt2, n_qubits_, phase2); - PauliNode_ptr node2 = - std::make_shared(pauli_dense2, theta2); - apply_node_at_end(node2); + apply_pauli_at_end(paulis1, phase1, qbs); + apply_pauli_at_end(paulis2, phase2, qbs); + break; } case OpType::PauliExpCommutingSetBox: { const PauliExpCommutingSetBox& peb = static_cast(*op); for (const SymPauliTensor& pt : peb.get_pauli_gadgets()) { - const std::vector paulis = pt.string; - const Expr phase = pt.coeff; - QubitPauliMap qpm; - for (unsigned i = 0; i != args.size(); ++i) { - qpm.insert({Qubit(args[i]), paulis[i]}); - } - SpPauliStabiliser qpt = cliff_.get_row_product(SpPauliStabiliser(qpm)); - auto [pauli_dense, theta] = dense_pauli(qpt, n_qubits_, phase); - PauliNode_ptr node = - std::make_shared(pauli_dense, theta); - apply_node_at_end(node); + apply_pauli_at_end(pt.string, pt.coeff, qbs); } + break; } default: { throw BadOpType("Cannot add gate to GPGraph", type); @@ -638,6 +471,49 @@ void GPGraph::apply_gate_at_end(const Command& cmd) { } } +std::vector GPGraph::vertices_in_order() const { + GPVIndex index = boost::get(boost::vertex_index, graph_); + int i = 0; + BGL_FORALL_VERTICES(v, graph_, GPDAG) { boost::put(index, v, i++); } + std::vector vertices; + boost::topological_sort(graph_, std::back_inserter(vertices)); + std::reverse(vertices.begin(), vertices.end()); + return vertices; +} + +std::tuple< + std::vector>, std::vector, + boost::bimap, Expr> +GPGraph::get_sequence() { + std::vector vertices = vertices_in_order(); + auto it = vertices.begin(); + std::vector> interior_nodes; + while (it != vertices.end()) { + const PauliNode_ptr& node = graph_[*it]; + std::vector commuting_set; + commuting_set.push_back(node); + ++it; + while (it != vertices.end()) { + const PauliNode_ptr& u = graph_[*it]; + bool commutes_with_all = true; + for (const PauliNode_ptr& v : commuting_set) { + if (!nodes_commute(u, v)) { + commutes_with_all = false; + break; + } + } + if (!commutes_with_all) break; + commuting_set.push_back(u); + ++it; + } + interior_nodes.push_back(commuting_set); + } + // add clifford + std::vector cliff_nodes = + get_nodes_from_tableau(cliff_, n_qubits_); + return {interior_nodes, cliff_nodes, measures_, global_phase_}; +} + } // namespace GreedyPauliSimp } // namespace Transforms diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 5e1f959c59..b80f8ff203 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -441,27 +441,42 @@ Circuit greedy_pauli_set_synthesis( Circuit greedy_pauli_graph_synthesis( const Circuit& circ, double discount_rate, double depth_weight) { - // c is the circuit we are trying to build - auto [c, rotation_sets, rows, measure_circ, rev_unit_map] = - gpg_from_circuit(circ); - DepthTracker depth_tracker(c.n_qubits()); + Circuit circ_flat(circ); + unsigned n_qubits = circ_flat.n_qubits(); + unsigned n_bits = circ_flat.n_bits(); + // empty circuit + Circuit new_circ(n_qubits, n_bits); + std::optional name = circ_flat.get_name(); + if (name != std::nullopt) { + new_circ.set_name(name.value()); + } + unit_map_t unit_map = circ_flat.flatten_registers(); + unit_map_t rev_unit_map; + for (const auto& pair : unit_map) { + rev_unit_map.insert({pair.second, pair.first}); + } + GPGraph gpg(circ_flat); + auto [rotation_sets, rows, measures, global_phase] = gpg.get_sequence(); + new_circ.add_phase(global_phase); + DepthTracker depth_tracker(n_qubits); // synthesise Pauli exps pauli_exps_synthesis( - rotation_sets, rows, c, discount_rate, depth_weight, depth_tracker); + rotation_sets, rows, new_circ, discount_rate, depth_weight, + depth_tracker); // synthesise the tableau - tableau_row_nodes_synthesis(rows, c, depth_weight, depth_tracker); - c.append(measure_circ); - c.rename_units(rev_unit_map); - c.replace_SWAPs(); - return c; + tableau_row_nodes_synthesis(rows, new_circ, depth_weight, depth_tracker); + for (auto it = measures.begin(); it != measures.end(); ++it) { + new_circ.add_measure(it->left, it->right); + } + new_circ.rename_units(rev_unit_map); + new_circ.replace_SWAPs(); + return new_circ; } } // namespace GreedyPauliSimp Transform greedy_pauli_optimisation(double discount_rate, double depth_weight) { return Transform([discount_rate, depth_weight](Circuit& circ) { - synthesise_pauli_graph(PauliSynthStrat::Sets, CXConfigType::Snake) - .apply(circ); circ = GreedyPauliSimp::greedy_pauli_graph_synthesis( circ, discount_rate, depth_weight); singleq_clifford_sweep().apply(circ); diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index 16250ca8c8..94a3b0553c 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -47,15 +47,14 @@ SCENARIO("Unsupported circuits") { circ.add_op(OpType::Reset, {0}); REQUIRE_THROWS_MATCHES( Transforms::greedy_pauli_optimisation().apply(circ), BadOpType, - MessageContains("Cannot add gate to PauliGraph")); + MessageContains("Cannot add gate to GPGraph")); } GIVEN("Circuit with conditional gates") { Circuit circ(2, 2); circ.add_conditional_gate(OpType::Rz, {0.5}, {0}, {0}, 0); REQUIRE_THROWS_MATCHES( Transforms::greedy_pauli_optimisation().apply(circ), BadOpType, - MessageContains( - "Can only make a PauliGraph from a circuit of basic gates")); + MessageContains("Cannot add gate to GPGraph")); } } SCENARIO("Clifford synthesis") { From 5aef87318835a290c38469883f706c8be81bf08e Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Thu, 3 Oct 2024 12:35:58 +0100 Subject: [PATCH 10/57] Ignore global phase --- .../GreedyPauliOptimisation.hpp | 4 +- .../Transformations/GreedyPauliConverters.cpp | 60 +++++++------------ .../GreedyPauliOptimisation.cpp | 3 +- 3 files changed, 22 insertions(+), 45 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 85b9678af0..f36b77823a 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -336,7 +336,7 @@ class GPGraph { std::tuple< std::vector>, std::vector, - boost::bimap, Expr> + boost::bimap> get_sequence(); private: @@ -370,8 +370,6 @@ class GPGraph { /** The record of measurements at the very end of the circuit */ boost::bimap measures_; - Expr global_phase_; - GPVertSet start_line_; GPVertSet end_line_; }; diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index af2ab63783..9f1c318553 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -52,24 +52,6 @@ static std::vector get_nodes_from_tableau( return rows; } -// detect trivial pauli exps, if true then return the global phase -static std::pair is_trivial_pauliexp( - const std::vector& paulis, const Expr& theta) { - if (static_cast(std::count( - paulis.begin(), paulis.end(), Pauli::I)) == paulis.size()) { - // If all identity term - return {true, -theta / 2}; - } - if (equiv_0(theta, 2)) { - if (equiv_0(theta, 4)) { - return {true, 0}; - } else { - return {true, -1}; - } - } - return {false, 0}; -} - std::tuple, std::vector> gpg_from_unordered_set(const std::vector& unordered_set) { std::vector rotation_set; @@ -136,9 +118,7 @@ std::optional merge_nodes( } GPGraph::GPGraph(const Circuit& circ) - : n_qubits_(circ.n_qubits()), - n_bits_(circ.n_bits()), - global_phase_(circ.get_phase()) { + : n_qubits_(circ.n_qubits()), n_bits_(circ.n_bits()) { TKET_ASSERT(circ.is_simple()); qubit_vector_t qubits = circ.all_qubits(); bit_vector_t bits = circ.all_bits(); @@ -240,24 +220,24 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) { void GPGraph::apply_pauli_at_end( const std::vector& paulis, const Expr& angle, const qubit_vector_t& qbs) { - auto [trivial, global_phase] = is_trivial_pauliexp(paulis, angle); - if (trivial) { - global_phase_ += global_phase; - } else { - QubitPauliMap qpm; - for (unsigned i = 0; i != qbs.size(); ++i) - qpm.insert({Qubit(qbs[i]), paulis[i]}); - std::optional cliff_angle = equiv_Clifford(angle); - if (cliff_angle) { - if (cliff_angle.value() != 0) { - cliff_.apply_pauli_at_end(SpPauliStabiliser(qpm), *cliff_angle); - } - } else { - SpPauliStabiliser qpt = cliff_.get_row_product(SpPauliStabiliser(qpm)); - auto [pauli_dense, theta] = dense_pauli(qpt, n_qubits_, angle); - PauliNode_ptr node = std::make_shared(pauli_dense, theta); - apply_node_at_end(node); + // Note that global phase is ignored + if (static_cast(std::count( + paulis.begin(), paulis.end(), Pauli::I)) == paulis.size()) { + return; + } + QubitPauliMap qpm; + for (unsigned i = 0; i != qbs.size(); ++i) + qpm.insert({Qubit(qbs[i]), paulis[i]}); + std::optional cliff_angle = equiv_Clifford(angle); + if (cliff_angle) { + if (cliff_angle.value() != 0) { + cliff_.apply_pauli_at_end(SpPauliStabiliser(qpm), *cliff_angle); } + } else { + SpPauliStabiliser qpt = cliff_.get_row_product(SpPauliStabiliser(qpm)); + auto [pauli_dense, theta] = dense_pauli(qpt, n_qubits_, angle); + PauliNode_ptr node = std::make_shared(pauli_dense, theta); + apply_node_at_end(node); } } @@ -483,7 +463,7 @@ std::vector GPGraph::vertices_in_order() const { std::tuple< std::vector>, std::vector, - boost::bimap, Expr> + boost::bimap> GPGraph::get_sequence() { std::vector vertices = vertices_in_order(); auto it = vertices.begin(); @@ -511,7 +491,7 @@ GPGraph::get_sequence() { // add clifford std::vector cliff_nodes = get_nodes_from_tableau(cliff_, n_qubits_); - return {interior_nodes, cliff_nodes, measures_, global_phase_}; + return {interior_nodes, cliff_nodes, measures_}; } } // namespace GreedyPauliSimp diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index b80f8ff203..af4d48c24f 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -456,8 +456,7 @@ Circuit greedy_pauli_graph_synthesis( rev_unit_map.insert({pair.second, pair.first}); } GPGraph gpg(circ_flat); - auto [rotation_sets, rows, measures, global_phase] = gpg.get_sequence(); - new_circ.add_phase(global_phase); + auto [rotation_sets, rows, measures] = gpg.get_sequence(); DepthTracker depth_tracker(n_qubits); // synthesise Pauli exps pauli_exps_synthesis( From cd4411f466822223838e7a7c4a60b6ff8790ffbb Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Fri, 4 Oct 2024 09:43:14 +0100 Subject: [PATCH 11/57] Add supports for conditional gates --- .../GreedyPauliOptimisation.hpp | 39 ++- .../Transformations/GreedyPauliConverters.cpp | 268 +++++++++--------- tket/src/Transformations/GreedyPauliOps.cpp | 8 + .../GreedyPauliOptimisation.cpp | 67 +++-- tket/test/src/test_GreedyPauli.cpp | 25 +- 5 files changed, 229 insertions(+), 178 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index f36b77823a..3cdcc3a783 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -52,6 +52,8 @@ enum class PauliNodeType { // Defines how a Pauli X and a Pauli Z on the same qubit // get propagated from right to left through a Clifford operator. Propagation, + // Pauli rotation with classical control + ConditionalRotation, }; /** @@ -247,12 +249,38 @@ class PauliRotation : public SingleNode { PauliNodeType get_type() const { return PauliNodeType::Rotation; }; - Expr theta() const { return theta_; }; + Expr angle() const { return sign_ ? theta_ : -theta_; }; - private: + protected: Expr theta_; }; +/** + * @brief A Pauli exponential defined by a padded Pauli string + * and a rotation angle + */ +class ConditionalPauliRotation : public PauliRotation { + public: + /** + * @brief Construct a new PauliRotation object. + * + * @param string the Pauli string + * @param theta the rotation angle in half-turns + */ + ConditionalPauliRotation( + std::vector string, Expr theta, std::vector cond_bits, + unsigned cond_value); + + PauliNodeType get_type() const { return PauliNodeType::ConditionalRotation; }; + + std::vector cond_bits() const { return cond_bits_; }; + unsigned cond_value() const { return cond_value_; }; + + protected: + std::vector cond_bits_; + unsigned cond_value_; +}; + /** * @brief Defines how a Pauli X and a Pauli Z on the same qubit * get propagated from right to left through a Clifford operator. @@ -347,11 +375,14 @@ class GPGraph { * into PauliNodes by the tableau and added * to the graph. */ - void apply_gate_at_end(const Command& cmd); + void apply_gate_at_end( + const Command& cmd, bool conditional = false, + std::vector cond_bits = {}, unsigned cond_value = 0); void apply_pauli_at_end( const std::vector& paulis, const Expr& angle, - const qubit_vector_t& qbs); + const qubit_vector_t& qbs, bool conditional = false, + std::vector cond_bits = {}, unsigned cond_value = 0); void apply_node_at_end(PauliNode_ptr& node); diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index 9f1c318553..db7af7122f 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -91,15 +91,30 @@ static bool strings_commute( return (n_conflicts % 2) == 0; } +static std::vector get_single_node_string(const PauliNode_ptr& n) { + if (n->get_type() == PauliNodeType::Rotation) { + return dynamic_cast(*n).string(); + } else if (n->get_type() == PauliNodeType::ConditionalRotation) { + return dynamic_cast(*n).string(); + } + TKET_ASSERT(false); +} + static bool nodes_commute(const PauliNode_ptr& n1, const PauliNode_ptr& n2) { - if (n1->get_type() == PauliNodeType::Rotation && - n2->get_type() == PauliNodeType::Rotation) { - const PauliRotation& rot1 = dynamic_cast(*n1); - const PauliRotation& rot2 = dynamic_cast(*n2); - return strings_commute(rot1.string(), rot2.string()); - } else { - TKET_ASSERT(false); + if (n1->get_type() == PauliNodeType::Rotation) { + if (n2->get_type() == PauliNodeType::Rotation || + n2->get_type() == PauliNodeType::ConditionalRotation) { + return strings_commute( + get_single_node_string(n1), get_single_node_string(n2)); + } + } else if (n1->get_type() == PauliNodeType::ConditionalRotation) { + if (n2->get_type() == PauliNodeType::Rotation || + n2->get_type() == PauliNodeType::ConditionalRotation) { + return strings_commute( + get_single_node_string(n1), get_single_node_string(n2)); + } } + TKET_ASSERT(false); } std::optional merge_nodes( @@ -109,9 +124,8 @@ std::optional merge_nodes( const PauliRotation& rot1 = dynamic_cast(*n1); const PauliRotation& rot2 = dynamic_cast(*n2); if (rot1.string() == rot2.string()) { - Expr angle1 = rot1.sign() ? rot1.theta() : -rot1.theta(); - Expr angle2 = rot2.sign() ? rot2.theta() : -rot2.theta(); - return std::make_shared(rot1.string(), angle1 + angle2); + return std::make_shared( + rot1.string(), rot1.angle() + rot2.angle()); } } return std::nullopt; @@ -173,6 +187,7 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) { // Check if we can commute past it PauliNode_ptr compare_node = graph_[to_compare]; if (nodes_commute(node, compare_node)) { + // Check if two pauli rotations can be merged if (node->get_type() == PauliNodeType::Rotation && compare_node->get_type() == PauliNodeType::Rotation) { const PauliRotation& rot1 = dynamic_cast(*node); @@ -181,8 +196,7 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) { if (rot1.string() == rot2.string()) { boost::clear_vertex(new_vert, graph_); boost::remove_vertex(new_vert, graph_); - Expr merged_angle = (rot1.sign() ? rot1.theta() : -rot1.theta()) + - (rot2.sign() ? rot2.theta() : -rot2.theta()); + Expr merged_angle = rot1.angle() + rot2.angle(); std::optional cl_ang = equiv_Clifford(merged_angle); if (cl_ang) { cliff_.apply_pauli_at_front( @@ -219,29 +233,39 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) { void GPGraph::apply_pauli_at_end( const std::vector& paulis, const Expr& angle, - const qubit_vector_t& qbs) { + const qubit_vector_t& qbs, bool conditional, + std::vector cond_bits, unsigned cond_value) { // Note that global phase is ignored if (static_cast(std::count( paulis.begin(), paulis.end(), Pauli::I)) == paulis.size()) { return; } + std::optional cliff_angle = equiv_Clifford(angle); + if (cliff_angle && cliff_angle.value() == 0) { + return; + } QubitPauliMap qpm; for (unsigned i = 0; i != qbs.size(); ++i) qpm.insert({Qubit(qbs[i]), paulis[i]}); - std::optional cliff_angle = equiv_Clifford(angle); - if (cliff_angle) { - if (cliff_angle.value() != 0) { - cliff_.apply_pauli_at_end(SpPauliStabiliser(qpm), *cliff_angle); - } + if (cliff_angle && !conditional) { + cliff_.apply_pauli_at_end(SpPauliStabiliser(qpm), *cliff_angle); + return; + } + SpPauliStabiliser qpt = cliff_.get_row_product(SpPauliStabiliser(qpm)); + auto [pauli_dense, theta] = dense_pauli(qpt, n_qubits_, angle); + PauliNode_ptr node; + if (conditional) { + node = std::make_shared( + pauli_dense, theta, cond_bits, cond_value); } else { - SpPauliStabiliser qpt = cliff_.get_row_product(SpPauliStabiliser(qpm)); - auto [pauli_dense, theta] = dense_pauli(qpt, n_qubits_, angle); - PauliNode_ptr node = std::make_shared(pauli_dense, theta); - apply_node_at_end(node); + node = std::make_shared(pauli_dense, theta); } + apply_node_at_end(node); } -void GPGraph::apply_gate_at_end(const Command& cmd) { +void GPGraph::apply_gate_at_end( + const Command& cmd, bool conditional, std::vector cond_bits, + unsigned cond_value) { const Op_ptr op = cmd.get_op_ptr(); unit_vector_t args = cmd.get_args(); qubit_vector_t qbs = cmd.get_qubits(); @@ -264,184 +288,146 @@ void GPGraph::apply_gate_at_end(const Command& cmd) { } } + std::vector, Expr>> pauli_rots; switch (type) { + case OpType::Conditional: { + const Conditional& cond = static_cast(*op); + std::vector cond_bits; + unit_vector_t inner_args; + for (unsigned i = 0; i < cond.get_width(); ++i) + cond_bits.push_back(Bit(args.at(i)).index().at(0)); + for (unsigned i = cond.get_width(); i < args.size(); ++i) + inner_args.push_back(args.at(i)); + apply_gate_at_end( + Command(cond.get_op(), inner_args), true, cond_bits, + cond.get_value()); + return; + } case OpType::Measure: { measures_.insert({args.at(0).index().at(0), args.at(1).index().at(0)}); + return; + } + case OpType::Z: { + pauli_rots.push_back({{Pauli::Z}, 1}); + break; + } + case OpType::X: { + pauli_rots.push_back({{Pauli::X}, 1}); + break; + } + case OpType::Y: { + pauli_rots.push_back({{Pauli::Y}, 1}); + break; + } + case OpType::S: { + pauli_rots.push_back({{Pauli::Z}, 0.5}); + break; + } + case OpType::V: { + pauli_rots.push_back({{Pauli::X}, 0.5}); + break; + } + case OpType::Sdg: { + pauli_rots.push_back({{Pauli::Z}, 1.5}); + break; + } + case OpType::Vdg: { + pauli_rots.push_back({{Pauli::X}, 1.5}); + break; + } + case OpType::H: { + pauli_rots.push_back({{Pauli::Y}, 0.5}); + pauli_rots.push_back({{Pauli::X}, 1}); break; } - case OpType::Z: - case OpType::X: - case OpType::Y: - case OpType::S: - case OpType::Sdg: - case OpType::V: - case OpType::Vdg: - case OpType::H: case OpType::CX: case OpType::CY: - case OpType::CZ: - case OpType::SWAP: + case OpType::CZ: { + Pauli t = (type == OpType::CZ) ? Pauli::Z + : (type == OpType::CX) ? Pauli::X + : Pauli::Y; + pauli_rots.push_back({{Pauli::Z, Pauli::I}, 1.5}); + pauli_rots.push_back({{Pauli::I, t}, 1.5}); + pauli_rots.push_back({{Pauli::Z, t}, 0.5}); + break; + } + case OpType::SWAP: { + pauli_rots.push_back({{Pauli::Z, Pauli::Z}, 0.5}); + pauli_rots.push_back({{Pauli::X, Pauli::X}, 0.5}); + pauli_rots.push_back({{Pauli::Y, Pauli::Y}, 0.5}); + break; + } case OpType::noop: case OpType::Phase: { - cliff_.apply_gate_at_end(type, qbs); break; } case OpType::Rz: { - SpPauliStabiliser pauli = cliff_.get_zrow(qbs.at(0)); - Expr angle = op->get_params().at(0); - std::optional cliff_angle = equiv_Clifford(angle); - if (cliff_angle) { - for (unsigned i = 0; i < cliff_angle.value(); i++) { - cliff_.apply_gate_at_end(OpType::S, qbs); - } - } else { - auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, angle); - PauliNode_ptr node = - std::make_shared(pauli_dense, theta); - apply_node_at_end(node); - } + pauli_rots.push_back({{Pauli::Z}, op->get_params().at(0)}); break; } case OpType::Rx: { - SpPauliStabiliser pauli = cliff_.get_xrow(qbs.at(0)); - Expr angle = op->get_params().at(0); - std::optional cliff_angle = equiv_Clifford(angle); - if (cliff_angle) { - for (unsigned i = 0; i < cliff_angle.value(); i++) { - cliff_.apply_gate_at_end(OpType::V, qbs); - } - } else { - auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, angle); - PauliNode_ptr node = - std::make_shared(pauli_dense, theta); - apply_node_at_end(node); - } + pauli_rots.push_back({{Pauli::X}, op->get_params().at(0)}); break; } case OpType::Ry: { - Expr angle = op->get_params().at(0); - std::optional cliff_angle = equiv_Clifford(angle); - if (cliff_angle) { - if (cliff_angle.value() != 0) { - cliff_.apply_gate_at_end(OpType::V, qbs); - for (unsigned i = 0; i < cliff_angle.value(); i++) { - cliff_.apply_gate_at_end(OpType::S, qbs); - } - cliff_.apply_gate_at_end(OpType::Vdg, qbs); - } - } else { - SpPauliStabiliser ypauli = - cliff_.get_row_product(SpPauliStabiliser(qbs.at(0), Pauli::Y)); - auto [pauli_dense, theta] = dense_pauli(ypauli, n_qubits_, angle); - PauliNode_ptr node = - std::make_shared(pauli_dense, theta); - apply_node_at_end(node); - } + pauli_rots.push_back({{Pauli::Y}, op->get_params().at(0)}); break; } case OpType::PhasedX: { Expr alpha = op->get_params().at(0); Expr beta = op->get_params().at(1); - std::optional cliff_alpha = equiv_Clifford(alpha); - std::optional cliff_beta = equiv_Clifford(beta); - // Rz(-b) - if (cliff_beta) { - for (unsigned i = 0; i < cliff_beta.value(); i++) { - cliff_.apply_gate_at_end(OpType::Sdg, qbs); - } - } else { - SpPauliStabiliser zpauli = cliff_.get_zrow(qbs.at(0)); - auto [pauli_dense, theta] = dense_pauli(zpauli, n_qubits_, -beta); - PauliNode_ptr node = - std::make_shared(pauli_dense, theta); - apply_node_at_end(node); - } - // Rx(a) - if (cliff_alpha) { - for (unsigned i = 0; i < cliff_alpha.value(); i++) { - cliff_.apply_gate_at_end(OpType::V, qbs); - } - } else { - SpPauliStabiliser xpauli = cliff_.get_xrow(qbs.at(0)); - auto [pauli_dense, theta] = dense_pauli(xpauli, n_qubits_, alpha); - PauliNode_ptr node = - std::make_shared(pauli_dense, theta); - apply_node_at_end(node); - } - // Rz(b) - if (cliff_beta) { - for (unsigned i = 0; i < cliff_beta.value(); i++) { - cliff_.apply_gate_at_end(OpType::S, qbs); - } - } else { - SpPauliStabiliser zpauli = cliff_.get_zrow(qbs.at(0)); - auto [pauli_dense, theta] = dense_pauli(zpauli, n_qubits_, beta); - PauliNode_ptr node = - std::make_shared(pauli_dense, theta); - apply_node_at_end(node); - } + pauli_rots.push_back({{Pauli::Z}, -beta}); + pauli_rots.push_back({{Pauli::X}, alpha}); + pauli_rots.push_back({{Pauli::Z}, beta}); break; } case OpType::T: { - SpPauliStabiliser pauli = cliff_.get_zrow(qbs.at(0)); - auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, 0.25); - PauliNode_ptr node = std::make_shared(pauli_dense, theta); - apply_node_at_end(node); + pauli_rots.push_back({{Pauli::Z}, 0.25}); break; } case OpType::Tdg: { - SpPauliStabiliser pauli = cliff_.get_zrow(qbs.at(0)); - auto [pauli_dense, theta] = dense_pauli(pauli, n_qubits_, -0.25); - PauliNode_ptr node = std::make_shared(pauli_dense, theta); - apply_node_at_end(node); + pauli_rots.push_back({{Pauli::Z}, -0.25}); break; } case OpType::ZZMax: { - cliff_.apply_gate_at_end(OpType::S, {qbs.at(0)}); - cliff_.apply_gate_at_end(OpType::Z, {qbs.at(1)}); - cliff_.apply_gate_at_end(OpType::S, {qbs.at(1)}); - cliff_.apply_gate_at_end(OpType::V, {qbs.at(1)}); - cliff_.apply_gate_at_end(OpType::S, {qbs.at(1)}); - cliff_.apply_gate_at_end(OpType::CX, qbs); - cliff_.apply_gate_at_end(OpType::S, {qbs.at(1)}); - cliff_.apply_gate_at_end(OpType::V, {qbs.at(1)}); + pauli_rots.push_back({{Pauli::Z, Pauli::Z}, 0.5}); break; } case OpType::PhaseGadget: case OpType::ZZPhase: { Expr angle = op->get_params().at(0); std::vector paulis(qbs.size(), Pauli::Z); - apply_pauli_at_end(paulis, angle, qbs); + pauli_rots.push_back({paulis, angle}); break; } case OpType::XXPhase: { Expr angle = op->get_params().at(0); - apply_pauli_at_end({Pauli::X, Pauli::X}, angle, qbs); + pauli_rots.push_back({{Pauli::X, Pauli::X}, angle}); break; } case OpType::YYPhase: { Expr angle = op->get_params().at(0); - apply_pauli_at_end({Pauli::Y, Pauli::Y}, angle, qbs); + pauli_rots.push_back({{Pauli::Y, Pauli::Y}, angle}); break; } case OpType::PauliExpBox: { const PauliExpBox& peb = static_cast(*op); - apply_pauli_at_end(peb.get_paulis(), peb.get_phase(), qbs); + pauli_rots.push_back({peb.get_paulis(), peb.get_phase()}); break; } case OpType::PauliExpPairBox: { const PauliExpPairBox& peb = static_cast(*op); auto [paulis1, paulis2] = peb.get_paulis_pair(); auto [phase1, phase2] = peb.get_phase_pair(); - apply_pauli_at_end(paulis1, phase1, qbs); - apply_pauli_at_end(paulis2, phase2, qbs); + pauli_rots.push_back({paulis1, phase1}); + pauli_rots.push_back({paulis2, phase2}); break; } case OpType::PauliExpCommutingSetBox: { const PauliExpCommutingSetBox& peb = static_cast(*op); for (const SymPauliTensor& pt : peb.get_pauli_gadgets()) { - apply_pauli_at_end(pt.string, pt.coeff, qbs); + pauli_rots.push_back({pt.string, pt.coeff}); } break; } @@ -449,6 +435,10 @@ void GPGraph::apply_gate_at_end(const Command& cmd) { throw BadOpType("Cannot add gate to GPGraph", type); } } + for (auto it = pauli_rots.begin(); it != pauli_rots.end(); ++it) { + apply_pauli_at_end( + it->first, it->second, qbs, conditional, cond_bits, cond_value); + } } std::vector GPGraph::vertices_in_order() const { diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index 89cb000163..81ae152343 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -298,6 +298,14 @@ std::tuple ACPairNode::first_support() const { PauliRotation::PauliRotation(std::vector string, Expr theta) : SingleNode(string, true), theta_(theta) {} +// ConditionalPauliRotation +ConditionalPauliRotation::ConditionalPauliRotation( + std::vector string, Expr theta, std::vector cond_bits, + unsigned cond_value) + : PauliRotation(string, theta), + cond_bits_(cond_bits), + cond_value_(cond_value) {} + // PauliPropagation PauliPropagation::PauliPropagation( std::vector z_propagation, std::vector x_propagation, diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index af4d48c24f..037e4b38d1 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -325,35 +325,50 @@ static void consume_available_rotations( while (true) { std::vector& first_set = rotation_sets[0]; for (unsigned i = first_set.size(); i-- > 0;) { - TKET_ASSERT(first_set[i]->get_type() == PauliNodeType::Rotation); - PauliRotation& node = dynamic_cast(*first_set[i]); - if (node.tqe_cost() > 0) continue; - auto [q_index, supp] = node.first_support(); - depth_tracker.add_1q_gate(q_index); - OpType rot_type; - switch (supp) { - case Pauli::Y: { - rot_type = OpType::Ry; - break; + PauliNode_ptr& node_ptr = first_set[i]; + TKET_ASSERT( + node_ptr->get_type() == PauliNodeType::Rotation || + node_ptr->get_type() == PauliNodeType::ConditionalRotation); + if (node_ptr->get_type() == PauliNodeType::ConditionalRotation) { + // TODO: always consume conditionals + ConditionalPauliRotation& node = + dynamic_cast(*node_ptr); + Op_ptr cond = std::make_shared( + std::make_shared( + SymPauliTensor(node.string(), node.angle())), + (unsigned)node.cond_bits().size(), node.cond_value()); + std::vector args = node.cond_bits(); + for (unsigned i = 0; i < node.string().size(); i++) { + args.push_back(i); } - case Pauli::Z: { - rot_type = OpType::Rz; - break; - } - case Pauli::X: { - rot_type = OpType::Rx; - break; - } - default: - // support can't be Pauli::I - TKET_ASSERT(false); - } - if (node.sign()) { - circ.add_op(rot_type, node.theta(), {q_index}); + circ.add_op(cond, args); + first_set.erase(first_set.begin() + i); } else { - circ.add_op(rot_type, -node.theta(), {q_index}); + PauliRotation& node = dynamic_cast(*node_ptr); + if (node.tqe_cost() > 0) continue; + auto [q_index, supp] = node.first_support(); + depth_tracker.add_1q_gate(q_index); + OpType rot_type; + switch (supp) { + case Pauli::Y: { + rot_type = OpType::Ry; + break; + } + case Pauli::Z: { + rot_type = OpType::Rz; + break; + } + case Pauli::X: { + rot_type = OpType::Rx; + break; + } + default: + // support can't be Pauli::I + TKET_ASSERT(false); + } + circ.add_op(rot_type, node.angle(), {q_index}); + first_set.erase(first_set.begin() + i); } - first_set.erase(first_set.begin() + i); } if (first_set.empty()) { rotation_sets.erase(rotation_sets.begin()); diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index 94a3b0553c..66babfa7fe 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -49,13 +49,6 @@ SCENARIO("Unsupported circuits") { Transforms::greedy_pauli_optimisation().apply(circ), BadOpType, MessageContains("Cannot add gate to GPGraph")); } - GIVEN("Circuit with conditional gates") { - Circuit circ(2, 2); - circ.add_conditional_gate(OpType::Rz, {0.5}, {0}, {0}, 0); - REQUIRE_THROWS_MATCHES( - Transforms::greedy_pauli_optimisation().apply(circ), BadOpType, - MessageContains("Cannot add gate to GPGraph")); - } } SCENARIO("Clifford synthesis") { GIVEN("Empty circuit") { @@ -73,9 +66,9 @@ SCENARIO("Clifford synthesis") { } GIVEN("2Q Simple Clifford") { Circuit circ(2); - circ.add_op(OpType::Y, {0}); + // circ.add_op(OpType::Y, {0}); circ.add_op(OpType::Vdg, {1}); - circ.add_op(OpType::CX, {0, 1}); + // circ.add_op(OpType::CX, {0, 1}); Circuit d(circ); REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); REQUIRE(test_unitary_comparison(circ, d, true)); @@ -275,6 +268,20 @@ SCENARIO("Complete synthesis") { REQUIRE(Transforms::greedy_pauli_optimisation().apply(g)); REQUIRE(d == g); } + GIVEN("Circuit with conditional gates") { + Circuit circ(2, 2); + circ.add_op(OpType::CX, {0, 1}); + circ.add_conditional_gate(OpType::Rz, {0.5}, {0}, {0}, 0); + circ.add_op(OpType::CX, {0, 1}); + Circuit d(2, 2); + Op_ptr cond = std::make_shared( + std::make_shared( + SymPauliTensor({Pauli::Z, Pauli::I}, 0.5)), + 1, 0); + d.add_op(cond, {0, 0, 1}); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + REQUIRE(circ == d); + } } SCENARIO("Test GreedyPauliSimp pass construction") { // test pass construction From 388873d88cdfc636ec7506039cac672fe24756f2 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Fri, 4 Oct 2024 16:28:33 +0100 Subject: [PATCH 12/57] Add support for classical ops --- .../GreedyPauliOptimisation.hpp | 53 +++++++++++- .../Transformations/GreedyPauliConverters.cpp | 59 ++++++------- tket/src/Transformations/GreedyPauliOps.cpp | 36 ++++++++ .../GreedyPauliOptimisation.cpp | 86 +++++++++++-------- tket/test/src/test_GreedyPauli.cpp | 28 ++++++ 5 files changed, 187 insertions(+), 75 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 3cdcc3a783..fa86ba10a3 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -54,6 +54,8 @@ enum class PauliNodeType { Propagation, // Pauli rotation with classical control ConditionalRotation, + // Classical operations, + Classical, }; /** @@ -70,12 +72,23 @@ enum class CommuteType : unsigned { C, }; +enum class BitType : unsigned { + READ, + WRITE, +}; + /** * @brief Type for 2-qubit entangled Clifford gates * */ using TQE = std::tuple; +struct CommuteInfo { + std::vector> paulis; + // We use UnitID to differentiate between Bit and WasmState + std::vector> bits_info; +}; + class PauliNode { public: virtual PauliNodeType get_type() const = 0; @@ -84,6 +97,7 @@ class PauliNode { virtual void update(const TQE& tqe) = 0; virtual void update(const OpType& sq_cliff, const unsigned& a); virtual void swap(const unsigned& a, const unsigned& b); + virtual CommuteInfo get_commute_info() const = 0; virtual std::vector reduction_tqes() const = 0; virtual ~PauliNode(); }; @@ -233,6 +247,29 @@ class ACPairNode : public PauliNode { unsigned n_anti_commute_entries_; }; +/** + * @brief Contains a Op on classical wires + */ +class ClassicalNode : public PauliNode { + public: + ClassicalNode(std::vector args, Op_ptr op); + + PauliNodeType get_type() const override { return PauliNodeType::Classical; }; + + unsigned tqe_cost() const override { return 0; }; + int tqe_cost_increase(const TQE& /*tqe*/) const override { return 0; }; + void update(const TQE& /*tqe*/) override { return; }; + std::vector reduction_tqes() const override { return {}; }; + std::vector args() const { return args_; }; + Op_ptr op() const { return op_; }; + + CommuteInfo get_commute_info() const override; + + protected: + std::vector args_; + Op_ptr op_; +}; + /** * @brief A Pauli exponential defined by a padded Pauli string * and a rotation angle @@ -247,10 +284,12 @@ class PauliRotation : public SingleNode { */ PauliRotation(std::vector string, Expr theta); - PauliNodeType get_type() const { return PauliNodeType::Rotation; }; + PauliNodeType get_type() const override { return PauliNodeType::Rotation; }; Expr angle() const { return sign_ ? theta_ : -theta_; }; + CommuteInfo get_commute_info() const override; + protected: Expr theta_; }; @@ -271,7 +310,11 @@ class ConditionalPauliRotation : public PauliRotation { std::vector string, Expr theta, std::vector cond_bits, unsigned cond_value); - PauliNodeType get_type() const { return PauliNodeType::ConditionalRotation; }; + PauliNodeType get_type() const override { + return PauliNodeType::ConditionalRotation; + }; + + CommuteInfo get_commute_info() const override; std::vector cond_bits() const { return cond_bits_; }; unsigned cond_value() const { return cond_value_; }; @@ -303,7 +346,11 @@ class PauliPropagation : public ACPairNode { std::vector z_propagation, std::vector x_propagation, bool z_sign, bool x_sign, unsigned qubit_index); - PauliNodeType get_type() const { return PauliNodeType::Propagation; }; + PauliNodeType get_type() const override { + return PauliNodeType::Propagation; + }; + + CommuteInfo get_commute_info() const override; unsigned qubit_index() const { return qubit_index_; }; diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index db7af7122f..ad65d6b572 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -91,44 +91,28 @@ static bool strings_commute( return (n_conflicts % 2) == 0; } -static std::vector get_single_node_string(const PauliNode_ptr& n) { - if (n->get_type() == PauliNodeType::Rotation) { - return dynamic_cast(*n).string(); - } else if (n->get_type() == PauliNodeType::ConditionalRotation) { - return dynamic_cast(*n).string(); - } - TKET_ASSERT(false); -} - static bool nodes_commute(const PauliNode_ptr& n1, const PauliNode_ptr& n2) { - if (n1->get_type() == PauliNodeType::Rotation) { - if (n2->get_type() == PauliNodeType::Rotation || - n2->get_type() == PauliNodeType::ConditionalRotation) { - return strings_commute( - get_single_node_string(n1), get_single_node_string(n2)); - } - } else if (n1->get_type() == PauliNodeType::ConditionalRotation) { - if (n2->get_type() == PauliNodeType::Rotation || - n2->get_type() == PauliNodeType::ConditionalRotation) { - return strings_commute( - get_single_node_string(n1), get_single_node_string(n2)); + CommuteInfo c1 = n1->get_commute_info(); + CommuteInfo c2 = n2->get_commute_info(); + // check if every string in n1 commutes with all strings in n2 + for (const std::vector& p1 : c1.paulis) { + for (const std::vector& p2 : c2.paulis) { + if (!strings_commute(p1, p2)) return false; } } - TKET_ASSERT(false); -} - -std::optional merge_nodes( - const PauliNode_ptr& n1, const PauliNode_ptr& n2) { - if (n1->get_type() == PauliNodeType::Rotation && - n2->get_type() == PauliNodeType::Rotation) { - const PauliRotation& rot1 = dynamic_cast(*n1); - const PauliRotation& rot2 = dynamic_cast(*n2); - if (rot1.string() == rot2.string()) { - return std::make_shared( - rot1.string(), rot1.angle() + rot2.angle()); + // check if the bits commute + for (const std::pair& b1 : c1.bits_info) { + for (const std::pair& b2 : c2.bits_info) { + if (b1.first == b2.first) { + // if two nodes read the same bit it's OK + if (b1.second == BitType::READ && b2.second == BitType::READ) { + break; + } + return false; + } } } - return std::nullopt; + return true; } GPGraph::GPGraph(const Circuit& circ) @@ -359,7 +343,8 @@ void GPGraph::apply_gate_at_end( } case OpType::noop: case OpType::Phase: { - break; + // ignore global phase + return; } case OpType::Rz: { pauli_rots.push_back({{Pauli::Z}, op->get_params().at(0)}); @@ -432,6 +417,12 @@ void GPGraph::apply_gate_at_end( break; } default: { + if (qbs.empty()) { + // ops with no quantum dependencies + PauliNode_ptr node = std::make_shared(args, op); + apply_node_at_end(node); + return; + } throw BadOpType("Cannot add gate to GPGraph", type); } } diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index 81ae152343..7c70e1d8c0 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -298,6 +298,14 @@ std::tuple ACPairNode::first_support() const { PauliRotation::PauliRotation(std::vector string, Expr theta) : SingleNode(string, true), theta_(theta) {} +CommuteInfo PauliRotation::get_commute_info() const { + std::vector> paulis; + for (Pauli p : string_) { + paulis.push_back({p}); + } + return {paulis, {}}; +} + // ConditionalPauliRotation ConditionalPauliRotation::ConditionalPauliRotation( std::vector string, Expr theta, std::vector cond_bits, @@ -306,6 +314,18 @@ ConditionalPauliRotation::ConditionalPauliRotation( cond_bits_(cond_bits), cond_value_(cond_value) {} +CommuteInfo ConditionalPauliRotation::get_commute_info() const { + std::vector> paulis; + std::vector> bits_info; + for (Pauli p : string_) { + paulis.push_back({p}); + } + for (unsigned b : cond_bits_) { + bits_info.push_back({Bit(b), BitType::READ}); + } + return {paulis, bits_info}; +} + // PauliPropagation PauliPropagation::PauliPropagation( std::vector z_propagation, std::vector x_propagation, @@ -313,6 +333,22 @@ PauliPropagation::PauliPropagation( : ACPairNode(z_propagation, x_propagation, z_sign, x_sign), qubit_index_(qubit_index) {} +CommuteInfo PauliPropagation::get_commute_info() const { + return {{z_propagation_, x_propagation_}, {}}; +} + +// ClassicalNode +ClassicalNode::ClassicalNode(std::vector args, Op_ptr op) + : args_(args), op_(op) {} + +CommuteInfo ClassicalNode::get_commute_info() const { + std::vector> bits_info; + for (const UnitID& b : args_) { + bits_info.push_back({b, BitType::WRITE}); + } + return {{}, bits_info}; +} + } // namespace GreedyPauliSimp } // namespace Transforms diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 037e4b38d1..2ebada9fa6 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -326,48 +326,58 @@ static void consume_available_rotations( std::vector& first_set = rotation_sets[0]; for (unsigned i = first_set.size(); i-- > 0;) { PauliNode_ptr& node_ptr = first_set[i]; - TKET_ASSERT( - node_ptr->get_type() == PauliNodeType::Rotation || - node_ptr->get_type() == PauliNodeType::ConditionalRotation); - if (node_ptr->get_type() == PauliNodeType::ConditionalRotation) { - // TODO: always consume conditionals - ConditionalPauliRotation& node = - dynamic_cast(*node_ptr); - Op_ptr cond = std::make_shared( - std::make_shared( - SymPauliTensor(node.string(), node.angle())), - (unsigned)node.cond_bits().size(), node.cond_value()); - std::vector args = node.cond_bits(); - for (unsigned i = 0; i < node.string().size(); i++) { - args.push_back(i); + switch (node_ptr->get_type()) { + case PauliNodeType::Classical: { + ClassicalNode& node = dynamic_cast(*node_ptr); + circ.add_op(node.op(), node.args()); + first_set.erase(first_set.begin() + i); + break; } - circ.add_op(cond, args); - first_set.erase(first_set.begin() + i); - } else { - PauliRotation& node = dynamic_cast(*node_ptr); - if (node.tqe_cost() > 0) continue; - auto [q_index, supp] = node.first_support(); - depth_tracker.add_1q_gate(q_index); - OpType rot_type; - switch (supp) { - case Pauli::Y: { - rot_type = OpType::Ry; - break; + // conditionals are added as conditional PauliExpBoxes + case PauliNodeType::ConditionalRotation: { + ConditionalPauliRotation& node = + dynamic_cast(*node_ptr); + Op_ptr cond = std::make_shared( + std::make_shared( + SymPauliTensor(node.string(), node.angle())), + (unsigned)node.cond_bits().size(), node.cond_value()); + std::vector args = node.cond_bits(); + for (unsigned i = 0; i < node.string().size(); i++) { + args.push_back(i); } - case Pauli::Z: { - rot_type = OpType::Rz; - break; - } - case Pauli::X: { - rot_type = OpType::Rx; - break; + circ.add_op(cond, args); + first_set.erase(first_set.begin() + i); + break; + } + case PauliNodeType::Rotation: { + PauliRotation& node = dynamic_cast(*node_ptr); + if (node.tqe_cost() > 0) continue; + auto [q_index, supp] = node.first_support(); + depth_tracker.add_1q_gate(q_index); + OpType rot_type; + switch (supp) { + case Pauli::Y: { + rot_type = OpType::Ry; + break; + } + case Pauli::Z: { + rot_type = OpType::Rz; + break; + } + case Pauli::X: { + rot_type = OpType::Rx; + break; + } + default: + // support can't be Pauli::I + TKET_ASSERT(false); } - default: - // support can't be Pauli::I - TKET_ASSERT(false); + circ.add_op(rot_type, node.angle(), {q_index}); + first_set.erase(first_set.begin() + i); + break; } - circ.add_op(rot_type, node.angle(), {q_index}); - first_set.erase(first_set.begin() + i); + default: + TKET_ASSERT(false); } } if (first_set.empty()) { diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index 66babfa7fe..9a6aa2a2f8 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -20,6 +20,7 @@ #include "tket/Circuit/PauliExpBoxes.hpp" #include "tket/Circuit/Simulation/CircuitSimulator.hpp" #include "tket/Gate/SymTable.hpp" +#include "tket/Ops/ClassicalOps.hpp" #include "tket/PauliGraph/PauliGraph.hpp" #include "tket/Predicates/PassGenerators.hpp" #include "tket/Transformations/GreedyPauliOptimisation.hpp" @@ -282,6 +283,33 @@ SCENARIO("Complete synthesis") { REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } + GIVEN("Circuit with classical gates") { + Circuit circ(1, 4); + circ.add_op(OpType::S, {0}); + circ.add_op(OpType::V, {0}); + circ.add_op(OpType::S, {0}); + circ.add_op(ClassicalX(), {1}); + circ.add_op(ClassicalCX(), {0, 1}); + circ.add_op(AndWithOp(), {2, 3}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + REQUIRE(circ == d); + } + GIVEN("Circuit with WASMs") { + std::string wasm_file = "string/with/path/to/wasm/file"; + std::string wasm_func = "stringNameOfWASMFunc"; + std::vector uv = {2, 1}; + const std::shared_ptr wop_ptr = + std::make_shared(6, 1, uv, uv, wasm_func, wasm_file); + Circuit circ(1, 7); + circ.add_op(OpType::X, {0}); + circ.add_op( + wop_ptr, + {Bit(0), Bit(1), Bit(2), Bit(3), Bit(4), Bit(5), WasmState(0)}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + REQUIRE(circ == d); + } } SCENARIO("Test GreedyPauliSimp pass construction") { // test pass construction From ef7d0b6ac3beab9a21a3de288165a0519c91c1e6 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Fri, 4 Oct 2024 17:01:32 +0100 Subject: [PATCH 13/57] flatten_registers should only rename qubits and bits --- tket/src/Circuit/basic_circ_manip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tket/src/Circuit/basic_circ_manip.cpp b/tket/src/Circuit/basic_circ_manip.cpp index 8bae39c18b..ca3d27faa3 100644 --- a/tket/src/Circuit/basic_circ_manip.cpp +++ b/tket/src/Circuit/basic_circ_manip.cpp @@ -429,7 +429,7 @@ unit_map_t Circuit::flatten_registers() { for (const BoundaryElement& el : boundary.get()) { if (el.type() == UnitType::Qubit) { rename_map.insert({el.id_, Qubit(q_index++)}); - } else { + } else if (el.type() == UnitType::Bit) { rename_map.insert({el.id_, Bit(c_index++)}); } } From 9e9a2fd556dfdb47873be8dae01bd04bda1d772e Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Fri, 4 Oct 2024 17:03:38 +0100 Subject: [PATCH 14/57] Replace unsigned with Bit --- .../GreedyPauliOptimisation.hpp | 15 +++++++------- .../Transformations/GreedyPauliConverters.cpp | 20 +++++++++---------- tket/src/Transformations/GreedyPauliOps.cpp | 6 +++--- .../GreedyPauliOptimisation.cpp | 8 ++++---- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index fa86ba10a3..3694d366e1 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -307,7 +307,7 @@ class ConditionalPauliRotation : public PauliRotation { * @param theta the rotation angle in half-turns */ ConditionalPauliRotation( - std::vector string, Expr theta, std::vector cond_bits, + std::vector string, Expr theta, bit_vector_t cond_bits, unsigned cond_value); PauliNodeType get_type() const override { @@ -316,11 +316,11 @@ class ConditionalPauliRotation : public PauliRotation { CommuteInfo get_commute_info() const override; - std::vector cond_bits() const { return cond_bits_; }; + bit_vector_t cond_bits() const { return cond_bits_; }; unsigned cond_value() const { return cond_value_; }; protected: - std::vector cond_bits_; + bit_vector_t cond_bits_; unsigned cond_value_; }; @@ -411,7 +411,7 @@ class GPGraph { std::tuple< std::vector>, std::vector, - boost::bimap> + boost::bimap> get_sequence(); private: @@ -424,12 +424,12 @@ class GPGraph { */ void apply_gate_at_end( const Command& cmd, bool conditional = false, - std::vector cond_bits = {}, unsigned cond_value = 0); + const bit_vector_t& cond_bits = {}, unsigned cond_value = 0); void apply_pauli_at_end( const std::vector& paulis, const Expr& angle, const qubit_vector_t& qbs, bool conditional = false, - std::vector cond_bits = {}, unsigned cond_value = 0); + const bit_vector_t& cond_bits = {}, unsigned cond_value = 0); void apply_node_at_end(PauliNode_ptr& node); @@ -441,12 +441,11 @@ class GPGraph { */ mutable GPDAG graph_; const unsigned n_qubits_; - const unsigned n_bits_; /** The tableau of the Clifford effect of the circuit */ UnitaryRevTableau cliff_; /** The record of measurements at the very end of the circuit */ - boost::bimap measures_; + boost::bimap measures_; GPVertSet start_line_; GPVertSet end_line_; diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index ad65d6b572..90814e38a2 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -115,8 +115,7 @@ static bool nodes_commute(const PauliNode_ptr& n1, const PauliNode_ptr& n2) { return true; } -GPGraph::GPGraph(const Circuit& circ) - : n_qubits_(circ.n_qubits()), n_bits_(circ.n_bits()) { +GPGraph::GPGraph(const Circuit& circ) : n_qubits_(circ.n_qubits()) { TKET_ASSERT(circ.is_simple()); qubit_vector_t qubits = circ.all_qubits(); bit_vector_t bits = circ.all_bits(); @@ -217,8 +216,8 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) { void GPGraph::apply_pauli_at_end( const std::vector& paulis, const Expr& angle, - const qubit_vector_t& qbs, bool conditional, - std::vector cond_bits, unsigned cond_value) { + const qubit_vector_t& qbs, bool conditional, const bit_vector_t& cond_bits, + unsigned cond_value) { // Note that global phase is ignored if (static_cast(std::count( paulis.begin(), paulis.end(), Pauli::I)) == paulis.size()) { @@ -248,7 +247,7 @@ void GPGraph::apply_pauli_at_end( } void GPGraph::apply_gate_at_end( - const Command& cmd, bool conditional, std::vector cond_bits, + const Command& cmd, bool conditional, const bit_vector_t& cond_bits, unsigned cond_value) { const Op_ptr op = cmd.get_op_ptr(); unit_vector_t args = cmd.get_args(); @@ -263,8 +262,7 @@ void GPGraph::apply_gate_at_end( "- cannot add gate after measure on qubit " + arg.repr()); } - } else if ( - measures_.right.find(arg.index().at(0)) != measures_.right.end()) { + } else if (measures_.right.find(arg) != measures_.right.end()) { throw MidCircuitMeasurementNotAllowed( "PauliGraph does not support mid-circuit measurements - " "cannot add gate after measure to bit " + @@ -276,10 +274,10 @@ void GPGraph::apply_gate_at_end( switch (type) { case OpType::Conditional: { const Conditional& cond = static_cast(*op); - std::vector cond_bits; + bit_vector_t cond_bits; unit_vector_t inner_args; for (unsigned i = 0; i < cond.get_width(); ++i) - cond_bits.push_back(Bit(args.at(i)).index().at(0)); + cond_bits.push_back(Bit(args.at(i))); for (unsigned i = cond.get_width(); i < args.size(); ++i) inner_args.push_back(args.at(i)); apply_gate_at_end( @@ -288,7 +286,7 @@ void GPGraph::apply_gate_at_end( return; } case OpType::Measure: { - measures_.insert({args.at(0).index().at(0), args.at(1).index().at(0)}); + measures_.insert({args.at(0).index().at(0), args.at(1)}); return; } case OpType::Z: { @@ -444,7 +442,7 @@ std::vector GPGraph::vertices_in_order() const { std::tuple< std::vector>, std::vector, - boost::bimap> + boost::bimap> GPGraph::get_sequence() { std::vector vertices = vertices_in_order(); auto it = vertices.begin(); diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index 7c70e1d8c0..56d6cc1936 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -308,7 +308,7 @@ CommuteInfo PauliRotation::get_commute_info() const { // ConditionalPauliRotation ConditionalPauliRotation::ConditionalPauliRotation( - std::vector string, Expr theta, std::vector cond_bits, + std::vector string, Expr theta, bit_vector_t cond_bits, unsigned cond_value) : PauliRotation(string, theta), cond_bits_(cond_bits), @@ -320,8 +320,8 @@ CommuteInfo ConditionalPauliRotation::get_commute_info() const { for (Pauli p : string_) { paulis.push_back({p}); } - for (unsigned b : cond_bits_) { - bits_info.push_back({Bit(b), BitType::READ}); + for (Bit b : cond_bits_) { + bits_info.push_back({b, BitType::READ}); } return {paulis, bits_info}; } diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 2ebada9fa6..f60a10b9b9 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -341,11 +341,11 @@ static void consume_available_rotations( std::make_shared( SymPauliTensor(node.string(), node.angle())), (unsigned)node.cond_bits().size(), node.cond_value()); - std::vector args = node.cond_bits(); + std::vector args = node.cond_bits(); for (unsigned i = 0; i < node.string().size(); i++) { - args.push_back(i); + args.push_back(Qubit(i)); } - circ.add_op(cond, args); + circ.add_op(cond, args); first_set.erase(first_set.begin() + i); break; } @@ -490,7 +490,7 @@ Circuit greedy_pauli_graph_synthesis( // synthesise the tableau tableau_row_nodes_synthesis(rows, new_circ, depth_weight, depth_tracker); for (auto it = measures.begin(); it != measures.end(); ++it) { - new_circ.add_measure(it->left, it->right); + new_circ.add_measure(Qubit(it->left), it->right); } new_circ.rename_units(rev_unit_map); new_circ.replace_SWAPs(); From 77a152231ac19f6b187035252be63815dc228a69 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Fri, 4 Oct 2024 17:04:24 +0100 Subject: [PATCH 15/57] Revert "Replace unsigned with Bit" This reverts commit 9e9a2fd556dfdb47873be8dae01bd04bda1d772e. --- .../GreedyPauliOptimisation.hpp | 15 +++++++------- .../Transformations/GreedyPauliConverters.cpp | 20 ++++++++++--------- tket/src/Transformations/GreedyPauliOps.cpp | 6 +++--- .../GreedyPauliOptimisation.cpp | 8 ++++---- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 3694d366e1..fa86ba10a3 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -307,7 +307,7 @@ class ConditionalPauliRotation : public PauliRotation { * @param theta the rotation angle in half-turns */ ConditionalPauliRotation( - std::vector string, Expr theta, bit_vector_t cond_bits, + std::vector string, Expr theta, std::vector cond_bits, unsigned cond_value); PauliNodeType get_type() const override { @@ -316,11 +316,11 @@ class ConditionalPauliRotation : public PauliRotation { CommuteInfo get_commute_info() const override; - bit_vector_t cond_bits() const { return cond_bits_; }; + std::vector cond_bits() const { return cond_bits_; }; unsigned cond_value() const { return cond_value_; }; protected: - bit_vector_t cond_bits_; + std::vector cond_bits_; unsigned cond_value_; }; @@ -411,7 +411,7 @@ class GPGraph { std::tuple< std::vector>, std::vector, - boost::bimap> + boost::bimap> get_sequence(); private: @@ -424,12 +424,12 @@ class GPGraph { */ void apply_gate_at_end( const Command& cmd, bool conditional = false, - const bit_vector_t& cond_bits = {}, unsigned cond_value = 0); + std::vector cond_bits = {}, unsigned cond_value = 0); void apply_pauli_at_end( const std::vector& paulis, const Expr& angle, const qubit_vector_t& qbs, bool conditional = false, - const bit_vector_t& cond_bits = {}, unsigned cond_value = 0); + std::vector cond_bits = {}, unsigned cond_value = 0); void apply_node_at_end(PauliNode_ptr& node); @@ -441,11 +441,12 @@ class GPGraph { */ mutable GPDAG graph_; const unsigned n_qubits_; + const unsigned n_bits_; /** The tableau of the Clifford effect of the circuit */ UnitaryRevTableau cliff_; /** The record of measurements at the very end of the circuit */ - boost::bimap measures_; + boost::bimap measures_; GPVertSet start_line_; GPVertSet end_line_; diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index 90814e38a2..ad65d6b572 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -115,7 +115,8 @@ static bool nodes_commute(const PauliNode_ptr& n1, const PauliNode_ptr& n2) { return true; } -GPGraph::GPGraph(const Circuit& circ) : n_qubits_(circ.n_qubits()) { +GPGraph::GPGraph(const Circuit& circ) + : n_qubits_(circ.n_qubits()), n_bits_(circ.n_bits()) { TKET_ASSERT(circ.is_simple()); qubit_vector_t qubits = circ.all_qubits(); bit_vector_t bits = circ.all_bits(); @@ -216,8 +217,8 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) { void GPGraph::apply_pauli_at_end( const std::vector& paulis, const Expr& angle, - const qubit_vector_t& qbs, bool conditional, const bit_vector_t& cond_bits, - unsigned cond_value) { + const qubit_vector_t& qbs, bool conditional, + std::vector cond_bits, unsigned cond_value) { // Note that global phase is ignored if (static_cast(std::count( paulis.begin(), paulis.end(), Pauli::I)) == paulis.size()) { @@ -247,7 +248,7 @@ void GPGraph::apply_pauli_at_end( } void GPGraph::apply_gate_at_end( - const Command& cmd, bool conditional, const bit_vector_t& cond_bits, + const Command& cmd, bool conditional, std::vector cond_bits, unsigned cond_value) { const Op_ptr op = cmd.get_op_ptr(); unit_vector_t args = cmd.get_args(); @@ -262,7 +263,8 @@ void GPGraph::apply_gate_at_end( "- cannot add gate after measure on qubit " + arg.repr()); } - } else if (measures_.right.find(arg) != measures_.right.end()) { + } else if ( + measures_.right.find(arg.index().at(0)) != measures_.right.end()) { throw MidCircuitMeasurementNotAllowed( "PauliGraph does not support mid-circuit measurements - " "cannot add gate after measure to bit " + @@ -274,10 +276,10 @@ void GPGraph::apply_gate_at_end( switch (type) { case OpType::Conditional: { const Conditional& cond = static_cast(*op); - bit_vector_t cond_bits; + std::vector cond_bits; unit_vector_t inner_args; for (unsigned i = 0; i < cond.get_width(); ++i) - cond_bits.push_back(Bit(args.at(i))); + cond_bits.push_back(Bit(args.at(i)).index().at(0)); for (unsigned i = cond.get_width(); i < args.size(); ++i) inner_args.push_back(args.at(i)); apply_gate_at_end( @@ -286,7 +288,7 @@ void GPGraph::apply_gate_at_end( return; } case OpType::Measure: { - measures_.insert({args.at(0).index().at(0), args.at(1)}); + measures_.insert({args.at(0).index().at(0), args.at(1).index().at(0)}); return; } case OpType::Z: { @@ -442,7 +444,7 @@ std::vector GPGraph::vertices_in_order() const { std::tuple< std::vector>, std::vector, - boost::bimap> + boost::bimap> GPGraph::get_sequence() { std::vector vertices = vertices_in_order(); auto it = vertices.begin(); diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index 56d6cc1936..7c70e1d8c0 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -308,7 +308,7 @@ CommuteInfo PauliRotation::get_commute_info() const { // ConditionalPauliRotation ConditionalPauliRotation::ConditionalPauliRotation( - std::vector string, Expr theta, bit_vector_t cond_bits, + std::vector string, Expr theta, std::vector cond_bits, unsigned cond_value) : PauliRotation(string, theta), cond_bits_(cond_bits), @@ -320,8 +320,8 @@ CommuteInfo ConditionalPauliRotation::get_commute_info() const { for (Pauli p : string_) { paulis.push_back({p}); } - for (Bit b : cond_bits_) { - bits_info.push_back({b, BitType::READ}); + for (unsigned b : cond_bits_) { + bits_info.push_back({Bit(b), BitType::READ}); } return {paulis, bits_info}; } diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index f60a10b9b9..2ebada9fa6 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -341,11 +341,11 @@ static void consume_available_rotations( std::make_shared( SymPauliTensor(node.string(), node.angle())), (unsigned)node.cond_bits().size(), node.cond_value()); - std::vector args = node.cond_bits(); + std::vector args = node.cond_bits(); for (unsigned i = 0; i < node.string().size(); i++) { - args.push_back(Qubit(i)); + args.push_back(i); } - circ.add_op(cond, args); + circ.add_op(cond, args); first_set.erase(first_set.begin() + i); break; } @@ -490,7 +490,7 @@ Circuit greedy_pauli_graph_synthesis( // synthesise the tableau tableau_row_nodes_synthesis(rows, new_circ, depth_weight, depth_tracker); for (auto it = measures.begin(); it != measures.end(); ++it) { - new_circ.add_measure(Qubit(it->left), it->right); + new_circ.add_measure(it->left, it->right); } new_circ.rename_units(rev_unit_map); new_circ.replace_SWAPs(); From 54d3e8990f3876cda90195ec73b477f596cf1fd2 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Fri, 4 Oct 2024 17:09:51 +0100 Subject: [PATCH 16/57] Manually check if qubits and bits can be flattened since ``is_simple()`` doesn't work with wasm --- tket/src/Transformations/GreedyPauliConverters.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index ad65d6b572..01124d433f 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -117,13 +117,14 @@ static bool nodes_commute(const PauliNode_ptr& n1, const PauliNode_ptr& n2) { GPGraph::GPGraph(const Circuit& circ) : n_qubits_(circ.n_qubits()), n_bits_(circ.n_bits()) { - TKET_ASSERT(circ.is_simple()); qubit_vector_t qubits = circ.all_qubits(); bit_vector_t bits = circ.all_bits(); for (const Qubit& q : qubits) { + TKET_ASSERT(q.reg_name() == q_default_reg()); TKET_ASSERT(q.index().at(0) < qubits.size()); } for (const Bit& b : bits) { + TKET_ASSERT(b.reg_name() == c_default_reg()); TKET_ASSERT(b.index().at(0) < bits.size()); } cliff_ = UnitaryRevTableau(n_qubits_); From 83ec4ac994399003260fada6473c9a8e71b24329 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Fri, 4 Oct 2024 17:15:38 +0100 Subject: [PATCH 17/57] Consistent enum names --- .../GreedyPauliOptimisation.hpp | 20 +++++++++++-------- .../Transformations/GreedyPauliConverters.cpp | 4 ++-- tket/src/Transformations/GreedyPauliOps.cpp | 14 ++----------- .../GreedyPauliOptimisation.cpp | 6 +++--- 4 files changed, 19 insertions(+), 25 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index fa86ba10a3..1708ba9896 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -48,14 +48,14 @@ enum class TQEType : unsigned { enum class PauliNodeType { // Pauli rotation - Rotation, + PauliRotation, // Defines how a Pauli X and a Pauli Z on the same qubit // get propagated from right to left through a Clifford operator. - Propagation, + PauliPropagation, // Pauli rotation with classical control - ConditionalRotation, + ConditionalPauliRotation, // Classical operations, - Classical, + ClassicalNode, }; /** @@ -254,7 +254,9 @@ class ClassicalNode : public PauliNode { public: ClassicalNode(std::vector args, Op_ptr op); - PauliNodeType get_type() const override { return PauliNodeType::Classical; }; + PauliNodeType get_type() const override { + return PauliNodeType::ClassicalNode; + }; unsigned tqe_cost() const override { return 0; }; int tqe_cost_increase(const TQE& /*tqe*/) const override { return 0; }; @@ -284,7 +286,9 @@ class PauliRotation : public SingleNode { */ PauliRotation(std::vector string, Expr theta); - PauliNodeType get_type() const override { return PauliNodeType::Rotation; }; + PauliNodeType get_type() const override { + return PauliNodeType::PauliRotation; + }; Expr angle() const { return sign_ ? theta_ : -theta_; }; @@ -311,7 +315,7 @@ class ConditionalPauliRotation : public PauliRotation { unsigned cond_value); PauliNodeType get_type() const override { - return PauliNodeType::ConditionalRotation; + return PauliNodeType::ConditionalPauliRotation; }; CommuteInfo get_commute_info() const override; @@ -347,7 +351,7 @@ class PauliPropagation : public ACPairNode { bool z_sign, bool x_sign, unsigned qubit_index); PauliNodeType get_type() const override { - return PauliNodeType::Propagation; + return PauliNodeType::PauliPropagation; }; CommuteInfo get_commute_info() const override; diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index 01124d433f..802b6b812e 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -173,8 +173,8 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) { PauliNode_ptr compare_node = graph_[to_compare]; if (nodes_commute(node, compare_node)) { // Check if two pauli rotations can be merged - if (node->get_type() == PauliNodeType::Rotation && - compare_node->get_type() == PauliNodeType::Rotation) { + if (node->get_type() == PauliNodeType::PauliRotation && + compare_node->get_type() == PauliNodeType::PauliRotation) { const PauliRotation& rot1 = dynamic_cast(*node); const PauliRotation& rot2 = dynamic_cast(*compare_node); diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index 7c70e1d8c0..d8ef2e0a94 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -298,13 +298,7 @@ std::tuple ACPairNode::first_support() const { PauliRotation::PauliRotation(std::vector string, Expr theta) : SingleNode(string, true), theta_(theta) {} -CommuteInfo PauliRotation::get_commute_info() const { - std::vector> paulis; - for (Pauli p : string_) { - paulis.push_back({p}); - } - return {paulis, {}}; -} +CommuteInfo PauliRotation::get_commute_info() const { return {{string_}, {}}; } // ConditionalPauliRotation ConditionalPauliRotation::ConditionalPauliRotation( @@ -315,15 +309,11 @@ ConditionalPauliRotation::ConditionalPauliRotation( cond_value_(cond_value) {} CommuteInfo ConditionalPauliRotation::get_commute_info() const { - std::vector> paulis; std::vector> bits_info; - for (Pauli p : string_) { - paulis.push_back({p}); - } for (unsigned b : cond_bits_) { bits_info.push_back({Bit(b), BitType::READ}); } - return {paulis, bits_info}; + return {{string_}, bits_info}; } // PauliPropagation diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 2ebada9fa6..266f841688 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -327,14 +327,14 @@ static void consume_available_rotations( for (unsigned i = first_set.size(); i-- > 0;) { PauliNode_ptr& node_ptr = first_set[i]; switch (node_ptr->get_type()) { - case PauliNodeType::Classical: { + case PauliNodeType::ClassicalNode: { ClassicalNode& node = dynamic_cast(*node_ptr); circ.add_op(node.op(), node.args()); first_set.erase(first_set.begin() + i); break; } // conditionals are added as conditional PauliExpBoxes - case PauliNodeType::ConditionalRotation: { + case PauliNodeType::ConditionalPauliRotation: { ConditionalPauliRotation& node = dynamic_cast(*node_ptr); Op_ptr cond = std::make_shared( @@ -349,7 +349,7 @@ static void consume_available_rotations( first_set.erase(first_set.begin() + i); break; } - case PauliNodeType::Rotation: { + case PauliNodeType::PauliRotation: { PauliRotation& node = dynamic_cast(*node_ptr); if (node.tqe_cost() > 0) continue; auto [q_index, supp] = node.first_support(); From 090ab697acaa210828cd293a31257e2b333f95d1 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Fri, 4 Oct 2024 23:57:43 +0100 Subject: [PATCH 18/57] Add support for mid-circuit measurement --- .../GreedyPauliOptimisation.hpp | 24 ++++++++- .../Transformations/GreedyPauliConverters.cpp | 36 ++++++++----- tket/src/Transformations/GreedyPauliOps.cpp | 8 +++ .../GreedyPauliOptimisation.cpp | 50 ++++++++++++++++++- tket/test/src/test_GreedyPauli.cpp | 24 ++++----- 5 files changed, 115 insertions(+), 27 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 1708ba9896..d24c3f61b0 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -56,6 +56,8 @@ enum class PauliNodeType { ConditionalPauliRotation, // Classical operations, ClassicalNode, + // Mid-circuit measurement + MidMeasure, }; /** @@ -298,6 +300,26 @@ class PauliRotation : public SingleNode { Expr theta_; }; +/** + * @brief Measurement that has quantum or classical successors + */ +class MidMeasure : public SingleNode { + public: + /** + * @brief Construct a new MidMeasure object. + * + * @param string the Pauli string + */ + MidMeasure(std::vector string, unsigned bit); + + PauliNodeType get_type() const override { return PauliNodeType::MidMeasure; }; + CommuteInfo get_commute_info() const override; + unsigned bit() const { return bit_; }; + + protected: + const unsigned bit_; +}; + /** * @brief A Pauli exponential defined by a padded Pauli string * and a rotation angle @@ -450,7 +472,7 @@ class GPGraph { /** The tableau of the Clifford effect of the circuit */ UnitaryRevTableau cliff_; /** The record of measurements at the very end of the circuit */ - boost::bimap measures_; + boost::bimap end_measures_; GPVertSet start_line_; GPVertSet end_line_; diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index 802b6b812e..59227502a3 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -258,18 +258,27 @@ void GPGraph::apply_gate_at_end( for (const UnitID& arg : args) { if (arg.type() == UnitType::Qubit) { - if (measures_.left.find(arg.index().at(0)) != measures_.left.end()) { - throw MidCircuitMeasurementNotAllowed( - "PauliGraph does not support mid-circuit measurements " - "- cannot add gate after measure on qubit " + - arg.repr()); + auto it = end_measures_.left.find(arg.index().at(0)); + if (it != end_measures_.left.end()) { + // the measurement is no longer end-circuit, we remove it from + // end_measures_ and add it as a MidMeasure node instead. + std::vector paulis(n_qubits_, Pauli::I); + paulis[it->first] = Pauli::Z; + PauliNode_ptr node = std::make_shared(paulis, it->second); + apply_node_at_end(node); + end_measures_.left.erase(it); + } + } else if (arg.type() == UnitType::Bit) { + auto it = end_measures_.right.find(arg.index().at(0)); + if (it != end_measures_.right.end()) { + // the measurement is no longer end-circuit, we remove it from + // end_measures_ and add it as a MidMeasure node instead. + std::vector paulis(n_qubits_, Pauli::I); + paulis[it->second] = Pauli::Z; + PauliNode_ptr node = std::make_shared(paulis, it->first); + apply_node_at_end(node); + end_measures_.right.erase(it); } - } else if ( - measures_.right.find(arg.index().at(0)) != measures_.right.end()) { - throw MidCircuitMeasurementNotAllowed( - "PauliGraph does not support mid-circuit measurements - " - "cannot add gate after measure to bit " + - arg.repr()); } } @@ -289,7 +298,8 @@ void GPGraph::apply_gate_at_end( return; } case OpType::Measure: { - measures_.insert({args.at(0).index().at(0), args.at(1).index().at(0)}); + end_measures_.insert( + {args.at(0).index().at(0), args.at(1).index().at(0)}); return; } case OpType::Z: { @@ -473,7 +483,7 @@ GPGraph::get_sequence() { // add clifford std::vector cliff_nodes = get_nodes_from_tableau(cliff_, n_qubits_); - return {interior_nodes, cliff_nodes, measures_}; + return {interior_nodes, cliff_nodes, end_measures_}; } } // namespace GreedyPauliSimp diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index d8ef2e0a94..fb28dee984 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -339,6 +339,14 @@ CommuteInfo ClassicalNode::get_commute_info() const { return {{}, bits_info}; } +// MidMeasure +MidMeasure::MidMeasure(std::vector string, unsigned bit) + : SingleNode(string, true), bit_(bit) {} + +CommuteInfo MidMeasure::get_commute_info() const { + return {{string_}, {{Bit(bit_), BitType::WRITE}}}; +} + } // namespace GreedyPauliSimp } // namespace Transforms diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 266f841688..9c753b0c2d 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -327,6 +327,54 @@ static void consume_available_rotations( for (unsigned i = first_set.size(); i-- > 0;) { PauliNode_ptr& node_ptr = first_set[i]; switch (node_ptr->get_type()) { + case PauliNodeType::MidMeasure: { + if (node_ptr->tqe_cost() > 0) continue; + MidMeasure& node = dynamic_cast(*node_ptr); + auto [q_index, supp] = node.first_support(); + switch (supp) { + case Pauli::Z: { + if (!node.sign()) { + circ.add_op(OpType::X, {q_index}); + } + circ.add_measure(q_index, node.bit()); + if (!node.sign()) { + circ.add_op(OpType::X, {q_index}); + } + break; + } + case Pauli::X: { + circ.add_op(OpType::H, {q_index}); + if (!node.sign()) { + circ.add_op(OpType::X, {q_index}); + } + circ.add_measure(q_index, node.bit()); + if (!node.sign()) { + circ.add_op(OpType::X, {q_index}); + } + circ.add_op(OpType::H, {q_index}); + break; + } + case Pauli::Y: { + if (node.sign()) { + circ.add_op(OpType::Vdg, {q_index}); + } else { + circ.add_op(OpType::V, {q_index}); + } + circ.add_measure(q_index, node.bit()); + if (node.sign()) { + circ.add_op(OpType::V, {q_index}); + } else { + circ.add_op(OpType::Vdg, {q_index}); + } + break; + } + default: { + TKET_ASSERT(false); + } + } + first_set.erase(first_set.begin() + i); + break; + } case PauliNodeType::ClassicalNode: { ClassicalNode& node = dynamic_cast(*node_ptr); circ.add_op(node.op(), node.args()); @@ -350,8 +398,8 @@ static void consume_available_rotations( break; } case PauliNodeType::PauliRotation: { + if (node_ptr->tqe_cost() > 0) continue; PauliRotation& node = dynamic_cast(*node_ptr); - if (node.tqe_cost() > 0) continue; auto [q_index, supp] = node.first_support(); depth_tracker.add_1q_gate(q_index); OpType rot_type; diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index 9a6aa2a2f8..068e9e45ed 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -30,18 +30,6 @@ namespace tket { namespace test_GreedyPauliSimp { SCENARIO("Unsupported circuits") { - GIVEN("Circuit with mid-circ measurements") { - Circuit circ(2, 2); - circ.add_op(OpType::H, {0}); - circ.add_op(OpType::Rx, 0.5, {1}); - circ.add_op(OpType::Measure, {0, 0}); - circ.add_op(OpType::CX, {0, 1}); - REQUIRE_THROWS_MATCHES( - Transforms::greedy_pauli_optimisation().apply(circ), - MidCircuitMeasurementNotAllowed, - MessageContains( - "PauliGraph does not support mid-circuit measurements")); - } GIVEN("Circuit with resets") { Circuit circ(1); circ.add_op(OpType::H, {0}); @@ -310,6 +298,18 @@ SCENARIO("Complete synthesis") { REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } + GIVEN("Circuit with mid-circuit measurements") { + Circuit circ(2, 2); + circ.add_op(OpType::T, {0}); + circ.add_op(OpType::Measure, {0, 0}); + circ.add_op(OpType::Tdg, {0}); + circ.add_op(OpType::Measure, {1, 1}); + Circuit d(2, 2); + d.add_op(OpType::Measure, {0, 0}); + d.add_op(OpType::Measure, {1, 1}); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + REQUIRE(circ == d); + } } SCENARIO("Test GreedyPauliSimp pass construction") { // test pass construction From 6083f1df294864084f400eda90c204de20ff08ce Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Mon, 7 Oct 2024 00:39:18 +0100 Subject: [PATCH 19/57] Rename variables --- .../GreedyPauliOptimisation.hpp | 45 ++++++++---- tket/src/Transformations/GreedyPauliOps.cpp | 72 +++++++++---------- 2 files changed, 67 insertions(+), 50 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index d24c3f61b0..dbdc34a79b 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -173,14 +173,14 @@ class ACPairNode : public PauliNode { /** * @brief Construct a new ACPairNode object * - * @param z_propagation - * @param x_propagation + * @param z_string + * @param x_string * @param z_sign * @param x_sign */ ACPairNode( - std::vector z_propagation, std::vector x_propagation, - bool z_sign, bool x_sign); + std::vector z_string, std::vector x_string, bool z_sign, + bool x_sign); /** * @brief Number of TQEs required to reduce the weight to 1 @@ -234,13 +234,13 @@ class ACPairNode : public PauliNode { bool x_sign() const { return x_sign_; }; - std::vector z_propagation() const { return z_propagation_; }; + std::vector z_string() const { return z_string_; }; - std::vector x_propagation() const { return x_propagation_; }; + std::vector x_string() const { return x_string_; }; protected: - std::vector z_propagation_; - std::vector x_propagation_; + std::vector z_string_; + std::vector x_string_; bool z_sign_; bool x_sign_; // extra cached data used by greedy synthesis @@ -309,6 +309,7 @@ class MidMeasure : public SingleNode { * @brief Construct a new MidMeasure object. * * @param string the Pauli string + * @param bit bit to store the readout */ MidMeasure(std::vector string, unsigned bit); @@ -362,15 +363,15 @@ class PauliPropagation : public ACPairNode { /** * @brief Construct a new PauliPropagation object * - * @param z_propagation - * @param x_propagation + * @param z_string + * @param x_string * @param z_sign * @param x_sign * @param qubit_index */ PauliPropagation( - std::vector z_propagation, std::vector x_propagation, - bool z_sign, bool x_sign, unsigned qubit_index); + std::vector z_string, std::vector x_string, bool z_sign, + bool x_sign, unsigned qubit_index); PauliNodeType get_type() const override { return PauliNodeType::PauliPropagation; @@ -384,6 +385,26 @@ class PauliPropagation : public ACPairNode { unsigned qubit_index_; }; +// /** +// * @brief Measurement that has quantum or classical successors +// */ +// class Reset : public ACPairNode { +// public: +// /** +// * @brief Construct a new MidMeasure object. +// * +// * @param string the Pauli string +// */ +// Reset(std::vector z_string, unsigned bit); + +// PauliNodeType get_type() const override { return PauliNodeType::MidMeasure; +// }; CommuteInfo get_commute_info() const override; unsigned bit() const { +// return bit_; }; + +// protected: +// const unsigned bit_; +// }; + typedef boost::adjacency_list< boost::listS, boost::listS, boost::bidirectionalS, // indexing needed for algorithms such as topological sort diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index fb28dee984..e90f7c4688 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -125,20 +125,20 @@ std::pair SingleNode::first_support() const { // ACPairNode ACPairNode::ACPairNode( - std::vector z_propagation, std::vector x_propagation, - bool z_sign, bool x_sign) - : z_propagation_(z_propagation), - x_propagation_(x_propagation), + std::vector z_string, std::vector x_string, bool z_sign, + bool x_sign) + : z_string_(z_string), + x_string_(x_string), z_sign_(z_sign), x_sign_(x_sign) { - if (z_propagation.empty() || x_propagation.empty()) { + if (z_string.empty() || x_string.empty()) { throw GreedyPauliSimpError("ACPairNode cannot have empty strings."); } n_commute_entries_ = 0; n_anti_commute_entries_ = 0; - for (unsigned i = 0; i < z_propagation_.size(); i++) { + for (unsigned i = 0; i < z_string_.size(); i++) { CommuteType commute_type = - get_pauli_pair_commute_type(z_propagation_[i], x_propagation_[i]); + get_pauli_pair_commute_type(z_string_[i], x_string_[i]); commute_type_vec_.push_back(commute_type); if (commute_type == CommuteType::C) { n_commute_entries_ += 1; @@ -156,10 +156,10 @@ unsigned ACPairNode::tqe_cost() const { int ACPairNode::tqe_cost_increase(const TQE& tqe) const { auto [g, a, b] = tqe; - Pauli z_p0 = z_propagation_[a]; - Pauli z_p1 = z_propagation_[b]; - Pauli x_p0 = x_propagation_[a]; - Pauli x_p1 = x_propagation_[b]; + Pauli z_p0 = z_string_[a]; + Pauli z_p1 = z_string_[b]; + Pauli x_p0 = x_string_[a]; + Pauli x_p1 = x_string_[b]; auto [new_z_p0, new_z_p1, z_sign] = TQE_PAULI_MAP.at({g, z_p0, z_p1}); auto [new_x_p0, new_x_p1, x_sign] = TQE_PAULI_MAP.at({g, x_p0, x_p1}); CommuteType new_a_type = get_pauli_pair_commute_type(new_z_p0, new_x_p0); @@ -179,10 +179,10 @@ int ACPairNode::tqe_cost_increase(const TQE& tqe) const { void ACPairNode::update(const TQE& tqe) { auto [g, a, b] = tqe; - Pauli z_p0 = z_propagation_[a]; - Pauli z_p1 = z_propagation_[b]; - Pauli x_p0 = x_propagation_[a]; - Pauli x_p1 = x_propagation_[b]; + Pauli z_p0 = z_string_[a]; + Pauli z_p1 = z_string_[b]; + Pauli x_p0 = x_string_[a]; + Pauli x_p1 = x_string_[b]; auto [new_z_p0, new_z_p1, z_sign] = TQE_PAULI_MAP.at({g, z_p0, z_p1}); auto [new_x_p0, new_x_p1, x_sign] = TQE_PAULI_MAP.at({g, x_p0, x_p1}); CommuteType new_a_type = get_pauli_pair_commute_type(new_z_p0, new_x_p0); @@ -201,10 +201,10 @@ void ACPairNode::update(const TQE& tqe) { n_commute_entries_ += commute_increase; commute_type_vec_[a] = new_a_type; commute_type_vec_[b] = new_b_type; - z_propagation_[a] = new_z_p0; - z_propagation_[b] = new_z_p1; - x_propagation_[a] = new_x_p0; - x_propagation_[b] = new_x_p1; + z_string_[a] = new_z_p0; + z_string_[b] = new_z_p1; + x_string_[a] = new_x_p0; + x_string_[b] = new_x_p1; if (!z_sign) { z_sign_ = !z_sign_; } @@ -214,10 +214,10 @@ void ACPairNode::update(const TQE& tqe) { } void ACPairNode::update(const OpType& sq_cliff, const unsigned& a) { - auto [new_z_p, z_sign] = SQ_CLIFF_MAP.at({sq_cliff, z_propagation_[a]}); - auto [new_x_p, x_sign] = SQ_CLIFF_MAP.at({sq_cliff, x_propagation_[a]}); - z_propagation_[a] = new_z_p; - x_propagation_[a] = new_x_p; + auto [new_z_p, z_sign] = SQ_CLIFF_MAP.at({sq_cliff, z_string_[a]}); + auto [new_x_p, x_sign] = SQ_CLIFF_MAP.at({sq_cliff, x_string_[a]}); + z_string_[a] = new_z_p; + x_string_[a] = new_x_p; if (!z_sign) { z_sign_ = !z_sign_; } @@ -227,8 +227,8 @@ void ACPairNode::update(const OpType& sq_cliff, const unsigned& a) { } void ACPairNode::swap(const unsigned& a, const unsigned& b) { - std::swap(z_propagation_[a], z_propagation_[b]); - std::swap(x_propagation_[a], x_propagation_[b]); + std::swap(z_string_[a], z_string_[b]); + std::swap(x_string_[a], x_string_[b]); std::swap(commute_type_vec_[a], commute_type_vec_[b]); } @@ -251,20 +251,17 @@ std::vector ACPairNode::reduction_tqes() const { if (ctype1 == CommuteType::A) { // TQEs that transform a AA pair to CC tqe_types = AA_TO_CC_MAP.at( - {z_propagation_[a], z_propagation_[b], x_propagation_[a], - x_propagation_[b]}); + {z_string_[a], z_string_[b], x_string_[a], x_string_[b]}); } else { // TQEs that transform a AC pair to AI tqe_types = AC_TO_AI_MAP.at( - {z_propagation_[a], z_propagation_[b], x_propagation_[a], - x_propagation_[b]}); + {z_string_[a], z_string_[b], x_string_[a], x_string_[b]}); } } else { if (ctype1 == CommuteType::A) { // TQEs that transform a CA pair to a IA tqe_types = AC_TO_AI_MAP.at( - {z_propagation_[b], z_propagation_[a], x_propagation_[b], - x_propagation_[a]}); + {z_string_[b], z_string_[a], x_string_[b], x_string_[a]}); // flip qubits a = sqs[j]; b = sqs[i]; @@ -272,8 +269,7 @@ std::vector ACPairNode::reduction_tqes() const { // TQEs that transform a CC pair to CI or IC, not always // possible tqe_types = CC_TO_IC_OR_CI_MAP.at( - {z_propagation_[a], z_propagation_[b], x_propagation_[a], - x_propagation_[b]}); + {z_string_[a], z_string_[b], x_string_[a], x_string_[b]}); } } for (const TQEType& tt : tqe_types) { @@ -287,7 +283,7 @@ std::vector ACPairNode::reduction_tqes() const { std::tuple ACPairNode::first_support() const { for (unsigned i = 0; i < commute_type_vec_.size(); i++) { if (commute_type_vec_[i] != CommuteType::I) { - return {i, z_propagation_[i], x_propagation_[i]}; + return {i, z_string_[i], x_string_[i]}; } } // Should be impossible to reach here @@ -318,13 +314,13 @@ CommuteInfo ConditionalPauliRotation::get_commute_info() const { // PauliPropagation PauliPropagation::PauliPropagation( - std::vector z_propagation, std::vector x_propagation, - bool z_sign, bool x_sign, unsigned qubit_index) - : ACPairNode(z_propagation, x_propagation, z_sign, x_sign), + std::vector z_string, std::vector x_string, bool z_sign, + bool x_sign, unsigned qubit_index) + : ACPairNode(z_string, x_string, z_sign, x_sign), qubit_index_(qubit_index) {} CommuteInfo PauliPropagation::get_commute_info() const { - return {{z_propagation_, x_propagation_}, {}}; + return {{z_string_, x_string_}, {}}; } // ClassicalNode From bbc43e5a57ae8f8ef45958c2bae8ffb7a07ca784 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Mon, 7 Oct 2024 11:46:28 +0100 Subject: [PATCH 20/57] Add support for resets --- .../GreedyPauliOptimisation.hpp | 35 ++++++++----------- .../Transformations/GreedyPauliConverters.cpp | 26 +++++++++----- tket/src/Transformations/GreedyPauliOps.cpp | 14 ++++++-- .../GreedyPauliOptimisation.cpp | 27 ++++++++++++++ tket/test/src/test_GreedyPauli.cpp | 18 +++++----- 5 files changed, 80 insertions(+), 40 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index dbdc34a79b..0c88caf1b5 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -58,6 +58,8 @@ enum class PauliNodeType { ClassicalNode, // Mid-circuit measurement MidMeasure, + // Reset + Reset, }; /** @@ -311,7 +313,7 @@ class MidMeasure : public SingleNode { * @param string the Pauli string * @param bit bit to store the readout */ - MidMeasure(std::vector string, unsigned bit); + MidMeasure(std::vector string, bool sign, unsigned bit); PauliNodeType get_type() const override { return PauliNodeType::MidMeasure; }; CommuteInfo get_commute_info() const override; @@ -385,25 +387,18 @@ class PauliPropagation : public ACPairNode { unsigned qubit_index_; }; -// /** -// * @brief Measurement that has quantum or classical successors -// */ -// class Reset : public ACPairNode { -// public: -// /** -// * @brief Construct a new MidMeasure object. -// * -// * @param string the Pauli string -// */ -// Reset(std::vector z_string, unsigned bit); - -// PauliNodeType get_type() const override { return PauliNodeType::MidMeasure; -// }; CommuteInfo get_commute_info() const override; unsigned bit() const { -// return bit_; }; - -// protected: -// const unsigned bit_; -// }; +/** + * @brief Reset + */ +class Reset : public ACPairNode { + public: + Reset( + std::vector z_string, std::vector x_string, bool z_sign, + bool x_sign); + + PauliNodeType get_type() const override { return PauliNodeType::Reset; }; + CommuteInfo get_commute_info() const override; +}; typedef boost::adjacency_list< boost::listS, boost::listS, boost::bidirectionalS, diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index 59227502a3..84dd2420e0 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -262,20 +262,20 @@ void GPGraph::apply_gate_at_end( if (it != end_measures_.left.end()) { // the measurement is no longer end-circuit, we remove it from // end_measures_ and add it as a MidMeasure node instead. - std::vector paulis(n_qubits_, Pauli::I); - paulis[it->first] = Pauli::Z; - PauliNode_ptr node = std::make_shared(paulis, it->second); + SpPauliStabiliser paulis = cliff_.get_zrow(Qubit(it->first)); + auto [pauli_dense, angle] = dense_pauli(paulis, n_qubits_, 1.); + PauliNode_ptr node = std::make_shared( + pauli_dense, (angle == 1.), it->second); apply_node_at_end(node); end_measures_.left.erase(it); } } else if (arg.type() == UnitType::Bit) { auto it = end_measures_.right.find(arg.index().at(0)); if (it != end_measures_.right.end()) { - // the measurement is no longer end-circuit, we remove it from - // end_measures_ and add it as a MidMeasure node instead. - std::vector paulis(n_qubits_, Pauli::I); - paulis[it->second] = Pauli::Z; - PauliNode_ptr node = std::make_shared(paulis, it->first); + SpPauliStabiliser paulis = cliff_.get_zrow(Qubit(it->second)); + auto [pauli_dense, angle] = dense_pauli(paulis, n_qubits_, 1.); + PauliNode_ptr node = + std::make_shared(pauli_dense, (angle == 1.), it->first); apply_node_at_end(node); end_measures_.right.erase(it); } @@ -302,6 +302,16 @@ void GPGraph::apply_gate_at_end( {args.at(0).index().at(0), args.at(1).index().at(0)}); return; } + case OpType::Reset: { + SpPauliStabiliser z_paulis = cliff_.get_zrow(qbs[0]); + auto [z_pauli_dense, z_angle] = dense_pauli(z_paulis, n_qubits_, 1.); + SpPauliStabiliser x_paulis = cliff_.get_xrow(qbs[0]); + auto [x_pauli_dense, x_angle] = dense_pauli(x_paulis, n_qubits_, 1.); + PauliNode_ptr node = std::make_shared( + z_pauli_dense, x_pauli_dense, (z_angle == 1.), (x_angle == 1.)); + apply_node_at_end(node); + return; + } case OpType::Z: { pauli_rots.push_back({{Pauli::Z}, 1}); break; diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index e90f7c4688..f1423f3ece 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -336,13 +336,23 @@ CommuteInfo ClassicalNode::get_commute_info() const { } // MidMeasure -MidMeasure::MidMeasure(std::vector string, unsigned bit) - : SingleNode(string, true), bit_(bit) {} +MidMeasure::MidMeasure(std::vector string, bool sign, unsigned bit) + : SingleNode(string, sign), bit_(bit) {} CommuteInfo MidMeasure::get_commute_info() const { return {{string_}, {{Bit(bit_), BitType::WRITE}}}; } +// Reset +Reset::Reset( + std::vector z_string, std::vector x_string, bool z_sign, + bool x_sign) + : ACPairNode(z_string, x_string, z_sign, x_sign) {} + +CommuteInfo Reset::get_commute_info() const { + return {{z_string_, x_string_}, {}}; +} + } // namespace GreedyPauliSimp } // namespace Transforms diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 9c753b0c2d..6456640e4e 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -327,6 +327,33 @@ static void consume_available_rotations( for (unsigned i = first_set.size(); i-- > 0;) { PauliNode_ptr& node_ptr = first_set[i]; switch (node_ptr->get_type()) { + case PauliNodeType::Reset: { + if (node_ptr->tqe_cost() > 0) continue; + Reset& node = dynamic_cast(*node_ptr); + auto [q_index, supp_z, supp_x] = node.first_support(); + std::vector optype_list = AA_TO_ZX.at({supp_z, supp_x}); + for (auto it = optype_list.begin(); it != optype_list.end(); ++it) { + circ.add_op(*it, {q_index}); + } + if (!node.z_sign()) { + circ.add_op(OpType::X, {q_index}); + } + if (!node.x_sign()) { + circ.add_op(OpType::Z, {q_index}); + } + circ.add_op(OpType::Reset, {q_index}); + if (!node.z_sign()) { + circ.add_op(OpType::X, {q_index}); + } + if (!node.x_sign()) { + circ.add_op(OpType::Z, {q_index}); + } + for (auto it = optype_list.rbegin(); it != optype_list.rend(); ++it) { + circ.add_op(SQ_CLIFF_DAGGER.at(*it), {q_index}); + } + first_set.erase(first_set.begin() + i); + break; + } case PauliNodeType::MidMeasure: { if (node_ptr->tqe_cost() > 0) continue; MidMeasure& node = dynamic_cast(*node_ptr); diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index 068e9e45ed..002198237a 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -29,16 +29,6 @@ namespace tket { namespace test_GreedyPauliSimp { -SCENARIO("Unsupported circuits") { - GIVEN("Circuit with resets") { - Circuit circ(1); - circ.add_op(OpType::H, {0}); - circ.add_op(OpType::Reset, {0}); - REQUIRE_THROWS_MATCHES( - Transforms::greedy_pauli_optimisation().apply(circ), BadOpType, - MessageContains("Cannot add gate to GPGraph")); - } -} SCENARIO("Clifford synthesis") { GIVEN("Empty circuit") { Circuit circ(3); @@ -310,6 +300,14 @@ SCENARIO("Complete synthesis") { REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } + GIVEN("Circuit with resets") { + Circuit circ(2); + circ.add_op(OpType::CX, {0, 1}); + circ.add_op(OpType::Reset, {0}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + REQUIRE(circ == d); + } } SCENARIO("Test GreedyPauliSimp pass construction") { // test pass construction From 00c6acad959b005e91e24ff63b6d6fe2475af4c2 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Mon, 7 Oct 2024 17:20:13 +0100 Subject: [PATCH 21/57] Update docstrings --- .../GreedyPauliOptimisation.hpp | 116 ++++++++++++------ .../Transformations/GreedyPauliConverters.cpp | 2 + .../GreedyPauliOptimisation.cpp | 11 +- 3 files changed, 85 insertions(+), 44 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 0c88caf1b5..a30cc9bcfa 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -54,7 +54,7 @@ enum class PauliNodeType { PauliPropagation, // Pauli rotation with classical control ConditionalPauliRotation, - // Classical operations, + // Classical operation ClassicalNode, // Mid-circuit measurement MidMeasure, @@ -87,12 +87,20 @@ enum class BitType : unsigned { */ using TQE = std::tuple; +/** + * @brief Commutation information of a node specified by a list of + * Pauli strings along with classical READs and WRITEs. + */ struct CommuteInfo { std::vector> paulis; // We use UnitID to differentiate between Bit and WasmState std::vector> bits_info; }; +/** + * @brief Base class for nodes in the Greedy Pauli graph + * + */ class PauliNode { public: virtual PauliNodeType get_type() const = 0; @@ -109,7 +117,7 @@ class PauliNode { typedef std::shared_ptr PauliNode_ptr; /** - * @brief A node defined by a single Pauli string + * @brief Base class for nodes defined by a single Pauli string */ class SingleNode : public PauliNode { public: @@ -168,7 +176,7 @@ class SingleNode : public PauliNode { }; /** - * @brief Node consists of a pair of anti-commuting Pauli strings + * @brief Base class for nodes defined by a pair of anti-commuting Pauli strings */ class ACPairNode : public PauliNode { public: @@ -252,7 +260,7 @@ class ACPairNode : public PauliNode { }; /** - * @brief Contains a Op on classical wires + * @brief Black box node for classical Ops */ class ClassicalNode : public PauliNode { public: @@ -272,12 +280,12 @@ class ClassicalNode : public PauliNode { CommuteInfo get_commute_info() const override; protected: - std::vector args_; - Op_ptr op_; + const std::vector args_; + const Op_ptr op_; }; /** - * @brief A Pauli exponential defined by a padded Pauli string + * @brief A Pauli exponential defined by a dense Pauli string * and a rotation angle */ class PauliRotation : public SingleNode { @@ -299,7 +307,7 @@ class PauliRotation : public SingleNode { CommuteInfo get_commute_info() const override; protected: - Expr theta_; + const Expr theta_; }; /** @@ -308,10 +316,11 @@ class PauliRotation : public SingleNode { class MidMeasure : public SingleNode { public: /** - * @brief Construct a new MidMeasure object. + * @brief Construct a new Mid Measure object * - * @param string the Pauli string - * @param bit bit to store the readout + * @param string dense Pauli string + * @param sign the sign of the Pauli string + * @param bit readout bit */ MidMeasure(std::vector string, bool sign, unsigned bit); @@ -324,16 +333,17 @@ class MidMeasure : public SingleNode { }; /** - * @brief A Pauli exponential defined by a padded Pauli string - * and a rotation angle + * @brief Conditional Pauli rotation */ class ConditionalPauliRotation : public PauliRotation { public: /** - * @brief Construct a new PauliRotation object. + * @brief Construct a new Conditional Pauli Rotation object * * @param string the Pauli string * @param theta the rotation angle in half-turns + * @param cond_bits conditional bits + * @param cond_value conditional value */ ConditionalPauliRotation( std::vector string, Expr theta, std::vector cond_bits, @@ -349,8 +359,8 @@ class ConditionalPauliRotation : public PauliRotation { unsigned cond_value() const { return cond_value_; }; protected: - std::vector cond_bits_; - unsigned cond_value_; + const std::vector cond_bits_; + const unsigned cond_value_; }; /** @@ -365,11 +375,11 @@ class PauliPropagation : public ACPairNode { /** * @brief Construct a new PauliPropagation object * - * @param z_string - * @param x_string - * @param z_sign - * @param x_sign - * @param qubit_index + * @param z_string propagated Pauli Z + * @param x_string propagated Pauli X + * @param z_sign the sign of z_string + * @param x_sign the sign of x_string + * @param qubit_index i.e. row index */ PauliPropagation( std::vector z_string, std::vector x_string, bool z_sign, @@ -384,14 +394,26 @@ class PauliPropagation : public ACPairNode { unsigned qubit_index() const { return qubit_index_; }; private: - unsigned qubit_index_; + const unsigned qubit_index_; }; /** - * @brief Reset + * @brief Reset operation defined by a pair of anti-commuting strings + * For example, a tket Reset OpType can be defined as a Z/X pair. The Pauli Z + * can be seen as a Z-basis measurement, and the Pauli X can be seen as the post + * measurement conditional X gate. + * */ class Reset : public ACPairNode { public: + /** + * @brief Construct a new Reset object + * + * @param z_string + * @param x_string + * @param z_sign + * @param x_sign + */ Reset( std::vector z_string, std::vector x_string, bool z_sign, bool x_sign); @@ -417,21 +439,27 @@ typedef boost::adj_list_vertex_property_map< GPVIndex; /** - * Pauli graph structure for GreedyPauliSimp. - * The dependency graph consists of Pauli rotations, mid-circuit measurements, - * resets, conditional Pauli rotations, and classical operations as internal - * nodes. Edges represent gate dependencies, where one node depends on another - * if their underlying Pauli strings do not commute or if they share a classical - * bit. + * @brief Pauli graph structure for GreedyPauliSimp. + * + * A DAG is used to store all operations except for the end-of-circuit Clifford + * and end-of-circuit measurements. The vertices consist of Pauli rotations, + * mid-circuit measurements, resets, conditional Pauli rotations, and classical + * operations. Edges represent gate dependencies, where two nodes commute if + * they commute on both quantum and classical wires. * - * End-of-circuit measurements are stored as an integer-to-integer map. These - * measurements are kept separately (i.e. after the final Clifford) so the - * optimisation around them can be later handed to CliffordPushThroughMeausres. + * - Quantum commutation: Nodes commute if all Pauli strings in one node + * commute with all strings in the other. + * - Classical commutation: Nodes commute if they do not share classical + * bits, or if they only read from shared bits. * - * The final Clifford operator is stored using a UnitaryRevTableau. Note that - * UnitaryRevTableau is chose over PauliPropagations because of the abundance of - * already existed updating methods. + * End-of-circuit measurements are stored as a map from integers to integers. + * These measurements are kept separate (i.e., after the final Clifford) so + * optimisation around them can later be handled by + * `CliffordPushThroughMeasures`. * + * The final Clifford operator is stored using a `UnitaryRevTableau`. Note that + * `UnitaryRevTableau` is chosen over `PauliPropagations` due to the + * availability of existing update methods. */ class GPGraph { public: @@ -458,7 +486,7 @@ class GPGraph { private: /** - * Applies the given gate to the end of the circuit. + * Applies the given gate to the end of the graph. * Clifford gates transform the tableau. * Non-Clifford gates and conditional Clifford gates are transformed * into PauliNodes by the tableau and added @@ -468,11 +496,19 @@ class GPGraph { const Command& cmd, bool conditional = false, std::vector cond_bits = {}, unsigned cond_value = 0); + /** + * Add a Pauli rotation to the graph + * If the angle is non-Clifford or if conditional is true then add to the DAG + * as a PauliRotation node, otherwise update the tableau. + */ void apply_pauli_at_end( const std::vector& paulis, const Expr& angle, const qubit_vector_t& qbs, bool conditional = false, std::vector cond_bits = {}, unsigned cond_value = 0); + /** + * Add a node to the DAG and check if it can be merged with another node. + */ void apply_node_at_end(PauliNode_ptr& node); /** @@ -505,10 +541,10 @@ std::tuple, std::vector> gpg_from_unordered_set(const std::vector& unordered_set); /** - * @brief Given a circuit consists of PauliExpBoxes followed by clifford gates, - * and end-of-circuit measurements, implement the PauliExpBoxes and the final - * clifford subcircuit by applying Clifford gates and single qubit rotations in - * a greedy fashion. + * @brief Converts the given circuit into a GPGraph and conjugates each node + * by greedily applying 2-qubit Clifford gates until the node can be realised + * as a single-qubit gate, a measurement, or a reset. The final Clifford + * operator is synthesized in a similar fashion. * * @param circ * @param discount_rate diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index 84dd2420e0..6458386ff1 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -151,6 +151,8 @@ GPVertSet GPGraph::get_predecessors(const GPVert& vert) const { return preds; } +// Adapted from `PauliGraph`, when adding a node to the graph, we check if it +// can be merged with an existing node. void GPGraph::apply_node_at_end(PauliNode_ptr& node) { GPVertSet to_search = end_line_; GPVertSet commuted; diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 6456640e4e..f9d6e9516a 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -307,7 +307,7 @@ static void tableau_row_nodes_synthesis( } /** - * @brief Given a vector of sets of PauliRotation, implement any node in the + * @brief Given a vector of sets of PauliNodes, implement any node in the * first set where the tqe_cost is zero. Remove implemented nodes and the first * set if empty. * @@ -316,7 +316,7 @@ static void tableau_row_nodes_synthesis( * @return true if the first set is now empty and removed * @return false */ -static void consume_available_rotations( +static void consume_nodes( std::vector>& rotation_sets, Circuit& circ, DepthTracker& depth_tracker) { if (rotation_sets.empty()) { @@ -331,6 +331,7 @@ static void consume_available_rotations( if (node_ptr->tqe_cost() > 0) continue; Reset& node = dynamic_cast(*node_ptr); auto [q_index, supp_z, supp_x] = node.first_support(); + // conjugate the pair to +Z/X std::vector optype_list = AA_TO_ZX.at({supp_z, supp_x}); for (auto it = optype_list.begin(); it != optype_list.end(); ++it) { circ.add_op(*it, {q_index}); @@ -358,6 +359,7 @@ static void consume_available_rotations( if (node_ptr->tqe_cost() > 0) continue; MidMeasure& node = dynamic_cast(*node_ptr); auto [q_index, supp] = node.first_support(); + // Conjugate the Pauli to +Z switch (supp) { case Pauli::Z: { if (!node.sign()) { @@ -403,13 +405,14 @@ static void consume_available_rotations( break; } case PauliNodeType::ClassicalNode: { + // always implement Classical nodes ClassicalNode& node = dynamic_cast(*node_ptr); circ.add_op(node.op(), node.args()); first_set.erase(first_set.begin() + i); break; } - // conditionals are added as conditional PauliExpBoxes case PauliNodeType::ConditionalPauliRotation: { + // conditionals are added as conditional PauliExpBoxes ConditionalPauliRotation& node = dynamic_cast(*node_ptr); Op_ptr cond = std::make_shared( @@ -474,7 +477,7 @@ static void pauli_exps_synthesis( std::vector& rows, Circuit& circ, double discount_rate, double depth_weight, DepthTracker& depth_tracker) { while (true) { - consume_available_rotations(rotation_sets, circ, depth_tracker); + consume_nodes(rotation_sets, circ, depth_tracker); if (rotation_sets.empty()) break; std::vector& first_set = rotation_sets[0]; // get nodes with min cost From 30d483b9c3d3be891a5754a5f0aab1e855bccd99 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Mon, 7 Oct 2024 17:24:05 +0100 Subject: [PATCH 22/57] Update pass predicate --- tket/src/Predicates/PassGenerators.cpp | 48 ++++++++++++++++++-------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/tket/src/Predicates/PassGenerators.cpp b/tket/src/Predicates/PassGenerators.cpp index 54b0d98683..071a7065df 100644 --- a/tket/src/Predicates/PassGenerators.cpp +++ b/tket/src/Predicates/PassGenerators.cpp @@ -985,22 +985,40 @@ PassPtr gen_synthesise_pauli_graph( PassPtr gen_greedy_pauli_simp(double discount_rate, double depth_weight) { Transform t = Transforms::greedy_pauli_optimisation(discount_rate, depth_weight); - PredicatePtr ccontrol_pred = std::make_shared(); - PredicatePtr mid_pred = std::make_shared(); - OpTypeSet ins = {OpType::Z, OpType::X, OpType::Y, - OpType::S, OpType::Sdg, OpType::V, - OpType::Vdg, OpType::H, OpType::CX, - OpType::CY, OpType::CZ, OpType::SWAP, - OpType::Rz, OpType::Rx, OpType::Ry, - OpType::T, OpType::Tdg, OpType::ZZMax, - OpType::ZZPhase, OpType::PhaseGadget, OpType::XXPhase, - OpType::YYPhase, OpType::PauliExpBox, OpType::Measure, - OpType::PhasedX}; + OpTypeSet ins = { + OpType::Z, + OpType::X, + OpType::Y, + OpType::S, + OpType::Sdg, + OpType::V, + OpType::Vdg, + OpType::H, + OpType::CX, + OpType::CY, + OpType::CZ, + OpType::SWAP, + OpType::Rz, + OpType::Rx, + OpType::Ry, + OpType::T, + OpType::Tdg, + OpType::ZZMax, + OpType::ZZPhase, + OpType::PhaseGadget, + OpType::XXPhase, + OpType::YYPhase, + OpType::Measure, + OpType::PhasedX, + OpType::Reset, + OpType::Conditional, + OpType::PauliExpBox, + OpType::PauliExpPairBox, + OpType::PauliExpCommutingSetBox}; + + ins.insert(all_classical_types().begin(), all_classical_types().end()); PredicatePtr in_gates = std::make_shared(ins); - PredicatePtrMap precons{ - CompilationUnit::make_type_pair(ccontrol_pred), - CompilationUnit::make_type_pair(mid_pred), - CompilationUnit::make_type_pair(in_gates)}; + PredicatePtrMap precons{CompilationUnit::make_type_pair(in_gates)}; PredicateClassGuarantees g_postcons = { {typeid(ConnectivityPredicate), Guarantee::Clear}, {typeid(NoWireSwapsPredicate), Guarantee::Clear}}; From 7e1eee52bc4544e8a003cc900588b37f062fb68b Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 8 Oct 2024 00:58:45 +0100 Subject: [PATCH 23/57] Add more tests --- .../Transformations/GreedyPauliConverters.cpp | 2 +- tket/test/src/test_GreedyPauli.cpp | 132 ++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index 6458386ff1..44ebef10e0 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -446,7 +446,7 @@ void GPGraph::apply_gate_at_end( apply_node_at_end(node); return; } - throw BadOpType("Cannot add gate to GPGraph", type); + throw BadOpType("GreedyPauliSimp doesn't support", type); } } for (auto it = pauli_rots.begin(); it != pauli_rots.end(); ++it) { diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index 002198237a..939a95ba06 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -261,6 +261,54 @@ SCENARIO("Complete synthesis") { REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } + GIVEN("Circuit with conditional gates 2") { + Circuit circ(2, 1); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::X, Pauli::Z}, 0.12)), {0, 1}); + Op_ptr cond = std::make_shared( + std::make_shared( + SymPauliTensor({Pauli::Z, Pauli::Z}, 0.5)), + 1, 0); + circ.add_op(cond, {0, 0, 1}); + // two boxes anti-commute hence simultaneous diagonalisation + Circuit d(2, 1); + d.add_op(OpType::CY, {1, 0}); + d.add_op(OpType::Rx, 0.12, {0}); + Op_ptr cond2 = std::make_shared( + std::make_shared( + SymPauliTensor({Pauli::Z, Pauli::I}, 0.5)), + 1, 0); + d.add_op(cond2, {0, 0, 1}); + d.add_op(OpType::CY, {1, 0}); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + REQUIRE(circ == d); + } + GIVEN("Circuit with conditional gates and measures") { + PauliExpBox pb1(SymPauliTensor({Pauli::Z, Pauli::X}, 0.12)); + PauliExpBox pb2(SymPauliTensor({Pauli::X, Pauli::Z}, 0.3)); + Circuit circ(2, 2); + Op_ptr cond1 = std::make_shared( + std::make_shared( + SymPauliTensor({Pauli::Z, Pauli::X}, 0.5)), + 1, 0); + Op_ptr cond2 = std::make_shared( + std::make_shared( + SymPauliTensor({Pauli::X, Pauli::Z}, 0.25)), + 1, 0); + circ.add_op(cond1, {0, 0, 1}); + circ.add_op(OpType::Measure, {0, 0}); + // doesn't commute with measure + circ.add_op(cond2, {1, 0, 1}); + // can commute to the front + circ.add_op(cond1, {1, 0, 1}); + Circuit d(2, 2); + d.add_op(cond1, {0, 0, 1}); + d.add_op(cond1, {1, 0, 1}); + d.add_op(OpType::Measure, {0, 0}); + d.add_op(cond2, {1, 0, 1}); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + REQUIRE(circ == d); + } GIVEN("Circuit with classical gates") { Circuit circ(1, 4); circ.add_op(OpType::S, {0}); @@ -308,6 +356,90 @@ SCENARIO("Complete synthesis") { REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } + GIVEN("Circuit with measures, classicals, and resets") { + Circuit circ(3, 1); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::X, Pauli::Z, Pauli::Z}, 0.3)), + {0, 1, 2}); + circ.add_op(OpType::Measure, {0, 0}); + circ.add_op(ClassicalX(), {0}); + circ.add_op(OpType::Reset, {1}); + Circuit d(3, 1); + d.add_op(OpType::CZ, {0, 2}); + d.add_op(OpType::CZ, {0, 1}); + d.add_op(OpType::Rx, 0.3, {0}); + d.add_op(OpType::Measure, {0, 0}); + d.add_op(ClassicalX(), {0}); + d.add_op(OpType::CZ, {1, 0}); + d.add_op(OpType::CZ, {0, 2}); + d.add_op(OpType::Reset, {1}); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + REQUIRE(circ == d); + } +} +SCENARIO("Test GreedyPauliSimp for individual gates") { + Circuit circ(1); + circ.add_op(OpType::Z, {0}); + std::vector ops_0q = { + get_op_ptr(OpType::Phase, 0.25), + }; + std::vector ops_1q = { + get_op_ptr(OpType::noop), + get_op_ptr(OpType::Z), + get_op_ptr(OpType::X), + get_op_ptr(OpType::Y), + get_op_ptr(OpType::S), + get_op_ptr(OpType::V), + get_op_ptr(OpType::Sdg), + get_op_ptr(OpType::Vdg), + get_op_ptr(OpType::H), + get_op_ptr(OpType::Rz, 0.25), + get_op_ptr(OpType::Rz, 0.5), + get_op_ptr(OpType::Rx, 1), + get_op_ptr(OpType::Rx, 0.15), + get_op_ptr(OpType::Ry, 0.25), + get_op_ptr(OpType::Ry, -0.5), + get_op_ptr(OpType::PhasedX, {0.15, 0.2}), + get_op_ptr(OpType::PhasedX, {0.5, -0.5}), + get_op_ptr(OpType::PhasedX, {0.2, 1}), + get_op_ptr(OpType::T), + get_op_ptr(OpType::Tdg), + }; + std::vector ops_2q = { + get_op_ptr(OpType::SWAP), + get_op_ptr(OpType::CX), + get_op_ptr(OpType::CY), + get_op_ptr(OpType::CZ), + get_op_ptr(OpType::ZZMax), + get_op_ptr(OpType::ZZPhase, 0.25), + get_op_ptr(OpType::ZZPhase, 0.5), + get_op_ptr(OpType::PhaseGadget, 0.5, 2), + get_op_ptr(OpType::XXPhase, 0.25), + get_op_ptr(OpType::XXPhase, 0.5), + get_op_ptr(OpType::YYPhase, 0.25), + get_op_ptr(OpType::YYPhase, 1), + }; + for (Op_ptr op : ops_0q) { + Circuit circ(1); + circ.add_op(op, {}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + REQUIRE(test_unitary_comparison(circ, d, true)); + } + for (Op_ptr op : ops_1q) { + Circuit circ(1); + circ.add_op(op, {0}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + REQUIRE(test_unitary_comparison(circ, d, true)); + } + for (Op_ptr op : ops_2q) { + Circuit circ(2); + circ.add_op(op, {0, 1}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + REQUIRE(test_unitary_comparison(circ, d, true)); + } } SCENARIO("Test GreedyPauliSimp pass construction") { // test pass construction From 9d8094d16a14b93f80bc050680a4b1de791e7747 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 8 Oct 2024 01:16:25 +0100 Subject: [PATCH 24/57] bump tket version --- tket/conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tket/conanfile.py b/tket/conanfile.py index 5986b00417..f479eed352 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.3.31" + version = "1.3.32" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" From b98cb8a632cd255276f0319112ea6466e77051e3 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 8 Oct 2024 01:19:07 +0100 Subject: [PATCH 25/57] Add changelog entry --- pytket/docs/changelog.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 5d35129f96..05e072acfc 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -1,6 +1,14 @@ Changelog ========= +Unreleased +---------- + +Features: + +* `GreedyPauliSimp`` now supports mid-circuit measurements, resets, conditionals, + and classical gates. + 1.33.0 (October 2024) --------------------- From dc2dc1d37575723e3a87f3a7e3315a703128cca9 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 8 Oct 2024 09:49:52 +0100 Subject: [PATCH 26/57] Fix docs errors --- .../Transformations/GreedyPauliOptimisation.hpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index a30cc9bcfa..8e364640e9 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -200,10 +200,11 @@ class ACPairNode : public PauliNode { unsigned tqe_cost() const override; /** - * @brief Number of TQEs would required to reduce the weight to 1 + * @brief Number of additional TQEs would required to reduce the weight to 1 * after the given TQE is applied - * - * @return unsigned + * + * @param tqe + * @return int */ int tqe_cost_increase(const TQE& tqe) const override; @@ -216,15 +217,17 @@ class ACPairNode : public PauliNode { /** * @brief Update the support vector with a single-qubit Clifford gate - * - * @param tqe + * + * @param sq_cliff + * @param a */ void update(const OpType& sq_cliff, const unsigned& a) override; /** * @brief Update the support vector with a SWAP gate - * - * @param tqe + * + * @param a + * @param b */ void swap(const unsigned& a, const unsigned& b) override; From a8d3288fa8004f9f86f8983782fbbad52a99260b Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 8 Oct 2024 09:55:22 +0100 Subject: [PATCH 27/57] bump tket version --- tket/conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tket/conanfile.py b/tket/conanfile.py index f479eed352..04deefbc46 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.3.32" + version = "1.3.33" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" From 8ac2da54032f35e3d649a4e2c53caa771cfa7bb3 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 8 Oct 2024 10:07:13 +0100 Subject: [PATCH 28/57] Remove unused headers --- .../GreedyPauliOptimisation.hpp | 19 ++++++++++--------- .../Transformations/GreedyPauliConverters.cpp | 8 ++------ tket/src/Transformations/GreedyPauliOps.cpp | 3 --- .../GreedyPauliOptimisation.cpp | 2 -- tket/test/src/test_GreedyPauli.cpp | 1 - 5 files changed, 12 insertions(+), 21 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 8e364640e9..2da01eed33 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -125,6 +125,7 @@ class SingleNode : public PauliNode { * @brief Construct a new SinglePauliNode object. * * @param string the Pauli string + * @param sign sign of the Pauli string */ SingleNode(std::vector string, bool sign); @@ -202,9 +203,9 @@ class ACPairNode : public PauliNode { /** * @brief Number of additional TQEs would required to reduce the weight to 1 * after the given TQE is applied - * - * @param tqe - * @return int + * + * @param tqe + * @return int */ int tqe_cost_increase(const TQE& tqe) const override; @@ -217,17 +218,17 @@ class ACPairNode : public PauliNode { /** * @brief Update the support vector with a single-qubit Clifford gate - * - * @param sq_cliff - * @param a + * + * @param sq_cliff + * @param a */ void update(const OpType& sq_cliff, const unsigned& a) override; /** * @brief Update the support vector with a SWAP gate - * - * @param a - * @param b + * + * @param a + * @param b */ void swap(const unsigned& a, const unsigned& b) override; diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index 44ebef10e0..3d92dcdb46 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -15,12 +15,8 @@ #include #include "tket/Circuit/PauliExpBoxes.hpp" -#include "tket/Converters/Converters.hpp" #include "tket/OpType/OpType.hpp" -#include "tket/PauliGraph/PauliGraph.hpp" -#include "tket/Transformations/CliffordOptimisation.hpp" #include "tket/Transformations/GreedyPauliOptimisation.hpp" -#include "tket/Transformations/GreedyPauliOptimisationLookupTables.hpp" #include "tket/Transformations/Transform.hpp" namespace tket { @@ -164,7 +160,7 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) { to_search.erase(to_search.begin()); // Check that we have already commuted past all of its children bool ready = true; - for (const PauliVert& child : get_successors(to_compare)) { + for (const GPVert& child : get_successors(to_compare)) { if (commuted.get().find(child) == commuted.get().end()) { ready = false; break; @@ -205,7 +201,7 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) { } } // Commute and continue searching - PauliVertSet preds = get_predecessors(to_compare); + GPVertSet preds = get_predecessors(to_compare); to_search.insert(preds.begin(), preds.end()); commuted.insert(to_compare); } else { diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index f1423f3ece..8a40db259c 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -15,13 +15,10 @@ #include #include "tket/Circuit/PauliExpBoxes.hpp" -#include "tket/Converters/Converters.hpp" #include "tket/OpType/OpType.hpp" -#include "tket/PauliGraph/PauliGraph.hpp" #include "tket/Transformations/CliffordOptimisation.hpp" #include "tket/Transformations/GreedyPauliOptimisation.hpp" #include "tket/Transformations/GreedyPauliOptimisationLookupTables.hpp" -#include "tket/Transformations/Transform.hpp" namespace tket { diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index f9d6e9516a..9c364717a7 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -17,9 +17,7 @@ #include #include "tket/Circuit/PauliExpBoxes.hpp" -#include "tket/Converters/Converters.hpp" #include "tket/OpType/OpType.hpp" -#include "tket/PauliGraph/PauliGraph.hpp" #include "tket/Transformations/CliffordOptimisation.hpp" #include "tket/Transformations/GreedyPauliOptimisationLookupTables.hpp" #include "tket/Transformations/Transform.hpp" diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index 939a95ba06..ea63621e17 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -21,7 +21,6 @@ #include "tket/Circuit/Simulation/CircuitSimulator.hpp" #include "tket/Gate/SymTable.hpp" #include "tket/Ops/ClassicalOps.hpp" -#include "tket/PauliGraph/PauliGraph.hpp" #include "tket/Predicates/PassGenerators.hpp" #include "tket/Transformations/GreedyPauliOptimisation.hpp" #include "tket/Utils/Expression.hpp" From 354ce1f5754c2b66fa70edee6a8eeabff6f11930 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 8 Oct 2024 10:11:36 +0100 Subject: [PATCH 29/57] bump tket version in pytket --- pytket/conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 19596da3f2..bbba711fbf 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -38,7 +38,7 @@ def requirements(self): self.requires("pybind11_json/0.2.14") self.requires("symengine/0.12.0") self.requires("tkassert/0.3.4@tket/stable") - self.requires("tket/1.3.32@tket/stable") + self.requires("tket/1.3.33@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tktokenswap/0.3.9@tket/stable") From 64eebbb2700c8fe7e2bd67add97d393716074418 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Thu, 10 Oct 2024 09:02:25 +0100 Subject: [PATCH 30/57] uncomment lines --- tket/test/src/test_GreedyPauli.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index ea63621e17..3da4cb6161 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -44,9 +44,9 @@ SCENARIO("Clifford synthesis") { } GIVEN("2Q Simple Clifford") { Circuit circ(2); - // circ.add_op(OpType::Y, {0}); + circ.add_op(OpType::Y, {0}); circ.add_op(OpType::Vdg, {1}); - // circ.add_op(OpType::CX, {0, 1}); + circ.add_op(OpType::CX, {0, 1}); Circuit d(circ); REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); REQUIRE(test_unitary_comparison(circ, d, true)); From 664cf1047a1ce8a3b11cd10897c576cb39690dff Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Thu, 10 Oct 2024 09:20:11 +0100 Subject: [PATCH 31/57] remove consts in SQ_CLIFF_DAGGER --- .../Transformations/GreedyPauliOptimisationLookupTables.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp index af8c144426..a85a12b8bf 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp @@ -60,7 +60,7 @@ const static std::unordered_map< {{Pauli::Z, Pauli::X}, {}}, {{Pauli::Z, Pauli::Y}, {OpType::S}}}; -const static std::unordered_map SQ_CLIFF_DAGGER = { +const static std::unordered_map SQ_CLIFF_DAGGER = { {OpType::H, OpType::H}, {OpType::S, OpType::Sdg}, {OpType::Sdg, OpType::S}, From f5138f1357edeabd4cee884ce3512535901a447e Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Thu, 10 Oct 2024 15:19:14 +0100 Subject: [PATCH 32/57] Merging conditionals --- .../GreedyPauliOptimisation.cpp | 75 +++++++++++++------ tket/test/src/test_GreedyPauli.cpp | 40 ++++++---- 2 files changed, 77 insertions(+), 38 deletions(-) diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 9c364717a7..4ba70473cc 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -309,19 +309,26 @@ static void tableau_row_nodes_synthesis( * first set where the tqe_cost is zero. Remove implemented nodes and the first * set if empty. * - * @param rotation_sets + * @param node_sets * @param circ + * @param depth_tracker + * @param discount_rate + * @param depth_weight + * * @return true if the first set is now empty and removed * @return false */ static void consume_nodes( - std::vector>& rotation_sets, Circuit& circ, - DepthTracker& depth_tracker) { - if (rotation_sets.empty()) { + std::vector>& node_sets, Circuit& circ, + DepthTracker& depth_tracker, double discount_rate, double depth_weight) { + if (node_sets.empty()) { return; } while (true) { - std::vector& first_set = rotation_sets[0]; + std::vector& first_set = node_sets[0]; + // try to merge conditionals, and each conditioned circuits will be + // optimised recursively via greedy_pauli_graph_synthesis + std::map, unsigned>, Circuit> conditionals; for (unsigned i = first_set.size(); i-- > 0;) { PauliNode_ptr& node_ptr = first_set[i]; switch (node_ptr->get_type()) { @@ -413,15 +420,24 @@ static void consume_nodes( // conditionals are added as conditional PauliExpBoxes ConditionalPauliRotation& node = dynamic_cast(*node_ptr); - Op_ptr cond = std::make_shared( - std::make_shared( - SymPauliTensor(node.string(), node.angle())), - (unsigned)node.cond_bits().size(), node.cond_value()); - std::vector args = node.cond_bits(); + const std::vector cond_bits = node.cond_bits(); + const unsigned cond_value = node.cond_value(); + std::vector qubits; for (unsigned i = 0; i < node.string().size(); i++) { - args.push_back(i); + qubits.push_back(i); + } + Op_ptr peb_op = std::make_shared( + SymPauliTensor(node.string(), node.angle())); + + if (conditionals.find({cond_bits, cond_value}) == + conditionals.end()) { + Circuit cond_circ(circ.n_qubits()); + cond_circ.add_op(peb_op, qubits); + conditionals[{cond_bits, cond_value}] = cond_circ; + } else { + conditionals[{cond_bits, cond_value}].add_op( + peb_op, qubits); } - circ.add_op(cond, args); first_set.erase(first_set.begin() + i); break; } @@ -456,9 +472,21 @@ static void consume_nodes( TKET_ASSERT(false); } } + for (auto it = conditionals.begin(); it != conditionals.end(); it++) { + Circuit cond_circ = + greedy_pauli_graph_synthesis(it->second, discount_rate, depth_weight); + Op_ptr cond = std::make_shared( + std::make_shared(cond_circ), it->first.first.size(), + it->first.second); + std::vector args = it->first.first; + for (unsigned i = 0; i < cond_circ.n_qubits(); i++) { + args.push_back(i); + } + circ.add_op(cond, args); + } if (first_set.empty()) { - rotation_sets.erase(rotation_sets.begin()); - if (rotation_sets.empty()) { + node_sets.erase(node_sets.begin()); + if (node_sets.empty()) { return; } } else { @@ -471,13 +499,13 @@ static void consume_nodes( * @brief Synthesise a vector of unordered rotation sets */ static void pauli_exps_synthesis( - std::vector>& rotation_sets, + std::vector>& node_sets, std::vector& rows, Circuit& circ, double discount_rate, double depth_weight, DepthTracker& depth_tracker) { while (true) { - consume_nodes(rotation_sets, circ, depth_tracker); - if (rotation_sets.empty()) break; - std::vector& first_set = rotation_sets[0]; + consume_nodes(node_sets, circ, depth_tracker, discount_rate, depth_weight); + if (node_sets.empty()) break; + std::vector& first_set = node_sets[0]; // get nodes with min cost std::vector min_nodes_indices = {0}; unsigned min_cost = first_set[0]->tqe_cost(); @@ -501,7 +529,7 @@ static void pauli_exps_synthesis( for (const TQE& tqe : tqe_candidates) { tqe_candidates_cost.insert( {tqe, - {default_pauliexp_tqe_cost(discount_rate, rotation_sets, rows, tqe), + {default_pauliexp_tqe_cost(discount_rate, node_sets, rows, tqe), static_cast(depth_tracker.gate_depth( std::get<1>(tqe), std::get<2>(tqe)))}}); } @@ -511,7 +539,7 @@ static void pauli_exps_synthesis( apply_tqe_to_circ(selected_tqe, circ); depth_tracker.add_2q_gate( std::get<1>(selected_tqe), std::get<2>(selected_tqe)); - for (std::vector& rotation_set : rotation_sets) { + for (std::vector& rotation_set : node_sets) { for (PauliNode_ptr& node : rotation_set) { node->update(selected_tqe); } @@ -557,12 +585,11 @@ Circuit greedy_pauli_graph_synthesis( rev_unit_map.insert({pair.second, pair.first}); } GPGraph gpg(circ_flat); - auto [rotation_sets, rows, measures] = gpg.get_sequence(); + auto [node_sets, rows, measures] = gpg.get_sequence(); DepthTracker depth_tracker(n_qubits); // synthesise Pauli exps pauli_exps_synthesis( - rotation_sets, rows, new_circ, discount_rate, depth_weight, - depth_tracker); + node_sets, rows, new_circ, discount_rate, depth_weight, depth_tracker); // synthesise the tableau tableau_row_nodes_synthesis(rows, new_circ, depth_weight, depth_tracker); for (auto it = measures.begin(); it != measures.end(); ++it) { @@ -579,6 +606,8 @@ Transform greedy_pauli_optimisation(double discount_rate, double depth_weight) { return Transform([discount_rate, depth_weight](Circuit& circ) { circ = GreedyPauliSimp::greedy_pauli_graph_synthesis( circ, discount_rate, depth_weight); + // decompose conditional circ boxes + circ.decompose_boxes_recursively(); singleq_clifford_sweep().apply(circ); return true; }); diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index 3da4cb6161..b31dbda5c0 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -252,11 +252,8 @@ SCENARIO("Complete synthesis") { circ.add_conditional_gate(OpType::Rz, {0.5}, {0}, {0}, 0); circ.add_op(OpType::CX, {0, 1}); Circuit d(2, 2); - Op_ptr cond = std::make_shared( - std::make_shared( - SymPauliTensor({Pauli::Z, Pauli::I}, 0.5)), - 1, 0); - d.add_op(cond, {0, 0, 1}); + d.add_conditional_gate(OpType::Sdg, {}, {0}, {0}, 0); + d.add_conditional_gate(OpType::Z, {}, {0}, {0}, 0); REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } @@ -273,18 +270,13 @@ SCENARIO("Complete synthesis") { Circuit d(2, 1); d.add_op(OpType::CY, {1, 0}); d.add_op(OpType::Rx, 0.12, {0}); - Op_ptr cond2 = std::make_shared( - std::make_shared( - SymPauliTensor({Pauli::Z, Pauli::I}, 0.5)), - 1, 0); - d.add_op(cond2, {0, 0, 1}); + d.add_conditional_gate(OpType::Sdg, {}, {0}, {0}, 0); + d.add_conditional_gate(OpType::Z, {}, {0}, {0}, 0); d.add_op(OpType::CY, {1, 0}); REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } GIVEN("Circuit with conditional gates and measures") { - PauliExpBox pb1(SymPauliTensor({Pauli::Z, Pauli::X}, 0.12)); - PauliExpBox pb2(SymPauliTensor({Pauli::X, Pauli::Z}, 0.3)); Circuit circ(2, 2); Op_ptr cond1 = std::make_shared( std::make_shared( @@ -301,10 +293,28 @@ SCENARIO("Complete synthesis") { // can commute to the front circ.add_op(cond1, {1, 0, 1}); Circuit d(2, 2); - d.add_op(cond1, {0, 0, 1}); - d.add_op(cond1, {1, 0, 1}); + // ZZ 0.5 + d.add_conditional_gate(OpType::CX, {}, {0, 1}, {0}, 0); + d.add_conditional_gate(OpType::Sdg, {}, {0}, {0}, 0); + d.add_conditional_gate(OpType::V, {}, {1}, {0}, 0); + d.add_conditional_gate(OpType::Z, {}, {0}, {0}, 0); + // ZZ 0.5 conditioned on bit(1) + d.add_conditional_gate(OpType::CX, {}, {0, 1}, {1}, 0); + d.add_conditional_gate(OpType::Sdg, {}, {0}, {1}, 0); + d.add_conditional_gate(OpType::V, {}, {1}, {1}, 0); + d.add_conditional_gate(OpType::Z, {}, {0}, {1}, 0); d.add_op(OpType::Measure, {0, 0}); - d.add_op(cond2, {1, 0, 1}); + // XZ 0.25 + // compute + d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); + d.add_conditional_gate(OpType::CX, {}, {0, 1}, {1}, 0); + d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); + // action + d.add_conditional_gate(OpType::Rz, {0.25}, {1}, {1}, 0); + // uncompute + d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); + d.add_conditional_gate(OpType::CX, {}, {0, 1}, {1}, 0); + d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } From 21f806f73dd1310734a912a9a6e98fb44e2bffe1 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Thu, 10 Oct 2024 15:19:20 +0100 Subject: [PATCH 33/57] Revert "Merging conditionals" This reverts commit f5138f1357edeabd4cee884ce3512535901a447e. --- .../GreedyPauliOptimisation.cpp | 75 ++++++------------- tket/test/src/test_GreedyPauli.cpp | 40 ++++------ 2 files changed, 38 insertions(+), 77 deletions(-) diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 4ba70473cc..9c364717a7 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -309,26 +309,19 @@ static void tableau_row_nodes_synthesis( * first set where the tqe_cost is zero. Remove implemented nodes and the first * set if empty. * - * @param node_sets + * @param rotation_sets * @param circ - * @param depth_tracker - * @param discount_rate - * @param depth_weight - * * @return true if the first set is now empty and removed * @return false */ static void consume_nodes( - std::vector>& node_sets, Circuit& circ, - DepthTracker& depth_tracker, double discount_rate, double depth_weight) { - if (node_sets.empty()) { + std::vector>& rotation_sets, Circuit& circ, + DepthTracker& depth_tracker) { + if (rotation_sets.empty()) { return; } while (true) { - std::vector& first_set = node_sets[0]; - // try to merge conditionals, and each conditioned circuits will be - // optimised recursively via greedy_pauli_graph_synthesis - std::map, unsigned>, Circuit> conditionals; + std::vector& first_set = rotation_sets[0]; for (unsigned i = first_set.size(); i-- > 0;) { PauliNode_ptr& node_ptr = first_set[i]; switch (node_ptr->get_type()) { @@ -420,24 +413,15 @@ static void consume_nodes( // conditionals are added as conditional PauliExpBoxes ConditionalPauliRotation& node = dynamic_cast(*node_ptr); - const std::vector cond_bits = node.cond_bits(); - const unsigned cond_value = node.cond_value(); - std::vector qubits; + Op_ptr cond = std::make_shared( + std::make_shared( + SymPauliTensor(node.string(), node.angle())), + (unsigned)node.cond_bits().size(), node.cond_value()); + std::vector args = node.cond_bits(); for (unsigned i = 0; i < node.string().size(); i++) { - qubits.push_back(i); - } - Op_ptr peb_op = std::make_shared( - SymPauliTensor(node.string(), node.angle())); - - if (conditionals.find({cond_bits, cond_value}) == - conditionals.end()) { - Circuit cond_circ(circ.n_qubits()); - cond_circ.add_op(peb_op, qubits); - conditionals[{cond_bits, cond_value}] = cond_circ; - } else { - conditionals[{cond_bits, cond_value}].add_op( - peb_op, qubits); + args.push_back(i); } + circ.add_op(cond, args); first_set.erase(first_set.begin() + i); break; } @@ -472,21 +456,9 @@ static void consume_nodes( TKET_ASSERT(false); } } - for (auto it = conditionals.begin(); it != conditionals.end(); it++) { - Circuit cond_circ = - greedy_pauli_graph_synthesis(it->second, discount_rate, depth_weight); - Op_ptr cond = std::make_shared( - std::make_shared(cond_circ), it->first.first.size(), - it->first.second); - std::vector args = it->first.first; - for (unsigned i = 0; i < cond_circ.n_qubits(); i++) { - args.push_back(i); - } - circ.add_op(cond, args); - } if (first_set.empty()) { - node_sets.erase(node_sets.begin()); - if (node_sets.empty()) { + rotation_sets.erase(rotation_sets.begin()); + if (rotation_sets.empty()) { return; } } else { @@ -499,13 +471,13 @@ static void consume_nodes( * @brief Synthesise a vector of unordered rotation sets */ static void pauli_exps_synthesis( - std::vector>& node_sets, + std::vector>& rotation_sets, std::vector& rows, Circuit& circ, double discount_rate, double depth_weight, DepthTracker& depth_tracker) { while (true) { - consume_nodes(node_sets, circ, depth_tracker, discount_rate, depth_weight); - if (node_sets.empty()) break; - std::vector& first_set = node_sets[0]; + consume_nodes(rotation_sets, circ, depth_tracker); + if (rotation_sets.empty()) break; + std::vector& first_set = rotation_sets[0]; // get nodes with min cost std::vector min_nodes_indices = {0}; unsigned min_cost = first_set[0]->tqe_cost(); @@ -529,7 +501,7 @@ static void pauli_exps_synthesis( for (const TQE& tqe : tqe_candidates) { tqe_candidates_cost.insert( {tqe, - {default_pauliexp_tqe_cost(discount_rate, node_sets, rows, tqe), + {default_pauliexp_tqe_cost(discount_rate, rotation_sets, rows, tqe), static_cast(depth_tracker.gate_depth( std::get<1>(tqe), std::get<2>(tqe)))}}); } @@ -539,7 +511,7 @@ static void pauli_exps_synthesis( apply_tqe_to_circ(selected_tqe, circ); depth_tracker.add_2q_gate( std::get<1>(selected_tqe), std::get<2>(selected_tqe)); - for (std::vector& rotation_set : node_sets) { + for (std::vector& rotation_set : rotation_sets) { for (PauliNode_ptr& node : rotation_set) { node->update(selected_tqe); } @@ -585,11 +557,12 @@ Circuit greedy_pauli_graph_synthesis( rev_unit_map.insert({pair.second, pair.first}); } GPGraph gpg(circ_flat); - auto [node_sets, rows, measures] = gpg.get_sequence(); + auto [rotation_sets, rows, measures] = gpg.get_sequence(); DepthTracker depth_tracker(n_qubits); // synthesise Pauli exps pauli_exps_synthesis( - node_sets, rows, new_circ, discount_rate, depth_weight, depth_tracker); + rotation_sets, rows, new_circ, discount_rate, depth_weight, + depth_tracker); // synthesise the tableau tableau_row_nodes_synthesis(rows, new_circ, depth_weight, depth_tracker); for (auto it = measures.begin(); it != measures.end(); ++it) { @@ -606,8 +579,6 @@ Transform greedy_pauli_optimisation(double discount_rate, double depth_weight) { return Transform([discount_rate, depth_weight](Circuit& circ) { circ = GreedyPauliSimp::greedy_pauli_graph_synthesis( circ, discount_rate, depth_weight); - // decompose conditional circ boxes - circ.decompose_boxes_recursively(); singleq_clifford_sweep().apply(circ); return true; }); diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index b31dbda5c0..3da4cb6161 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -252,8 +252,11 @@ SCENARIO("Complete synthesis") { circ.add_conditional_gate(OpType::Rz, {0.5}, {0}, {0}, 0); circ.add_op(OpType::CX, {0, 1}); Circuit d(2, 2); - d.add_conditional_gate(OpType::Sdg, {}, {0}, {0}, 0); - d.add_conditional_gate(OpType::Z, {}, {0}, {0}, 0); + Op_ptr cond = std::make_shared( + std::make_shared( + SymPauliTensor({Pauli::Z, Pauli::I}, 0.5)), + 1, 0); + d.add_op(cond, {0, 0, 1}); REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } @@ -270,13 +273,18 @@ SCENARIO("Complete synthesis") { Circuit d(2, 1); d.add_op(OpType::CY, {1, 0}); d.add_op(OpType::Rx, 0.12, {0}); - d.add_conditional_gate(OpType::Sdg, {}, {0}, {0}, 0); - d.add_conditional_gate(OpType::Z, {}, {0}, {0}, 0); + Op_ptr cond2 = std::make_shared( + std::make_shared( + SymPauliTensor({Pauli::Z, Pauli::I}, 0.5)), + 1, 0); + d.add_op(cond2, {0, 0, 1}); d.add_op(OpType::CY, {1, 0}); REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } GIVEN("Circuit with conditional gates and measures") { + PauliExpBox pb1(SymPauliTensor({Pauli::Z, Pauli::X}, 0.12)); + PauliExpBox pb2(SymPauliTensor({Pauli::X, Pauli::Z}, 0.3)); Circuit circ(2, 2); Op_ptr cond1 = std::make_shared( std::make_shared( @@ -293,28 +301,10 @@ SCENARIO("Complete synthesis") { // can commute to the front circ.add_op(cond1, {1, 0, 1}); Circuit d(2, 2); - // ZZ 0.5 - d.add_conditional_gate(OpType::CX, {}, {0, 1}, {0}, 0); - d.add_conditional_gate(OpType::Sdg, {}, {0}, {0}, 0); - d.add_conditional_gate(OpType::V, {}, {1}, {0}, 0); - d.add_conditional_gate(OpType::Z, {}, {0}, {0}, 0); - // ZZ 0.5 conditioned on bit(1) - d.add_conditional_gate(OpType::CX, {}, {0, 1}, {1}, 0); - d.add_conditional_gate(OpType::Sdg, {}, {0}, {1}, 0); - d.add_conditional_gate(OpType::V, {}, {1}, {1}, 0); - d.add_conditional_gate(OpType::Z, {}, {0}, {1}, 0); + d.add_op(cond1, {0, 0, 1}); + d.add_op(cond1, {1, 0, 1}); d.add_op(OpType::Measure, {0, 0}); - // XZ 0.25 - // compute - d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); - d.add_conditional_gate(OpType::CX, {}, {0, 1}, {1}, 0); - d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); - // action - d.add_conditional_gate(OpType::Rz, {0.25}, {1}, {1}, 0); - // uncompute - d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); - d.add_conditional_gate(OpType::CX, {}, {0, 1}, {1}, 0); - d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); + d.add_op(cond2, {1, 0, 1}); REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } From 5423b711afc28c6486b9c06f555bf817d377412f Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Fri, 11 Oct 2024 10:42:15 +0100 Subject: [PATCH 34/57] Optimise conditional handling --- .../GreedyPauliOptimisation.hpp | 72 +++++++++++++++- .../Transformations/GreedyPauliConverters.cpp | 85 ++++++++++++------- tket/src/Transformations/GreedyPauliOps.cpp | 70 ++++++++++++++- .../GreedyPauliOptimisation.cpp | 41 ++++++--- tket/test/src/test_GreedyPauli.cpp | 63 ++++++++++---- 5 files changed, 265 insertions(+), 66 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 2da01eed33..fe11dcbb31 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -54,6 +54,8 @@ enum class PauliNodeType { PauliPropagation, // Pauli rotation with classical control ConditionalPauliRotation, + // Conditional Pauli rotations + ConditionalBlock, // Classical operation ClassicalNode, // Mid-circuit measurement @@ -300,7 +302,7 @@ class PauliRotation : public SingleNode { * @param string the Pauli string * @param theta the rotation angle in half-turns */ - PauliRotation(std::vector string, Expr theta); + PauliRotation(std::vector string, bool sign, Expr theta); PauliNodeType get_type() const override { return PauliNodeType::PauliRotation; @@ -367,6 +369,70 @@ class ConditionalPauliRotation : public PauliRotation { const unsigned cond_value_; }; +/** + * @brief Conditional block for rotations + */ +class ConditionalBlock : public PauliNode { + public: + /** + * @brief Construct a new Conditional Block object + * + * @param rotations Pauli rotations + * @param cond_bits conditional bits + * @param cond_value conditional value + */ + ConditionalBlock( + std::vector, bool, Expr>> rotations, + std::vector cond_bits, unsigned cond_value); + + /** + * @brief Sum of tqe_cost for each Pauli rotation + * + * @return unsigned + */ + unsigned tqe_cost() const override; + + /** + * @brief Sum of tqe_cost for each Pauli rotation after the given TQE is + * applied + * + * @return unsigned + */ + int tqe_cost_increase(const TQE& tqe) const override; + + /** + * @brief Update the all Pauli rotations with the given TQE + * + * @param tqe + */ + void update(const TQE& tqe) override; + + std::vector reduction_tqes() const override { return {}; }; + + std::vector cond_bits() const { return cond_bits_; }; + unsigned cond_value() const { return cond_value_; }; + + PauliNodeType get_type() const override { + return PauliNodeType::ConditionalBlock; + }; + + CommuteInfo get_commute_info() const override; + + void append(const ConditionalBlock& other); + + const std::vector, bool, Expr>>& rotations() + const { + return rotations_; + }; + + protected: + std::vector, bool, Expr>> rotations_; + const std::vector cond_bits_; + const unsigned cond_value_; + // extra cached data used by greedy synthesis + unsigned total_weight_; +}; + /** * @brief Defines how a Pauli X and a Pauli Z on the same qubit * get propagated from right to left through a Clifford operator. @@ -505,8 +571,8 @@ class GPGraph { * If the angle is non-Clifford or if conditional is true then add to the DAG * as a PauliRotation node, otherwise update the tableau. */ - void apply_pauli_at_end( - const std::vector& paulis, const Expr& angle, + void apply_paulis_at_end( + const std::vector, Expr>>& rotations, const qubit_vector_t& qbs, bool conditional = false, std::vector cond_bits = {}, unsigned cond_value = 0); diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp index 3d92dcdb46..86cbdc56db 100644 --- a/tket/src/Transformations/GreedyPauliConverters.cpp +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -55,7 +55,7 @@ gpg_from_unordered_set(const std::vector& unordered_set) { for (auto& pauli : unordered_set) { TKET_ASSERT(pauli.string.size() == n_qubits); rotation_set.push_back( - std::make_shared(pauli.string, pauli.coeff)); + std::make_shared(pauli.string, true, pauli.coeff)); } UnitaryRevTableau tab(n_qubits); std::vector rows = get_nodes_from_tableau(tab, n_qubits); @@ -169,6 +169,22 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) { if (!ready) continue; // Check if we can commute past it PauliNode_ptr compare_node = graph_[to_compare]; + // merge two ConditionalBlocks if they share the same condition + // this sacrifices the ability to commute the node but can group operations + // for better optimisation + if (node->get_type() == PauliNodeType::ConditionalBlock && + compare_node->get_type() == PauliNodeType::ConditionalBlock) { + const ConditionalBlock& block1 = + dynamic_cast(*node); + ConditionalBlock& block2 = dynamic_cast(*compare_node); + if (block1.cond_bits() == block2.cond_bits() && + block1.cond_value() == block2.cond_value()) { + block2.append(block1); + boost::clear_vertex(new_vert, graph_); + boost::remove_vertex(new_vert, graph_); + return; + } + } if (nodes_commute(node, compare_node)) { // Check if two pauli rotations can be merged if (node->get_type() == PauliNodeType::PauliRotation && @@ -194,8 +210,8 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) { boost::clear_vertex(to_compare, graph_); boost::remove_vertex(to_compare, graph_); } else { - graph_[to_compare] = - std::make_shared(rot1.string(), merged_angle); + graph_[to_compare] = std::make_shared( + rot1.string(), true, merged_angle); } return; } @@ -214,36 +230,46 @@ void GPGraph::apply_node_at_end(PauliNode_ptr& node) { if (get_predecessors(new_vert).empty()) start_line_.insert(new_vert); } -void GPGraph::apply_pauli_at_end( - const std::vector& paulis, const Expr& angle, +void GPGraph::apply_paulis_at_end( + const std::vector, Expr>>& rotations, const qubit_vector_t& qbs, bool conditional, std::vector cond_bits, unsigned cond_value) { - // Note that global phase is ignored - if (static_cast(std::count( - paulis.begin(), paulis.end(), Pauli::I)) == paulis.size()) { - return; - } - std::optional cliff_angle = equiv_Clifford(angle); - if (cliff_angle && cliff_angle.value() == 0) { - return; - } - QubitPauliMap qpm; - for (unsigned i = 0; i != qbs.size(); ++i) - qpm.insert({Qubit(qbs[i]), paulis[i]}); - if (cliff_angle && !conditional) { - cliff_.apply_pauli_at_end(SpPauliStabiliser(qpm), *cliff_angle); - return; + std::vector, bool, Expr>> conj_rotations; + for (const auto& pair : rotations) { + const std::vector& paulis = pair.first; + const Expr& angle = pair.second; + // Note that global phase is ignored + if (static_cast(std::count( + paulis.begin(), paulis.end(), Pauli::I)) == paulis.size()) + continue; + std::optional cliff_angle = equiv_Clifford(angle); + if (cliff_angle && cliff_angle.value() == 0) continue; + QubitPauliMap qpm; + for (unsigned i = 0; i != qbs.size(); ++i) + qpm.insert({Qubit(qbs[i]), paulis[i]}); + if (cliff_angle && !conditional) { + cliff_.apply_pauli_at_end(SpPauliStabiliser(qpm), *cliff_angle); + continue; + } + // if not clifford we conjugate the string with the end-circuit tableau + SpPauliStabiliser qpt = cliff_.get_row_product(SpPauliStabiliser(qpm)); + auto [pauli_dense, theta] = dense_pauli(qpt, n_qubits_, angle); + conj_rotations.push_back({pauli_dense, true, theta}); } - SpPauliStabiliser qpt = cliff_.get_row_product(SpPauliStabiliser(qpm)); - auto [pauli_dense, theta] = dense_pauli(qpt, n_qubits_, angle); - PauliNode_ptr node; + + // if conditional we add a ConditionalBlock otherwise we add individual + // rotations. if (conditional) { - node = std::make_shared( - pauli_dense, theta, cond_bits, cond_value); + PauliNode_ptr node = std::make_shared( + conj_rotations, cond_bits, cond_value); + apply_node_at_end(node); } else { - node = std::make_shared(pauli_dense, theta); + for (const auto& t : conj_rotations) { + PauliNode_ptr node = std::make_shared( + std::get<0>(t), std::get<1>(t), std::get<2>(t)); + apply_node_at_end(node); + } } - apply_node_at_end(node); } void GPGraph::apply_gate_at_end( @@ -445,10 +471,7 @@ void GPGraph::apply_gate_at_end( throw BadOpType("GreedyPauliSimp doesn't support", type); } } - for (auto it = pauli_rots.begin(); it != pauli_rots.end(); ++it) { - apply_pauli_at_end( - it->first, it->second, qbs, conditional, cond_bits, cond_value); - } + apply_paulis_at_end(pauli_rots, qbs, conditional, cond_bits, cond_value); } std::vector GPGraph::vertices_in_order() const { diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index 8a40db259c..43bc100687 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -288,8 +288,8 @@ std::tuple ACPairNode::first_support() const { } // PauliRotation -PauliRotation::PauliRotation(std::vector string, Expr theta) - : SingleNode(string, true), theta_(theta) {} +PauliRotation::PauliRotation(std::vector string, bool sign, Expr theta) + : SingleNode(string, sign), theta_(theta) {} CommuteInfo PauliRotation::get_commute_info() const { return {{string_}, {}}; } @@ -297,7 +297,7 @@ CommuteInfo PauliRotation::get_commute_info() const { return {{string_}, {}}; } ConditionalPauliRotation::ConditionalPauliRotation( std::vector string, Expr theta, std::vector cond_bits, unsigned cond_value) - : PauliRotation(string, theta), + : PauliRotation(string, true, theta), cond_bits_(cond_bits), cond_value_(cond_value) {} @@ -309,6 +309,70 @@ CommuteInfo ConditionalPauliRotation::get_commute_info() const { return {{string_}, bits_info}; } +ConditionalBlock::ConditionalBlock( + std::vector, bool, Expr>> rotations, + std::vector cond_bits, unsigned cond_value) + : rotations_(rotations), cond_bits_(cond_bits), cond_value_(cond_value) { + total_weight_ = 0; + for (const std::tuple, bool, Expr>& rot : rotations_) { + total_weight_ += + std::get<0>(rot).size() - + std::count(std::get<0>(rot).begin(), std::get<0>(rot).end(), Pauli::I); + } +} + +unsigned ConditionalBlock::tqe_cost() const { return total_weight_ - 1; } + +int ConditionalBlock::tqe_cost_increase(const TQE& tqe) const { + int total_increase = 0; + for (const std::tuple, bool, Expr>& rot : rotations_) { + auto [g, a, b] = tqe; + Pauli p0 = std::get<0>(rot)[a]; + Pauli p1 = std::get<0>(rot)[b]; + auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); + total_increase += (p0 == Pauli::I) + (p1 == Pauli::I) - + (new_p0 == Pauli::I) - (new_p1 == Pauli::I); + } + return total_increase; +} + +void ConditionalBlock::update(const TQE& tqe) { + for (std::tuple, bool, Expr>& rot : rotations_) { + auto [g, a, b] = tqe; + Pauli p0 = std::get<0>(rot)[a]; + Pauli p1 = std::get<0>(rot)[b]; + auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); + std::get<0>(rot)[a] = new_p0; + std::get<0>(rot)[b] = new_p1; + total_weight_ += (p0 == Pauli::I) + (p1 == Pauli::I) - + (new_p0 == Pauli::I) - (new_p1 == Pauli::I); + if (!sign) { + std::get<1>(rot) = !std::get<1>(rot); + } + } +} + +CommuteInfo ConditionalBlock::get_commute_info() const { + std::vector> bits_info; + for (unsigned b : cond_bits_) { + bits_info.push_back({Bit(b), BitType::READ}); + } + std::vector> strings; + for (const std::tuple, bool, Expr>& rot : rotations_) { + strings.push_back(std::get<0>(rot)); + } + return {strings, bits_info}; +} + +void ConditionalBlock::append(const ConditionalBlock& other) { + for (const auto& rot : other.rotations()) { + rotations_.push_back(rot); + total_weight_ += + std::get<0>(rot).size() - + std::count(std::get<0>(rot).begin(), std::get<0>(rot).end(), Pauli::I); + } +} + // PauliPropagation PauliPropagation::PauliPropagation( std::vector z_string, std::vector x_string, bool z_sign, diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 9c364717a7..008603cd25 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -18,7 +18,7 @@ #include "tket/Circuit/PauliExpBoxes.hpp" #include "tket/OpType/OpType.hpp" -#include "tket/Transformations/CliffordOptimisation.hpp" +#include "tket/Transformations/CliffordReductionPass.hpp" #include "tket/Transformations/GreedyPauliOptimisationLookupTables.hpp" #include "tket/Transformations/Transform.hpp" @@ -409,16 +409,32 @@ static void consume_nodes( first_set.erase(first_set.begin() + i); break; } - case PauliNodeType::ConditionalPauliRotation: { - // conditionals are added as conditional PauliExpBoxes - ConditionalPauliRotation& node = - dynamic_cast(*node_ptr); + case PauliNodeType::ConditionalBlock: { + // conditionals are implemented as a conditioned sequence of + // PauliExpBoxes and subsequently optimised by recursively calling + // greedy_pauli_optimisation + ConditionalBlock& node = dynamic_cast(*node_ptr); + const std::vector cond_bits = node.cond_bits(); + const unsigned cond_value = node.cond_value(); + std::vector qubits; + for (unsigned i = 0; i < circ.n_qubits(); i++) { + qubits.push_back(i); + } + Circuit cond_circ(circ.n_qubits()); + for (const auto& t : node.rotations()) { + const std::vector& string = std::get<0>(t); + bool sign = std::get<1>(t); + Expr angle = sign ? std::get<2>(t) : -std::get<2>(t); + Op_ptr peb_op = + std::make_shared(SymPauliTensor(string, angle)); + cond_circ.add_op(peb_op, qubits); + } + greedy_pauli_optimisation().apply(cond_circ); Op_ptr cond = std::make_shared( - std::make_shared( - SymPauliTensor(node.string(), node.angle())), - (unsigned)node.cond_bits().size(), node.cond_value()); - std::vector args = node.cond_bits(); - for (unsigned i = 0; i < node.string().size(); i++) { + std::make_shared(cond_circ), cond_bits.size(), + cond_value); + std::vector args = cond_bits; + for (unsigned i = 0; i < cond_circ.n_qubits(); i++) { args.push_back(i); } circ.add_op(cond, args); @@ -579,7 +595,10 @@ Transform greedy_pauli_optimisation(double discount_rate, double depth_weight) { return Transform([discount_rate, depth_weight](Circuit& circ) { circ = GreedyPauliSimp::greedy_pauli_graph_synthesis( circ, discount_rate, depth_weight); - singleq_clifford_sweep().apply(circ); + // use clifford_reduction to merge single qubit Clifford gates + clifford_reduction().apply(circ); + // decompose the conditional CircBoxes + circ.decompose_boxes_recursively(); return true; }); } diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index 3da4cb6161..3cc4ec73e3 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -256,7 +256,8 @@ SCENARIO("Complete synthesis") { std::make_shared( SymPauliTensor({Pauli::Z, Pauli::I}, 0.5)), 1, 0); - d.add_op(cond, {0, 0, 1}); + d.add_conditional_gate(OpType::Sdg, {}, {0}, {0}, 0); + d.add_conditional_gate(OpType::Z, {}, {0}, {0}, 0); REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } @@ -273,18 +274,13 @@ SCENARIO("Complete synthesis") { Circuit d(2, 1); d.add_op(OpType::CY, {1, 0}); d.add_op(OpType::Rx, 0.12, {0}); - Op_ptr cond2 = std::make_shared( - std::make_shared( - SymPauliTensor({Pauli::Z, Pauli::I}, 0.5)), - 1, 0); - d.add_op(cond2, {0, 0, 1}); + d.add_conditional_gate(OpType::Sdg, {}, {0}, {0}, 0); + d.add_conditional_gate(OpType::Z, {}, {0}, {0}, 0); d.add_op(OpType::CY, {1, 0}); REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } GIVEN("Circuit with conditional gates and measures") { - PauliExpBox pb1(SymPauliTensor({Pauli::Z, Pauli::X}, 0.12)); - PauliExpBox pb2(SymPauliTensor({Pauli::X, Pauli::Z}, 0.3)); Circuit circ(2, 2); Op_ptr cond1 = std::make_shared( std::make_shared( @@ -292,27 +288,58 @@ SCENARIO("Complete synthesis") { 1, 0); Op_ptr cond2 = std::make_shared( std::make_shared( - SymPauliTensor({Pauli::X, Pauli::Z}, 0.25)), + SymPauliTensor({Pauli::Z, Pauli::Y}, 0.12)), 1, 0); circ.add_op(cond1, {0, 0, 1}); circ.add_op(OpType::Measure, {0, 0}); - // doesn't commute with measure - circ.add_op(cond2, {1, 0, 1}); // can commute to the front - circ.add_op(cond1, {1, 0, 1}); + circ.add_op(cond2, {1, 0, 1}); Circuit d(2, 2); - d.add_op(cond1, {0, 0, 1}); - d.add_op(cond1, {1, 0, 1}); + // ZX 0.5 + d.add_conditional_gate(OpType::CX, {}, {0, 1}, {0}, 0); + d.add_conditional_gate(OpType::Sdg, {}, {0}, {0}, 0); + d.add_conditional_gate(OpType::V, {}, {1}, {0}, 0); + d.add_conditional_gate(OpType::Z, {}, {0}, {0}, 0); d.add_op(OpType::Measure, {0, 0}); - d.add_op(cond2, {1, 0, 1}); + // ZY 0.12 + // compute + d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); + d.add_conditional_gate(OpType::CY, {}, {0, 1}, {1}, 0); + d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); + // action + d.add_conditional_gate(OpType::Rz, {0.12}, {0}, {1}, 0); + // uncompute + d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); + d.add_conditional_gate(OpType::CY, {}, {0, 1}, {1}, 0); + d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + REQUIRE(circ == d); + } + + GIVEN("Conditionals merging") { + Circuit circ(2, 2); + Op_ptr cond1 = std::make_shared( + std::make_shared( + SymPauliTensor({Pauli::Z, Pauli::X}, 0.25)), + 1, 0); + Op_ptr cond2 = std::make_shared( + std::make_shared( + SymPauliTensor({Pauli::Z, Pauli::X}, -0.25)), + 1, 0); + circ.add_op(cond1, {0, 0, 1}); + circ.add_op(OpType::Rz, 0.3, {0}); + circ.add_op(cond2, {0, 0, 1}); + circ.add_conditional_gate(OpType::CX, {}, {0, 1}, {0}, 0); + circ.add_conditional_gate(OpType::CX, {}, {0, 1}, {0}, 0); + circ.add_op(OpType::Rz, -0.3, {0}); + // should all be canceled + Circuit d(2, 2); REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } GIVEN("Circuit with classical gates") { Circuit circ(1, 4); - circ.add_op(OpType::S, {0}); - circ.add_op(OpType::V, {0}); - circ.add_op(OpType::S, {0}); + circ.add_op(OpType::H, {0}); circ.add_op(ClassicalX(), {1}); circ.add_op(ClassicalCX(), {0, 1}); circ.add_op(AndWithOp(), {2, 3}); From 6e14c7d90b49781200f39935dada89b19f89de12 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Fri, 11 Oct 2024 10:43:58 +0100 Subject: [PATCH 35/57] remove ConditionalPauliRotation --- .../GreedyPauliOptimisation.hpp | 33 ------------------- tket/src/Transformations/GreedyPauliOps.cpp | 16 --------- 2 files changed, 49 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index fe11dcbb31..34a05ee314 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -52,8 +52,6 @@ enum class PauliNodeType { // Defines how a Pauli X and a Pauli Z on the same qubit // get propagated from right to left through a Clifford operator. PauliPropagation, - // Pauli rotation with classical control - ConditionalPauliRotation, // Conditional Pauli rotations ConditionalBlock, // Classical operation @@ -338,37 +336,6 @@ class MidMeasure : public SingleNode { const unsigned bit_; }; -/** - * @brief Conditional Pauli rotation - */ -class ConditionalPauliRotation : public PauliRotation { - public: - /** - * @brief Construct a new Conditional Pauli Rotation object - * - * @param string the Pauli string - * @param theta the rotation angle in half-turns - * @param cond_bits conditional bits - * @param cond_value conditional value - */ - ConditionalPauliRotation( - std::vector string, Expr theta, std::vector cond_bits, - unsigned cond_value); - - PauliNodeType get_type() const override { - return PauliNodeType::ConditionalPauliRotation; - }; - - CommuteInfo get_commute_info() const override; - - std::vector cond_bits() const { return cond_bits_; }; - unsigned cond_value() const { return cond_value_; }; - - protected: - const std::vector cond_bits_; - const unsigned cond_value_; -}; - /** * @brief Conditional block for rotations */ diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index 43bc100687..0b58983917 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -293,22 +293,6 @@ PauliRotation::PauliRotation(std::vector string, bool sign, Expr theta) CommuteInfo PauliRotation::get_commute_info() const { return {{string_}, {}}; } -// ConditionalPauliRotation -ConditionalPauliRotation::ConditionalPauliRotation( - std::vector string, Expr theta, std::vector cond_bits, - unsigned cond_value) - : PauliRotation(string, true, theta), - cond_bits_(cond_bits), - cond_value_(cond_value) {} - -CommuteInfo ConditionalPauliRotation::get_commute_info() const { - std::vector> bits_info; - for (unsigned b : cond_bits_) { - bits_info.push_back({Bit(b), BitType::READ}); - } - return {{string_}, bits_info}; -} - ConditionalBlock::ConditionalBlock( std::vector, bool, Expr>> rotations, std::vector cond_bits, unsigned cond_value) From e3b85a30119de80b751d6bc92763042bf5ffa4ea Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Fri, 11 Oct 2024 10:51:26 +0100 Subject: [PATCH 36/57] Remove clifford reduction and pass parameters when synthesis conditionals --- tket/src/Transformations/GreedyPauliOptimisation.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 008603cd25..a3c968b2b7 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -18,7 +18,6 @@ #include "tket/Circuit/PauliExpBoxes.hpp" #include "tket/OpType/OpType.hpp" -#include "tket/Transformations/CliffordReductionPass.hpp" #include "tket/Transformations/GreedyPauliOptimisationLookupTables.hpp" #include "tket/Transformations/Transform.hpp" @@ -316,7 +315,7 @@ static void tableau_row_nodes_synthesis( */ static void consume_nodes( std::vector>& rotation_sets, Circuit& circ, - DepthTracker& depth_tracker) { + DepthTracker& depth_tracker, double discount_rate, double depth_weight) { if (rotation_sets.empty()) { return; } @@ -429,7 +428,8 @@ static void consume_nodes( std::make_shared(SymPauliTensor(string, angle)); cond_circ.add_op(peb_op, qubits); } - greedy_pauli_optimisation().apply(cond_circ); + greedy_pauli_optimisation(discount_rate, depth_weight) + .apply(cond_circ); Op_ptr cond = std::make_shared( std::make_shared(cond_circ), cond_bits.size(), cond_value); @@ -491,7 +491,8 @@ static void pauli_exps_synthesis( std::vector& rows, Circuit& circ, double discount_rate, double depth_weight, DepthTracker& depth_tracker) { while (true) { - consume_nodes(rotation_sets, circ, depth_tracker); + consume_nodes( + rotation_sets, circ, depth_tracker, discount_rate, depth_weight); if (rotation_sets.empty()) break; std::vector& first_set = rotation_sets[0]; // get nodes with min cost @@ -595,8 +596,6 @@ Transform greedy_pauli_optimisation(double discount_rate, double depth_weight) { return Transform([discount_rate, depth_weight](Circuit& circ) { circ = GreedyPauliSimp::greedy_pauli_graph_synthesis( circ, discount_rate, depth_weight); - // use clifford_reduction to merge single qubit Clifford gates - clifford_reduction().apply(circ); // decompose the conditional CircBoxes circ.decompose_boxes_recursively(); return true; From 0b46746e43f741b1266336103302b6616b46fdd9 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Fri, 11 Oct 2024 14:11:52 +0100 Subject: [PATCH 37/57] Seeded tie breaking --- .../GreedyPauliOptimisation.hpp | 11 ++- .../GreedyPauliOptimisation.cpp | 79 ++++++++++++------- 2 files changed, 59 insertions(+), 31 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 34a05ee314..96d03dce73 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -586,10 +586,12 @@ gpg_from_unordered_set(const std::vector& unordered_set); * @param circ * @param discount_rate * @param depth_weight + * @param seed * @return Circuit */ Circuit greedy_pauli_graph_synthesis( - const Circuit& circ, double discount_rate = 0.7, double depth_weight = 0.3); + const Circuit& circ, double discount_rate = 0.7, double depth_weight = 0.3, + unsigned seed = 0); /** * @brief Synthesise a set of unordered Pauli exponentials by applying Clifford @@ -598,16 +600,17 @@ Circuit greedy_pauli_graph_synthesis( * * @param unordered_set * @param depth_weight + * @param seed * @return Circuit */ Circuit greedy_pauli_set_synthesis( - const std::vector& unordered_set, - double depth_weight = 0.3); + const std::vector& unordered_set, double depth_weight = 0.3, + unsigned seed = 0); } // namespace GreedyPauliSimp Transform greedy_pauli_optimisation( - double discount_rate = 0.7, double depth_weight = 0.3); + double discount_rate = 0.7, double depth_weight = 0.3, unsigned seed = 0); } // namespace Transforms diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index a3c968b2b7..818c433588 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -15,6 +15,7 @@ #include "tket/Transformations/GreedyPauliOptimisation.hpp" #include +#include #include "tket/Circuit/PauliExpBoxes.hpp" #include "tket/OpType/OpType.hpp" @@ -27,6 +28,17 @@ namespace Transforms { namespace GreedyPauliSimp { +template +static typename Container::const_iterator sample_random_element( + const Container& container, const unsigned seed) { + std::mt19937 rng(seed); + std::uniform_int_distribution dist(0, container.size() - 1); + size_t random_index = dist(rng); + auto it = container.begin(); + std::advance(it, random_index); + return it; +} + static void apply_tqe_to_circ(const TQE& tqe, Circuit& circ) { auto [gate_type, a, b] = tqe; switch (gate_type) { @@ -105,7 +117,7 @@ static double default_pauliexp_tqe_cost( // weighted sum of minmax-normalised costs static TQE minmax_selection( const std::map>& tqe_candidates_cost, - const std::vector& weights) { + const std::vector& weights, unsigned seed) { TKET_ASSERT(tqe_candidates_cost.size() > 0); size_t n_costs = tqe_candidates_cost.begin()->second.size(); TKET_ASSERT(n_costs == weights.size()); @@ -132,26 +144,29 @@ static TQE minmax_selection( } // if all have the same cost, return the first one if (valid_indices.size() == 0) { - TQE min_tqe = tqe_candidates_cost.begin()->first; - return min_tqe; + auto it = sample_random_element(tqe_candidates_cost, seed); + return it->first; } // if only one cost variable, no need to normalise if (valid_indices.size() == 1) { auto it = tqe_candidates_cost.begin(); double min_cost = it->second[valid_indices[0]]; - TQE min_tqe = it->first; + std::vector min_tqes = {it->first}; for (; it != tqe_candidates_cost.end(); it++) { if (it->second[valid_indices[0]] < min_cost) { - min_tqe = it->first; + min_tqes = {it->first}; min_cost = it->second[valid_indices[0]]; + } else if (it->second[valid_indices[0]] == min_cost) { + min_tqes.push_back(it->first); } } - return min_tqe; + auto sampled_it = sample_random_element(min_tqes, seed); + return *sampled_it; } // find the tqe with the minimum normalised cost auto it = tqe_candidates_cost.begin(); double min_cost = 0; - TQE min_tqe = it->first; + std::vector min_tqes = {it->first}; // initialise min_cost for (const auto& cost_index : valid_indices) { min_cost += weights[cost_index] * @@ -169,22 +184,25 @@ static TQE minmax_selection( } if (cost < min_cost) { min_cost = cost; - min_tqe = it->first; + min_tqes = {it->first}; + } else if (cost == min_cost) { + min_tqes.push_back(it->first); } } - return min_tqe; + auto sampled_it = sample_random_element(min_tqes, seed); + return *sampled_it; } static TQE select_pauliexp_tqe( const std::map>& tqe_candidates_cost, - double depth_weight) { - return minmax_selection(tqe_candidates_cost, {1, depth_weight}); + double depth_weight, unsigned seed) { + return minmax_selection(tqe_candidates_cost, {1, depth_weight}, seed); } static TQE select_tableau_tqe( const std::map>& tqe_candidates_cost, - double depth_weight) { - return minmax_selection(tqe_candidates_cost, {1, depth_weight}); + double depth_weight, unsigned seed) { + return minmax_selection(tqe_candidates_cost, {1, depth_weight}, seed); } // simple struct that tracks the depth on each qubit @@ -217,7 +235,7 @@ struct DepthTracker { */ static void tableau_row_nodes_synthesis( std::vector& rows, Circuit& circ, double depth_weight, - DepthTracker& depth_tracker) { + DepthTracker& depth_tracker, unsigned seed) { // only consider nodes with a non-zero cost std::vector remaining_indices; for (unsigned i = 0; i < rows.size(); i++) { @@ -260,7 +278,8 @@ static void tableau_row_nodes_synthesis( } TKET_ASSERT(tqe_candidates_cost.size() > 0); // select the best one - TQE selected_tqe = select_tableau_tqe(tqe_candidates_cost, depth_weight); + TQE selected_tqe = + select_tableau_tqe(tqe_candidates_cost, depth_weight, seed); // apply TQE apply_tqe_to_circ(selected_tqe, circ); // update depth tracker @@ -489,7 +508,7 @@ static void consume_nodes( static void pauli_exps_synthesis( std::vector>& rotation_sets, std::vector& rows, Circuit& circ, double discount_rate, - double depth_weight, DepthTracker& depth_tracker) { + double depth_weight, DepthTracker& depth_tracker, unsigned seed) { while (true) { consume_nodes( rotation_sets, circ, depth_tracker, discount_rate, depth_weight); @@ -523,7 +542,8 @@ static void pauli_exps_synthesis( std::get<1>(tqe), std::get<2>(tqe)))}}); } // select the best one - TQE selected_tqe = select_pauliexp_tqe(tqe_candidates_cost, depth_weight); + TQE selected_tqe = + select_pauliexp_tqe(tqe_candidates_cost, depth_weight, seed); // apply TQE apply_tqe_to_circ(selected_tqe, circ); depth_tracker.add_2q_gate( @@ -540,7 +560,8 @@ static void pauli_exps_synthesis( } Circuit greedy_pauli_set_synthesis( - const std::vector& unordered_set, double depth_weight) { + const std::vector& unordered_set, double depth_weight, + unsigned seed) { if (unordered_set.size() == 0) { return Circuit(); } @@ -550,15 +571,17 @@ Circuit greedy_pauli_set_synthesis( std::vector> rotation_sets{rotation_set}; DepthTracker depth_tracker(n_qubits); // synthesise Pauli exps - pauli_exps_synthesis(rotation_sets, rows, c, 0, depth_weight, depth_tracker); + pauli_exps_synthesis( + rotation_sets, rows, c, 0, depth_weight, depth_tracker, seed); // synthesise the tableau - tableau_row_nodes_synthesis(rows, c, depth_weight, depth_tracker); + tableau_row_nodes_synthesis(rows, c, depth_weight, depth_tracker, seed); c.replace_SWAPs(); return c; } Circuit greedy_pauli_graph_synthesis( - const Circuit& circ, double discount_rate, double depth_weight) { + const Circuit& circ, double discount_rate, double depth_weight, + unsigned seed) { Circuit circ_flat(circ); unsigned n_qubits = circ_flat.n_qubits(); unsigned n_bits = circ_flat.n_bits(); @@ -578,10 +601,11 @@ Circuit greedy_pauli_graph_synthesis( DepthTracker depth_tracker(n_qubits); // synthesise Pauli exps pauli_exps_synthesis( - rotation_sets, rows, new_circ, discount_rate, depth_weight, - depth_tracker); + rotation_sets, rows, new_circ, discount_rate, depth_weight, depth_tracker, + seed); // synthesise the tableau - tableau_row_nodes_synthesis(rows, new_circ, depth_weight, depth_tracker); + tableau_row_nodes_synthesis( + rows, new_circ, depth_weight, depth_tracker, seed); for (auto it = measures.begin(); it != measures.end(); ++it) { new_circ.add_measure(it->left, it->right); } @@ -592,10 +616,11 @@ Circuit greedy_pauli_graph_synthesis( } // namespace GreedyPauliSimp -Transform greedy_pauli_optimisation(double discount_rate, double depth_weight) { - return Transform([discount_rate, depth_weight](Circuit& circ) { +Transform greedy_pauli_optimisation( + double discount_rate, double depth_weight, unsigned seed) { + return Transform([discount_rate, depth_weight, seed](Circuit& circ) { circ = GreedyPauliSimp::greedy_pauli_graph_synthesis( - circ, discount_rate, depth_weight); + circ, discount_rate, depth_weight, seed); // decompose the conditional CircBoxes circ.decompose_boxes_recursively(); return true; From 1bfca5a58319b3cf42b4055be887066fa3c55a37 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Mon, 14 Oct 2024 10:38:26 +0100 Subject: [PATCH 38/57] Add limits to the search space --- .../GreedyPauliOptimisation.hpp | 10 +- .../GreedyPauliOptimisation.cpp | 99 +++++++++++++++---- tket/test/src/test_GreedyPauli.cpp | 69 +++++++++++++ 3 files changed, 156 insertions(+), 22 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 96d03dce73..8b97bcedb6 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -586,11 +586,14 @@ gpg_from_unordered_set(const std::vector& unordered_set); * @param circ * @param discount_rate * @param depth_weight + * @param max_lookahead + * @param max_tqe_candidates * @param seed * @return Circuit */ Circuit greedy_pauli_graph_synthesis( const Circuit& circ, double discount_rate = 0.7, double depth_weight = 0.3, + unsigned max_lookahead = 500, unsigned max_tqe_candidates = 500, unsigned seed = 0); /** @@ -600,17 +603,22 @@ Circuit greedy_pauli_graph_synthesis( * * @param unordered_set * @param depth_weight + * @param max_lookahead + * @param max_tqe_candidates * @param seed * @return Circuit */ Circuit greedy_pauli_set_synthesis( const std::vector& unordered_set, double depth_weight = 0.3, + unsigned max_lookahead = 500, unsigned max_tqe_candidates = 500, unsigned seed = 0); } // namespace GreedyPauliSimp Transform greedy_pauli_optimisation( - double discount_rate = 0.7, double depth_weight = 0.3, unsigned seed = 0); + double discount_rate = 0.7, double depth_weight = 0.3, + unsigned max_lookahead = 500, unsigned max_tqe_candidates = 500, + unsigned seed = 0); } // namespace Transforms diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 818c433588..5607ed4cf5 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -30,7 +30,7 @@ namespace GreedyPauliSimp { template static typename Container::const_iterator sample_random_element( - const Container& container, const unsigned seed) { + const Container& container, unsigned seed) { std::mt19937 rng(seed); std::uniform_int_distribution dist(0, container.size() - 1); size_t random_index = dist(rng); @@ -39,6 +39,24 @@ static typename Container::const_iterator sample_random_element( return it; } +static std::vector sample_tqes( + const std::set& tqes, size_t k, unsigned seed) { + // https://stackoverflow.com/a/59090754 + size_t unsampled_sz = tqes.size(); + auto first = std::begin(tqes); + std::vector vec; + std::mt19937 rng(seed); + vec.reserve(std::min(k, unsampled_sz)); + for (k = std::min(k, unsampled_sz); k != 0; ++first) { + auto r = std::uniform_int_distribution(0, --unsampled_sz)(rng); + if (r < k) { + vec.push_back(*first); + --k; + } + } + return vec; +} + static void apply_tqe_to_circ(const TQE& tqe, Circuit& circ) { auto [gate_type, a, b] = tqe; switch (gate_type) { @@ -83,10 +101,13 @@ static void apply_tqe_to_circ(const TQE& tqe, Circuit& circ) { // return the sum of the cost increases on remaining tableau nodes static double default_tableau_tqe_cost( const std::vector& rows, - const std::vector& remaining_indices, const TQE& tqe) { + const std::vector& remaining_indices, const TQE& tqe, + unsigned max_lookahead) { double cost = 0; + unsigned count = 0; for (const unsigned& index : remaining_indices) { cost += rows[index]->tqe_cost_increase(tqe); + if (++count >= max_lookahead) break; } return cost; } @@ -96,19 +117,24 @@ static double default_tableau_tqe_cost( static double default_pauliexp_tqe_cost( const double discount_rate, const std::vector>& rotation_sets, - const std::vector& rows, const TQE& tqe) { + const std::vector& rows, const TQE& tqe, + const unsigned& max_lookahead) { double discount = 1 / (1 + discount_rate); double weight = 1; double exp_cost = 0; double tab_cost = 0; + unsigned count = 0; for (const std::vector& rotation_set : rotation_sets) { for (const PauliNode_ptr& node : rotation_set) { exp_cost += weight * node->tqe_cost_increase(tqe); + if (++count >= max_lookahead) break; } + if (count >= max_lookahead) break; weight *= discount; } for (const PauliNode_ptr& node : rows) { tab_cost += weight * node->tqe_cost_increase(tqe); + if (++count >= max_lookahead) break; } return exp_cost + tab_cost; } @@ -234,8 +260,9 @@ struct DepthTracker { * @brief Synthesise a vector of PauliPropagation */ static void tableau_row_nodes_synthesis( - std::vector& rows, Circuit& circ, double depth_weight, - DepthTracker& depth_tracker, unsigned seed) { + std::vector& rows, Circuit& circ, + DepthTracker& depth_tracker, double depth_weight, unsigned max_lookahead, + unsigned max_tqe_candidates, unsigned seed) { // only consider nodes with a non-zero cost std::vector remaining_indices; for (unsigned i = 0; i < rows.size(); i++) { @@ -265,14 +292,18 @@ static void tableau_row_nodes_synthesis( tqe_candidates.insert( node_reducing_tqes.begin(), node_reducing_tqes.end()); } + // sample + std::vector sampled_tqes = + sample_tqes(tqe_candidates, max_tqe_candidates, seed); // for each tqe we compute a vector of cost factors which will // be combined to make the final decision. // we currently only consider tqe_cost and gate_depth. std::map> tqe_candidates_cost; - for (const TQE& tqe : tqe_candidates) { + for (const TQE& tqe : sampled_tqes) { tqe_candidates_cost.insert( {tqe, - {default_tableau_tqe_cost(rows, remaining_indices, tqe), + {default_tableau_tqe_cost( + rows, remaining_indices, tqe, max_lookahead), static_cast(depth_tracker.gate_depth( std::get<1>(tqe), std::get<2>(tqe)))}}); } @@ -507,8 +538,9 @@ static void consume_nodes( */ static void pauli_exps_synthesis( std::vector>& rotation_sets, - std::vector& rows, Circuit& circ, double discount_rate, - double depth_weight, DepthTracker& depth_tracker, unsigned seed) { + std::vector& rows, Circuit& circ, + DepthTracker& depth_tracker, double discount_rate, double depth_weight, + unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed) { while (true) { consume_nodes( rotation_sets, circ, depth_tracker, discount_rate, depth_weight); @@ -532,12 +564,16 @@ static void pauli_exps_synthesis( tqe_candidates.insert( node_reducing_tqes.begin(), node_reducing_tqes.end()); } + // sample + std::vector sampled_tqes = + sample_tqes(tqe_candidates, max_tqe_candidates, seed); // for each tqe we compute costs which might subject to normalisation std::map> tqe_candidates_cost; - for (const TQE& tqe : tqe_candidates) { + for (const TQE& tqe : sampled_tqes) { tqe_candidates_cost.insert( {tqe, - {default_pauliexp_tqe_cost(discount_rate, rotation_sets, rows, tqe), + {default_pauliexp_tqe_cost( + discount_rate, rotation_sets, rows, tqe, max_lookahead), static_cast(depth_tracker.gate_depth( std::get<1>(tqe), std::get<2>(tqe)))}}); } @@ -561,7 +597,14 @@ static void pauli_exps_synthesis( Circuit greedy_pauli_set_synthesis( const std::vector& unordered_set, double depth_weight, - unsigned seed) { + unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed) { + if (max_lookahead == 0) { + throw GreedyPauliSimpError("max_lookahead must be greater than 0."); + } + if (max_tqe_candidates == 0) { + throw GreedyPauliSimpError("max_tqe_candidates must be greater than 0."); + } + if (unordered_set.size() == 0) { return Circuit(); } @@ -572,16 +615,26 @@ Circuit greedy_pauli_set_synthesis( DepthTracker depth_tracker(n_qubits); // synthesise Pauli exps pauli_exps_synthesis( - rotation_sets, rows, c, 0, depth_weight, depth_tracker, seed); + rotation_sets, rows, c, depth_tracker, 0, depth_weight, max_lookahead, + max_tqe_candidates, seed); // synthesise the tableau - tableau_row_nodes_synthesis(rows, c, depth_weight, depth_tracker, seed); + tableau_row_nodes_synthesis( + rows, c, depth_tracker, depth_weight, max_lookahead, max_tqe_candidates, + seed); c.replace_SWAPs(); return c; } Circuit greedy_pauli_graph_synthesis( const Circuit& circ, double discount_rate, double depth_weight, - unsigned seed) { + unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed) { + if (max_lookahead == 0) { + throw GreedyPauliSimpError("max_lookahead must be greater than 0."); + } + if (max_tqe_candidates == 0) { + throw GreedyPauliSimpError("max_tqe_candidates must be greater than 0."); + } + Circuit circ_flat(circ); unsigned n_qubits = circ_flat.n_qubits(); unsigned n_bits = circ_flat.n_bits(); @@ -601,11 +654,12 @@ Circuit greedy_pauli_graph_synthesis( DepthTracker depth_tracker(n_qubits); // synthesise Pauli exps pauli_exps_synthesis( - rotation_sets, rows, new_circ, discount_rate, depth_weight, depth_tracker, - seed); + rotation_sets, rows, new_circ, depth_tracker, discount_rate, depth_weight, + max_lookahead, max_tqe_candidates, seed); // synthesise the tableau tableau_row_nodes_synthesis( - rows, new_circ, depth_weight, depth_tracker, seed); + rows, new_circ, depth_tracker, depth_weight, max_lookahead, + max_tqe_candidates, seed); for (auto it = measures.begin(); it != measures.end(); ++it) { new_circ.add_measure(it->left, it->right); } @@ -617,10 +671,13 @@ Circuit greedy_pauli_graph_synthesis( } // namespace GreedyPauliSimp Transform greedy_pauli_optimisation( - double discount_rate, double depth_weight, unsigned seed) { - return Transform([discount_rate, depth_weight, seed](Circuit& circ) { + double discount_rate, double depth_weight, unsigned max_lookahead, + unsigned max_tqe_candidates, unsigned seed) { + return Transform([discount_rate, depth_weight, max_lookahead, + max_tqe_candidates, seed](Circuit& circ) { circ = GreedyPauliSimp::greedy_pauli_graph_synthesis( - circ, discount_rate, depth_weight, seed); + circ, discount_rate, depth_weight, max_lookahead, max_tqe_candidates, + seed); // decompose the conditional CircBoxes circ.decompose_boxes_recursively(); return true; diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index 3cc4ec73e3..35a8115cca 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -100,6 +100,26 @@ SCENARIO("Clifford synthesis") { REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); REQUIRE(test_unitary_comparison(circ, d, true)); } + GIVEN("Test search limits") { + Circuit circ(4); + circ.add_op(OpType::X, {0}); + circ.add_op(OpType::SWAP, {1, 2}); + circ.add_op(OpType::CX, {0, 2}); + circ.add_op(OpType::H, {3}); + circ.add_op(OpType::CZ, {1, 3}); + circ.add_op(OpType::H, {2}); + circ.add_op(OpType::CX, {3, 2}); + circ.add_op(OpType::Z, {2}); + circ.add_op(OpType::SWAP, {3, 1}); + circ.add_op(OpType::CY, {0, 2}); + Circuit d1(circ); + Circuit d2(circ); + REQUIRE(Transforms::greedy_pauli_optimisation(0.7, 0.3, 2, 1).apply(d1)); + REQUIRE(Transforms::greedy_pauli_optimisation(0.7, 0.3, 20, 20).apply(d2)); + REQUIRE(test_unitary_comparison(circ, d1, true)); + REQUIRE(test_unitary_comparison(circ, d2, true)); + REQUIRE(d1 != d2); + } } SCENARIO("Complete synthesis") { GIVEN("1Q Simple Circuit") { @@ -183,6 +203,55 @@ SCENARIO("Complete synthesis") { REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); REQUIRE(test_unitary_comparison(circ, d, true)); } + GIVEN("5Q PauliExp Circuit with search limits") { + Circuit circ(5); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::X, Pauli::X, Pauli::X}, 0.3)), + {0, 1, 4}); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::X, Pauli::Z, Pauli::Y}, -0.1)), + {2, 3, 0}); + circ.add_box( + PauliExpPairBox( + SymPauliTensor({Pauli::X, Pauli::Z, Pauli::Y}, 1.0), + SymPauliTensor({Pauli::Z, Pauli::X, Pauli::Y}, 0.4)), + {0, 2, 4}); + circ.add_box( + PauliExpCommutingSetBox({ + {{ + Pauli::I, + Pauli::Y, + Pauli::I, + }, + -0.1}, + {{Pauli::X, Pauli::Y, Pauli::Z}, -1.2}, + {{Pauli::X, Pauli::Y, Pauli::Z}, 0.5}, + }), + {1, 2, 3}); + circ.add_box( + PauliExpCommutingSetBox({ + {{ + Pauli::I, + Pauli::X, + Pauli::I, + }, + -0.15}, + {{Pauli::X, Pauli::X, Pauli::Z}, -1.25}, + {{Pauli::X, Pauli::X, Pauli::Z}, 0.2}, + }), + {0, 3, 4}); + circ.add_op(OpType::CX, {0, 2}); + circ.add_op(OpType::SWAP, {2, 3}); + circ.add_op(OpType::H, {3}); + circ.add_op(OpType::CZ, {1, 3}); + Circuit d1(circ); + Circuit d2(circ); + REQUIRE(Transforms::greedy_pauli_optimisation(0.7, 0.3, 3, 3).apply(d1)); + REQUIRE(Transforms::greedy_pauli_optimisation(0.7, 0.3, 30, 30).apply(d2)); + REQUIRE(test_unitary_comparison(circ, d1, true)); + REQUIRE(test_unitary_comparison(circ, d2, true)); + REQUIRE(d1 != d2); + } GIVEN("Circuit with trivial Pauli exps") { Circuit circ(4); circ.add_box(PauliExpBox(SymPauliTensor({Pauli::X, Pauli::X}, 2)), {0, 1}); From c5b778db1193ffcac69a60821a61049780cfe351 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 15 Oct 2024 12:07:22 +0100 Subject: [PATCH 39/57] Allowing ZZPhase gates --- .../GreedyPauliOptimisation.hpp | 39 +++- tket/src/Transformations/GreedyPauliOps.cpp | 24 +- .../GreedyPauliOptimisation.cpp | 220 +++++++++++++----- tket/test/src/test_GreedyPauli.cpp | 85 +++++++ 4 files changed, 296 insertions(+), 72 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 8b97bcedb6..dc50432e47 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -82,10 +82,31 @@ enum class BitType : unsigned { }; /** - * @brief Type for 2-qubit entangled Clifford gates + * @brief Struct for 2-qubit entangled Clifford gates * */ -using TQE = std::tuple; +struct TQE { + TQEType type; + unsigned a; + unsigned b; + bool operator<(const TQE& other) const { + return std::tie(type, a, b) < std::tie(other.type, other.a, other.b); + } +}; + +/** + * @brief Struct for 2-qubit rotation gates + * + */ +struct Rotation2Q { + Pauli p_a; + Pauli p_b; + unsigned a; + unsigned b; + Expr angle; + unsigned index; + bool operator<(const Rotation2Q& other) const { return index < other.index; } +}; /** * @brief Commutation information of a node specified by a list of @@ -167,7 +188,7 @@ class SingleNode : public PauliNode { bool sign() const { return sign_; }; - std::vector string() const { return string_; }; + const std::vector& string() const { return string_; }; protected: std::vector string_; @@ -248,9 +269,9 @@ class ACPairNode : public PauliNode { bool x_sign() const { return x_sign_; }; - std::vector z_string() const { return z_string_; }; + const std::vector& z_string() const { return z_string_; }; - std::vector x_string() const { return x_string_; }; + const std::vector& x_string() const { return x_string_; }; protected: std::vector z_string_; @@ -589,12 +610,13 @@ gpg_from_unordered_set(const std::vector& unordered_set); * @param max_lookahead * @param max_tqe_candidates * @param seed + * @param allow_zzphase * @return Circuit */ Circuit greedy_pauli_graph_synthesis( const Circuit& circ, double discount_rate = 0.7, double depth_weight = 0.3, unsigned max_lookahead = 500, unsigned max_tqe_candidates = 500, - unsigned seed = 0); + unsigned seed = 0, bool allow_zzphase = false); /** * @brief Synthesise a set of unordered Pauli exponentials by applying Clifford @@ -606,19 +628,20 @@ Circuit greedy_pauli_graph_synthesis( * @param max_lookahead * @param max_tqe_candidates * @param seed + * @param allow_zzphase * @return Circuit */ Circuit greedy_pauli_set_synthesis( const std::vector& unordered_set, double depth_weight = 0.3, unsigned max_lookahead = 500, unsigned max_tqe_candidates = 500, - unsigned seed = 0); + unsigned seed = 0, bool allow_zzphase = false); } // namespace GreedyPauliSimp Transform greedy_pauli_optimisation( double discount_rate = 0.7, double depth_weight = 0.3, unsigned max_lookahead = 500, unsigned max_tqe_candidates = 500, - unsigned seed = 0); + unsigned seed = 0, bool allow_zzphase = false); } // namespace Transforms diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index 0b58983917..a1735815ea 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -67,7 +67,9 @@ SingleNode::SingleNode(std::vector string, bool sign) unsigned SingleNode::tqe_cost() const { return weight_ - 1; } int SingleNode::tqe_cost_increase(const TQE& tqe) const { - auto [g, a, b] = tqe; + const TQEType& g = tqe.type; + const unsigned& a = tqe.a; + const unsigned& b = tqe.b; Pauli p0 = string_[a]; Pauli p1 = string_[b]; auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); @@ -76,7 +78,9 @@ int SingleNode::tqe_cost_increase(const TQE& tqe) const { } void SingleNode::update(const TQE& tqe) { - auto [g, a, b] = tqe; + const TQEType& g = tqe.type; + const unsigned& a = tqe.a; + const unsigned& b = tqe.b; Pauli p0 = string_[a]; Pauli p1 = string_[b]; auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); @@ -152,7 +156,9 @@ unsigned ACPairNode::tqe_cost() const { } int ACPairNode::tqe_cost_increase(const TQE& tqe) const { - auto [g, a, b] = tqe; + const TQEType& g = tqe.type; + const unsigned& a = tqe.a; + const unsigned& b = tqe.b; Pauli z_p0 = z_string_[a]; Pauli z_p1 = z_string_[b]; Pauli x_p0 = x_string_[a]; @@ -175,7 +181,9 @@ int ACPairNode::tqe_cost_increase(const TQE& tqe) const { } void ACPairNode::update(const TQE& tqe) { - auto [g, a, b] = tqe; + const TQEType& g = tqe.type; + const unsigned& a = tqe.a; + const unsigned& b = tqe.b; Pauli z_p0 = z_string_[a]; Pauli z_p1 = z_string_[b]; Pauli x_p0 = x_string_[a]; @@ -310,7 +318,9 @@ unsigned ConditionalBlock::tqe_cost() const { return total_weight_ - 1; } int ConditionalBlock::tqe_cost_increase(const TQE& tqe) const { int total_increase = 0; for (const std::tuple, bool, Expr>& rot : rotations_) { - auto [g, a, b] = tqe; + const TQEType& g = tqe.type; + const unsigned& a = tqe.a; + const unsigned& b = tqe.b; Pauli p0 = std::get<0>(rot)[a]; Pauli p1 = std::get<0>(rot)[b]; auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); @@ -322,7 +332,9 @@ int ConditionalBlock::tqe_cost_increase(const TQE& tqe) const { void ConditionalBlock::update(const TQE& tqe) { for (std::tuple, bool, Expr>& rot : rotations_) { - auto [g, a, b] = tqe; + const TQEType& g = tqe.type; + const unsigned& a = tqe.a; + const unsigned& b = tqe.b; Pauli p0 = std::get<0>(rot)[a]; Pauli p1 = std::get<0>(rot)[b]; auto [new_p0, new_p1, sign] = TQE_PAULI_MAP.at({g, p0, p1}); diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 5607ed4cf5..9d82ce3e92 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -28,15 +28,13 @@ namespace Transforms { namespace GreedyPauliSimp { -template -static typename Container::const_iterator sample_random_element( - const Container& container, unsigned seed) { +static TQE sample_random_tqe(const std::vector& vec, unsigned seed) { std::mt19937 rng(seed); - std::uniform_int_distribution dist(0, container.size() - 1); + std::uniform_int_distribution dist(0, vec.size() - 1); size_t random_index = dist(rng); - auto it = container.begin(); + auto it = vec.begin(); std::advance(it, random_index); - return it; + return *it; } static std::vector sample_tqes( @@ -57,8 +55,34 @@ static std::vector sample_tqes( return vec; } +static void apply_rot2q_to_circ(const Rotation2Q& rot, Circuit& circ) { + if (rot.p_a == Pauli::X) { + circ.add_op(OpType::H, {rot.a}); + } else if (rot.p_a == Pauli::Y) { + circ.add_op(OpType::V, {rot.a}); + } + if (rot.p_b == Pauli::X) { + circ.add_op(OpType::H, {rot.b}); + } else if (rot.p_b == Pauli::Y) { + circ.add_op(OpType::V, {rot.b}); + } + circ.add_op(OpType::ZZPhase, rot.angle, {rot.a, rot.b}); + if (rot.p_a == Pauli::X) { + circ.add_op(OpType::H, {rot.a}); + } else if (rot.p_a == Pauli::Y) { + circ.add_op(OpType::Vdg, {rot.a}); + } + if (rot.p_b == Pauli::X) { + circ.add_op(OpType::H, {rot.b}); + } else if (rot.p_b == Pauli::Y) { + circ.add_op(OpType::Vdg, {rot.b}); + } +} + static void apply_tqe_to_circ(const TQE& tqe, Circuit& circ) { - auto [gate_type, a, b] = tqe; + const TQEType& gate_type = tqe.type; + const unsigned& a = tqe.a; + const unsigned& b = tqe.b; switch (gate_type) { case TQEType::XX: circ.add_op(OpType::H, {a}); @@ -102,7 +126,7 @@ static void apply_tqe_to_circ(const TQE& tqe, Circuit& circ) { static double default_tableau_tqe_cost( const std::vector& rows, const std::vector& remaining_indices, const TQE& tqe, - unsigned max_lookahead) { + const unsigned& max_lookahead) { double cost = 0; unsigned count = 0; for (const unsigned& index : remaining_indices) { @@ -139,11 +163,14 @@ static double default_pauliexp_tqe_cost( return exp_cost + tab_cost; } -// given a map from TQE to a vector of costs, select the one with the minimum -// weighted sum of minmax-normalised costs -static TQE minmax_selection( +// given a map from TQE to a vector of costs, and an optional map +// specifying the costs for implementing some 2q rotations directly +// as ZZPhase gates. Select the TQEs and 2q rotations with the minimum +// weighted sum of minmax-normalised costs. +static std::pair, std::vector> minmax_selection( const std::map>& tqe_candidates_cost, - const std::vector& weights, unsigned seed) { + const std::map>& rot2q_gates_cost, + const std::vector& weights) { TKET_ASSERT(tqe_candidates_cost.size() > 0); size_t n_costs = tqe_candidates_cost.begin()->second.size(); TKET_ASSERT(n_costs == weights.size()); @@ -161,6 +188,17 @@ static TQE minmax_selection( } } } + for (const auto& pair : tqe_candidates_cost) { + TKET_ASSERT(pair.second.size() == n_costs); + for (unsigned cost_index = 0; cost_index < n_costs; cost_index++) { + if (pair.second[cost_index] < mins[cost_index]) { + mins[cost_index] = pair.second[cost_index]; + } + if (pair.second[cost_index] > maxs[cost_index]) { + maxs[cost_index] = pair.second[cost_index]; + } + } + } // valid_indices stores the indices of the costs where min!=max std::vector valid_indices; for (unsigned cost_index = 0; cost_index < n_costs; cost_index++) { @@ -170,8 +208,19 @@ static TQE minmax_selection( } // if all have the same cost, return the first one if (valid_indices.size() == 0) { - auto it = sample_random_element(tqe_candidates_cost, seed); - return it->first; + std::vector rot2qs; + rot2qs.reserve(rot2q_gates_cost.size()); + std::transform( + rot2q_gates_cost.begin(), rot2q_gates_cost.end(), + std::back_inserter(rot2qs), + [](const auto& pair) { return pair.first; }); + std::vector selected_tqes; + selected_tqes.reserve(tqe_candidates_cost.size()); + std::transform( + tqe_candidates_cost.begin(), tqe_candidates_cost.end(), + std::back_inserter(selected_tqes), + [](const auto& pair) { return pair.first; }); + return {selected_tqes, rot2qs}; } // if only one cost variable, no need to normalise if (valid_indices.size() == 1) { @@ -186,8 +235,18 @@ static TQE minmax_selection( min_tqes.push_back(it->first); } } - auto sampled_it = sample_random_element(min_tqes, seed); - return *sampled_it; + std::vector min_rot2qs; + for (auto it2 = rot2q_gates_cost.begin(); it2 != rot2q_gates_cost.end(); + it2++) { + if (it2->second[valid_indices[0]] < min_cost) { + min_tqes.clear(); + min_cost = it2->second[valid_indices[0]]; + min_rot2qs = {it2->first}; + } else if (it2->second[valid_indices[0]] == min_cost) { + min_rot2qs.push_back(it2->first); + } + } + return {min_tqes, min_rot2qs}; } // find the tqe with the minimum normalised cost auto it = tqe_candidates_cost.begin(); @@ -215,20 +274,24 @@ static TQE minmax_selection( min_tqes.push_back(it->first); } } - auto sampled_it = sample_random_element(min_tqes, seed); - return *sampled_it; -} - -static TQE select_pauliexp_tqe( - const std::map>& tqe_candidates_cost, - double depth_weight, unsigned seed) { - return minmax_selection(tqe_candidates_cost, {1, depth_weight}, seed); -} - -static TQE select_tableau_tqe( - const std::map>& tqe_candidates_cost, - double depth_weight, unsigned seed) { - return minmax_selection(tqe_candidates_cost, {1, depth_weight}, seed); + std::vector min_rot2qs; + for (auto it2 = rot2q_gates_cost.begin(); it2 != rot2q_gates_cost.end(); + it2++) { + double cost = 0; + for (const auto& cost_index : valid_indices) { + cost += weights[cost_index] * + (it2->second[cost_index] - mins[cost_index]) / + (maxs[cost_index] - mins[cost_index]); + } + if (cost < min_cost) { + min_cost = cost; + min_tqes.clear(); + min_rot2qs = {it2->first}; + } else if (cost == min_cost) { + min_rot2qs.push_back(it2->first); + } + } + return {min_tqes, min_rot2qs}; } // simple struct that tracks the depth on each qubit @@ -304,18 +367,17 @@ static void tableau_row_nodes_synthesis( {tqe, {default_tableau_tqe_cost( rows, remaining_indices, tqe, max_lookahead), - static_cast(depth_tracker.gate_depth( - std::get<1>(tqe), std::get<2>(tqe)))}}); + static_cast(depth_tracker.gate_depth(tqe.a, tqe.b))}}); } TKET_ASSERT(tqe_candidates_cost.size() > 0); // select the best one - TQE selected_tqe = - select_tableau_tqe(tqe_candidates_cost, depth_weight, seed); + auto [min_tqes, _] = + minmax_selection(tqe_candidates_cost, {}, {1, depth_weight}); + TQE selected_tqe = sample_random_tqe(min_tqes, seed); // apply TQE apply_tqe_to_circ(selected_tqe, circ); // update depth tracker - depth_tracker.add_2q_gate( - std::get<1>(selected_tqe), std::get<2>(selected_tqe)); + depth_tracker.add_2q_gate(selected_tqe.a, selected_tqe.b); // remove finished nodes for (unsigned i = remaining_indices.size(); i-- > 0;) { unsigned node_index = remaining_indices[i]; @@ -540,7 +602,8 @@ static void pauli_exps_synthesis( std::vector>& rotation_sets, std::vector& rows, Circuit& circ, DepthTracker& depth_tracker, double discount_rate, double depth_weight, - unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed) { + unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed, + bool allow_zzphase) { while (true) { consume_nodes( rotation_sets, circ, depth_tracker, discount_rate, depth_weight); @@ -574,30 +637,70 @@ static void pauli_exps_synthesis( {tqe, {default_pauliexp_tqe_cost( discount_rate, rotation_sets, rows, tqe, max_lookahead), - static_cast(depth_tracker.gate_depth( - std::get<1>(tqe), std::get<2>(tqe)))}}); + static_cast(depth_tracker.gate_depth(tqe.a, tqe.b))}}); } - // select the best one - TQE selected_tqe = - select_pauliexp_tqe(tqe_candidates_cost, depth_weight, seed); - // apply TQE - apply_tqe_to_circ(selected_tqe, circ); - depth_tracker.add_2q_gate( - std::get<1>(selected_tqe), std::get<2>(selected_tqe)); - for (std::vector& rotation_set : rotation_sets) { - for (PauliNode_ptr& node : rotation_set) { - node->update(selected_tqe); + std::map> rot2q_gates_cost; + if (allow_zzphase) { + // implementing a 2q rotation directly will result in a + // -1 tqe cost change in the first rotation set and 0 elsewhere. + // If multiple 2q rotations are worth implementing directly, we + // implement all of them to avoid doing the same cost calculation + // in the next rounds. + for (unsigned i = 0; i < first_set.size(); i++) { + if (first_set[i]->get_type() == PauliNodeType::PauliRotation && + first_set[i]->tqe_cost() == 1) { + const PauliRotation& node = + dynamic_cast(*first_set[i]); + std::vector supps; + std::vector paulis; + for (unsigned j = 0; j < node.string().size(); j++) { + if (node.string()[j] != Pauli::I) { + supps.push_back(j); + paulis.push_back(node.string()[j]); + } + } + rot2q_gates_cost.insert( + {{paulis[0], paulis[1], supps[0], supps[1], node.angle(), i}, + {-1, static_cast( + depth_tracker.gate_depth(supps[0], supps[1]))}}); + } } } - for (PauliNode_ptr& row : rows) { - row->update(selected_tqe); + // select the best one + auto [min_tqes, min_rot2qs] = minmax_selection( + tqe_candidates_cost, rot2q_gates_cost, {1, depth_weight}); + if (min_rot2qs.empty()) { + TQE selected_tqe = sample_random_tqe(min_tqes, seed); + // apply TQE + apply_tqe_to_circ(selected_tqe, circ); + depth_tracker.add_2q_gate(selected_tqe.a, selected_tqe.b); + for (std::vector& rotation_set : rotation_sets) { + for (PauliNode_ptr& node : rotation_set) { + node->update(selected_tqe); + } + } + for (PauliNode_ptr& row : rows) { + row->update(selected_tqe); + } + } else { + // apply 2q rotations directly + std::sort( + min_rot2qs.begin(), min_rot2qs.end(), + [](const Rotation2Q& r1, const Rotation2Q& r2) { + return r1.index > r2.index; + }); + for (const Rotation2Q& rot : min_rot2qs) { + apply_rot2q_to_circ(rot, circ); + first_set.erase(first_set.begin() + rot.index); + } } } } Circuit greedy_pauli_set_synthesis( const std::vector& unordered_set, double depth_weight, - unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed) { + unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed, + bool allow_zzphase) { if (max_lookahead == 0) { throw GreedyPauliSimpError("max_lookahead must be greater than 0."); } @@ -616,7 +719,7 @@ Circuit greedy_pauli_set_synthesis( // synthesise Pauli exps pauli_exps_synthesis( rotation_sets, rows, c, depth_tracker, 0, depth_weight, max_lookahead, - max_tqe_candidates, seed); + max_tqe_candidates, seed, allow_zzphase); // synthesise the tableau tableau_row_nodes_synthesis( rows, c, depth_tracker, depth_weight, max_lookahead, max_tqe_candidates, @@ -627,7 +730,8 @@ Circuit greedy_pauli_set_synthesis( Circuit greedy_pauli_graph_synthesis( const Circuit& circ, double discount_rate, double depth_weight, - unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed) { + unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed, + bool allow_zzphase) { if (max_lookahead == 0) { throw GreedyPauliSimpError("max_lookahead must be greater than 0."); } @@ -655,7 +759,7 @@ Circuit greedy_pauli_graph_synthesis( // synthesise Pauli exps pauli_exps_synthesis( rotation_sets, rows, new_circ, depth_tracker, discount_rate, depth_weight, - max_lookahead, max_tqe_candidates, seed); + max_lookahead, max_tqe_candidates, seed, allow_zzphase); // synthesise the tableau tableau_row_nodes_synthesis( rows, new_circ, depth_tracker, depth_weight, max_lookahead, @@ -672,12 +776,12 @@ Circuit greedy_pauli_graph_synthesis( Transform greedy_pauli_optimisation( double discount_rate, double depth_weight, unsigned max_lookahead, - unsigned max_tqe_candidates, unsigned seed) { + unsigned max_tqe_candidates, unsigned seed, bool allow_zzphase) { return Transform([discount_rate, depth_weight, max_lookahead, - max_tqe_candidates, seed](Circuit& circ) { + max_tqe_candidates, seed, allow_zzphase](Circuit& circ) { circ = GreedyPauliSimp::greedy_pauli_graph_synthesis( circ, discount_rate, depth_weight, max_lookahead, max_tqe_candidates, - seed); + seed, allow_zzphase); // decompose the conditional CircBoxes circ.decompose_boxes_recursively(); return true; diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index 35a8115cca..ac80643691 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -471,6 +471,91 @@ SCENARIO("Complete synthesis") { REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } + GIVEN("Compile to ZZPhase") { + Circuit circ(2); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::X, Pauli::X}, 0.3)), {0, 1}); + Circuit d1(circ); + Circuit d2(circ); + REQUIRE(Transforms::greedy_pauli_optimisation(0.7, 0.3, 500, 500, 0, true) + .apply(d1)); + REQUIRE(Transforms::greedy_pauli_optimisation(0.7, 0.3, 500, 500, 0, false) + .apply(d2)); + REQUIRE(test_unitary_comparison(circ, d1, true)); + REQUIRE(test_unitary_comparison(circ, d2, true)); + REQUIRE(d1.count_n_qubit_gates(2) == 1); + REQUIRE(d2.count_n_qubit_gates(2) == 2); + } + GIVEN("Multiple ZZPhases at once") { + Circuit circ(6); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::X, Pauli::X}, 0.3)), {0, 1}); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::Z, Pauli::X}, 0.1)), {2, 3}); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::X, Pauli::Y}, 0.2)), {4, 5}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation(0.7, 0.3, 500, 500, 0, true) + .apply(d)); + REQUIRE(test_unitary_comparison(circ, d, true)); + REQUIRE(d.count_n_qubit_gates(2) == 3); + } + GIVEN("Large circuit with ZZPhase") { + Circuit circ(6); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::X, Pauli::X}, 0.3)), {0, 1}); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::Z, Pauli::Y, Pauli::X}, 0.2)), + {0, 1, 2}); + circ.add_box( + PauliExpCommutingSetBox({ + {{Pauli::I, Pauli::Y, Pauli::I, Pauli::Z}, 1.2}, + {{Pauli::X, Pauli::Y, Pauli::Z, Pauli::I}, 0.8}, + {{Pauli::I, Pauli::I, Pauli::I, Pauli::Z}, 1.25}, + }), + {1, 2, 3, 4}); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::Y, Pauli::X}, 0.1)), {2, 3}); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::Z, Pauli::Y, Pauli::X}, 0.11)), + {1, 3, 4}); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::Y, Pauli::Y}, 0.2)), {4, 5}); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::Z, Pauli::Z, Pauli::X}, 0.15)), + {2, 4, 5}); + circ.add_box( + PauliExpBox( + SymPauliTensor({Pauli::X, Pauli::X, Pauli::X, Pauli::X}, 0.25)), + {2, 4, 5, 0}); + circ.add_box( + PauliExpBox( + SymPauliTensor({Pauli::Y, Pauli::Z, Pauli::Z, Pauli::X}, 0.125)), + {1, 3, 5, 0}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation(0.7, 0.3, 500, 500, 0, true) + .apply(d)); + REQUIRE(test_unitary_comparison(circ, d, true)); + } + GIVEN("Select TQE over ZZPhase") { + Circuit circ(3); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::X, Pauli::Y}, 0.3)), {0, 1}); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::Z, Pauli::Y, Pauli::Z}, 0.22)), + {0, 1, 2}); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::Z, Pauli::Y, Pauli::X}, 0.15)), + {0, 1, 2}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation(0.7, 0.3, 500, 500, 0, true) + .apply(d)); + REQUIRE(test_unitary_comparison(circ, d, true)); + // if the first XY was implemented using a ZZPhase + // then 2 TQEs is needed to conjugate the remaining two strings to weight 2 + // hence 5 2-qubit gates in total. + REQUIRE(d.count_n_qubit_gates(2) == 4); + } } SCENARIO("Test GreedyPauliSimp for individual gates") { Circuit circ(1); From 273349bfb4a1fe6cc9a480914c5dc71ac75cba73 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 15 Oct 2024 16:32:13 +0100 Subject: [PATCH 40/57] update binder and serialisation --- pytket/binders/passes.cpp | 13 +++++++++- pytket/binders/transform.cpp | 13 +++++++++- pytket/tests/passes_serialisation_test.py | 10 +++++++- schemas/compiler_pass_v1.json | 24 +++++++++++++++++-- .../tket/Predicates/PassGenerators.hpp | 9 ++++++- .../GreedyPauliOptimisation.hpp | 2 +- tket/src/Predicates/CompilerPass.cpp | 14 ++++++++++- tket/src/Predicates/PassGenerators.cpp | 13 +++++++--- 8 files changed, 87 insertions(+), 11 deletions(-) diff --git a/pytket/binders/passes.cpp b/pytket/binders/passes.cpp index 48590f9c81..d47181072f 100644 --- a/pytket/binders/passes.cpp +++ b/pytket/binders/passes.cpp @@ -927,8 +927,19 @@ PYBIND11_MODULE(passes, m) { "\n\n:param discount_rate: Rate used to discount the cost impact from " "gadgets that are further away. Default to 0.7." "\n:param depth_weight: Degree of depth optimisation. Default to 0.3." + "\n:param max_tqe_candidates: Maximum number of 2-qubit Clifford " + "gate candidates to evaluate at each step. Default to 500." + "\n:param max_lookahead: Maximum lookahead when evaluating each " + "Clifford gate candidate. Default to 500." + "\n:param seed: Unsigned integer seed used for sampling candidates " + "and tie breaking. Default to 0." + "\n:param allow_zzphase: If set to True, allows the algorithm to " + "implement 2-qubit rotations using ZZPhase gates when deemed " + "optimal. Defaults to False." "\n:return: a pass to perform the simplification", - py::arg("discount_rate") = 0.7, py::arg("depth_weight") = 0.3); + py::arg("discount_rate") = 0.7, py::arg("depth_weight") = 0.3, + py::arg("max_lookahead") = 500, py::arg("max_tqe_candidates") = 500, + py::arg("seed") = 0, py::arg("allow_zzphase") = false); m.def( "PauliSquash", &PauliSquash, "Applies :py:meth:`PauliSimp` followed by " diff --git a/pytket/binders/transform.cpp b/pytket/binders/transform.cpp index 8ef8027b78..21724759c3 100644 --- a/pytket/binders/transform.cpp +++ b/pytket/binders/transform.cpp @@ -431,8 +431,19 @@ PYBIND11_MODULE(transform, m) { "gadgets that are further away. Default to 0.7." "\n:param depth_weight: Degree of depth optimisation. Default to " "0.3." + "\n:param max_tqe_candidates: Maximum number of 2-qubit Clifford " + "gate candidates to evaluate at each step. Default to 500." + "\n:param max_lookahead: Maximum lookahead when evaluating each " + "Clifford gate candidate. Default to 500." + "\n:param seed: Unsigned integer seed used for sampling candidates " + "and tie breaking. Default to 0." + "\n:param allow_zzphase: If set to True, allows the algorithm to " + "implement 2-qubit rotations using ZZPhase gates when deemed " + "optimal. Defaults to False." "\n:return: a pass to perform the simplification", - py::arg("discount_rate") = 0.7, py::arg("depth_weight") = 0.3) + py::arg("discount_rate") = 0.7, py::arg("depth_weight") = 0.3, + py::arg("max_tqe_candidates") = 500, py::arg("max_lookahead") = 500, + py::arg("seed") = 0, py::arg("allow_zzphase") = false) .def_static( "ZZPhaseToRz", &Transforms::ZZPhase_to_Rz, "Fixes all ZZPhase gate angles to [-1, 1) half turns.") diff --git a/pytket/tests/passes_serialisation_test.py b/pytket/tests/passes_serialisation_test.py index f7d7d788b6..193ece5786 100644 --- a/pytket/tests/passes_serialisation_test.py +++ b/pytket/tests/passes_serialisation_test.py @@ -290,7 +290,15 @@ def nonparam_predicate_dict(name: str) -> Dict[str, Any]: {"name": "RoundAngles", "n": 6, "only_zeros": False} ), "GreedyPauliSimp": standard_pass_dict( - {"name": "GreedyPauliSimp", "discount_rate": 0.4, "depth_weight": 0.5} + { + "name": "GreedyPauliSimp", + "discount_rate": 0.4, + "depth_weight": 0.5, + "max_lookahead": 100, + "max_tqe_candidates": 100, + "seed": 2, + "allow_zzphase": True, + } ), # lists must be sorted by OpType value "AutoSquash": standard_pass_dict( diff --git a/schemas/compiler_pass_v1.json b/schemas/compiler_pass_v1.json index e3a807dde3..b8175c8b92 100644 --- a/schemas/compiler_pass_v1.json +++ b/schemas/compiler_pass_v1.json @@ -357,6 +357,22 @@ "depth_weight": { "type": "number", "definition": "parameter controlling the degree of depth optimisation in \"GreedyPauliSimp\"" + }, + "max_lookahead": { + "type": "number", + "definition": "parameter controlling the lookahead when evaluating candidates in \"GreedyPauliSimp\"" + }, + "max_tqe_candidates": { + "type": "number", + "definition": "parameter controlling the number of candidates to evaluate in \"GreedyPauliSimp\"" + }, + "seed": { + "type": "number", + "definition": "parameter controlling the random sampling and tie breaking in \"GreedyPauliSimp\"" + }, + "allow_zzphase": { + "type": "boolean", + "definition": "parameter controlling the use of ZZPhase gates in \"GreedyPauliSimp\"" } }, "required": [ @@ -883,9 +899,13 @@ "then": { "required": [ "discount_rate", - "depth_weight" + "depth_weight", + "max_lookahead", + "max_tqe_candidates", + "seed", + "allow_zzphase" ], - "maxProperties": 3 + "maxProperties": 7 } }, { diff --git a/tket/include/tket/Predicates/PassGenerators.hpp b/tket/include/tket/Predicates/PassGenerators.hpp index 6573a02e21..9e7f87931c 100644 --- a/tket/include/tket/Predicates/PassGenerators.hpp +++ b/tket/include/tket/Predicates/PassGenerators.hpp @@ -350,9 +350,16 @@ PassPtr gen_special_UCC_synthesis( * * @param discount_rate * @param depth_weight + * @param max_lookahead + * @param max_tqe_candidates + * @param seed + * @param allow_zzphase * @return PassPtr */ -PassPtr gen_greedy_pauli_simp(double discount_rate, double depth_weight); +PassPtr gen_greedy_pauli_simp( + double discount_rate = 0.7, double depth_weight = 0.3, + unsigned max_lookahead = 500, unsigned max_tqe_candidates = 500, + unsigned seed = 0, bool allow_zzphase = false); /** * Generate a pass to simplify the circuit where it acts on known basis states. diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index dc50432e47..efa894eeb7 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -628,7 +628,7 @@ Circuit greedy_pauli_graph_synthesis( * @param max_lookahead * @param max_tqe_candidates * @param seed - * @param allow_zzphase + * @param allow_zzphase * @return Circuit */ Circuit greedy_pauli_set_synthesis( diff --git a/tket/src/Predicates/CompilerPass.cpp b/tket/src/Predicates/CompilerPass.cpp index ccea6b246f..43691b0faa 100644 --- a/tket/src/Predicates/CompilerPass.cpp +++ b/tket/src/Predicates/CompilerPass.cpp @@ -500,7 +500,19 @@ void from_json(const nlohmann::json& j, PassPtr& pp) { } else if (passname == "GreedyPauliSimp") { double discount_rate = content.at("discount_rate").get(); double depth_weight = content.at("depth_weight").get(); - pp = gen_greedy_pauli_simp(discount_rate, depth_weight); + // for backward compatibility + if (!content.contains("max_tqe_candidates")) { + pp = gen_greedy_pauli_simp(discount_rate, depth_weight); + } + unsigned max_tqe_candidates = + content.at("max_tqe_candidates").get(); + unsigned max_lookahead = content.at("max_lookahead").get(); + unsigned seed = content.at("seed").get(); + bool allow_zzphase = content.at("allow_zzphase").get(); + pp = gen_greedy_pauli_simp( + discount_rate, depth_weight, max_lookahead, max_tqe_candidates, seed, + allow_zzphase); + } else if (passname == "PauliSimp") { // SEQUENCE PASS - DESERIALIZABLE ONLY Transforms::PauliSynthStrat pss = diff --git a/tket/src/Predicates/PassGenerators.cpp b/tket/src/Predicates/PassGenerators.cpp index 2347d18286..610caabd84 100644 --- a/tket/src/Predicates/PassGenerators.cpp +++ b/tket/src/Predicates/PassGenerators.cpp @@ -1013,9 +1013,12 @@ PassPtr gen_synthesise_pauli_graph( return std::make_shared(seq); } -PassPtr gen_greedy_pauli_simp(double discount_rate, double depth_weight) { - Transform t = - Transforms::greedy_pauli_optimisation(discount_rate, depth_weight); +PassPtr gen_greedy_pauli_simp( + double discount_rate, double depth_weight, unsigned max_lookahead, + unsigned max_tqe_candidates, unsigned seed, bool allow_zzphase) { + Transform t = Transforms::greedy_pauli_optimisation( + discount_rate, depth_weight, max_lookahead, max_tqe_candidates, seed, + allow_zzphase); OpTypeSet ins = { OpType::Z, OpType::X, @@ -1060,6 +1063,10 @@ PassPtr gen_greedy_pauli_simp(double discount_rate, double depth_weight) { j["name"] = "GreedyPauliSimp"; j["discount_rate"] = discount_rate; j["depth_weight"] = depth_weight; + j["max_lookahead"] = max_lookahead; + j["max_tqe_candidates"] = max_tqe_candidates; + j["seed"] = seed; + j["allow_zzphase"] = allow_zzphase; return std::make_shared(precons, t, postcon, j); } From eafa42ca487ba50b60f82beb2dabab3eb5955644 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 15 Oct 2024 17:11:21 +0100 Subject: [PATCH 41/57] Add test for ops handling in python --- pytket/tests/predicates_test.py | 52 +++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/pytket/tests/predicates_test.py b/pytket/tests/predicates_test.py index 186c190cb5..ea805321a4 100644 --- a/pytket/tests/predicates_test.py +++ b/pytket/tests/predicates_test.py @@ -27,6 +27,9 @@ UnitID, Conditional, Bit, + RangePredicateOp, + SetBitsOp, + MultiBitOp, ) from pytket.circuit.named_types import ParamType, RenameUnitsMap from pytket.pauli import Pauli @@ -1020,18 +1023,61 @@ def test_clifford_push_through_measures() -> None: assert coms[7].op.type == OpType.CopyBits -def greedy_pauli_synth() -> None: - circ = Circuit(4, name="test") +def test_greedy_pauli_synth() -> None: + circ = Circuit(name="test") rega = circ.add_q_register("a", 2) regb = circ.add_q_register("b", 2) - d = circ.copy() circ.Rz(0, rega[0]).H(regb[1]).CX(rega[0], rega[1]).Ry(0.3, rega[0]).S(regb[1]).CZ( rega[0], regb[0] ).SWAP(regb[1], rega[0]) + d = circ.copy() pss = GreedyPauliSimp(0.5, 0.5) assert pss.apply(d) assert np.allclose(circ.get_unitary(), d.get_unitary()) assert d.name == "test" + # test gateset + range_predicate = RangePredicateOp(3, 0, 6) + set_bits = SetBitsOp([True, True]) + multi_bit = MultiBitOp(set_bits, 2) + exp = Bit(2) & Bit(3) + eq_pred_values = [True, False, False, True] + and_values = [bool(i) for i in [0, 0, 0, 1]] + pg1 = PauliExpBox([Pauli.X, Pauli.Z], 0.3) + circ = Circuit(4, 4, name="test") + circ.add_pauliexpbox(pg1, [0, 1]) + circ.add_gate(multi_bit, [0, 1, 2, 3]) + circ.add_gate(range_predicate, [0, 1, 2, 3]) + circ.add_classicalexpbox_bit(exp, [Bit(0)]) + circ.add_c_predicate(eq_pred_values, [0, 1], 2, "EQ") + circ.add_c_modifier(and_values, [1], 2) + circ._add_wasm("funcname", "wasmfileuid", [1, 1], [], [Bit(0), Bit(1)], [0]) + circ.measure_all() + circ.Reset(0) + circ.add_pauliexpbox(pg1, [2, 3]) + assert GreedyPauliSimp(0.5, 0.5, 100, 100, 0, True).apply(circ) + # PauliExpBoxes implemented using ZZPhase + d = Circuit(4, 4, name="test") + d.H(0) + d.ZZPhase(0.3, 0, 1) + d.H(0) + d.add_gate(multi_bit, [0, 1, 2, 3]) + d.add_gate(range_predicate, [0, 1, 2, 3]) + d.add_classicalexpbox_bit(exp, [Bit(0)]) + d.add_c_predicate(eq_pred_values, [0, 1], 2, "EQ") + d.add_c_modifier(and_values, [1], 2) + d._add_wasm("funcname", "wasmfileuid", [1, 1], [], [Bit(0), Bit(1)], [0]) + d.measure_all() + d.Reset(0) + d.H(2) + d.ZZPhase(0.3, 2, 3) + d.H(2) + assert circ == d + # test barrier + circ = Circuit(1).add_barrier([0]) + with pytest.raises(RuntimeError) as e: + GreedyPauliSimp().apply(circ) + err_msg = "Predicate requirements are not satisfied" + assert err_msg in str(e.value) def test_auto_rebase_deprecation(recwarn: Any) -> None: From 1f91c2947edfe773bf3fd52fcdaa3c744bcebcd4 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 15 Oct 2024 17:19:44 +0100 Subject: [PATCH 42/57] add note for AC node cost --- tket/src/Transformations/GreedyPauliOps.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index a1735815ea..3e0af85795 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -151,6 +151,11 @@ ACPairNode::ACPairNode( } unsigned ACPairNode::tqe_cost() const { + // for a node with n A pairs and m C pairs + // it takes (n-1)/2 TQE gates to convert n-1 A + // pairs to C pairs. It then takes n-1+m TQE gates + // to convert all the C pairs to I pairs. + // total TQEs required is 1.5n - 1.5 + m return static_cast( 1.5 * (n_anti_commute_entries_ - 1) + n_commute_entries_); } From 102ea5b629302dba4ac4358756d6c35aad145a8c Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 15 Oct 2024 17:21:30 +0100 Subject: [PATCH 43/57] bump tket version --- pytket/conanfile.py | 2 +- tket/conanfile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pytket/conanfile.py b/pytket/conanfile.py index bbba711fbf..8674f27a3d 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -38,7 +38,7 @@ def requirements(self): self.requires("pybind11_json/0.2.14") self.requires("symengine/0.12.0") self.requires("tkassert/0.3.4@tket/stable") - self.requires("tket/1.3.33@tket/stable") + self.requires("tket/1.3.34@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tktokenswap/0.3.9@tket/stable") diff --git a/tket/conanfile.py b/tket/conanfile.py index 04deefbc46..2e7e0e9b43 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.3.33" + version = "1.3.34" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" From fd5e552e2332b8394a3509386c49d86b0c0d09a7 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 15 Oct 2024 17:30:22 +0100 Subject: [PATCH 44/57] update changelog --- pytket/docs/changelog.rst | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index b1752fca3a..d103dfb10e 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -4,16 +4,21 @@ Changelog 1.33.2 (Unreleased) ------------------- +Features: + * Support Python 3.13. +* Several updates to `GreedyPauliSimp`: + * Support for mid-circuit measurements, resets, conditionals, + and classical gates. + * New parameters `max_lookahead` and `max_tqe_candidates` are added + to limit the search space. + * New parameter `seed` is added to support random sampling and tie breaking. + * New parameter `allow_zzphase` allows the algorithm to implement 2-qubit rotations + using ZZPhase gates when deemed optimal. 1.33.1 (October 2024) --------------------- -Features: - -* `GreedyPauliSimp`` now supports mid-circuit measurements, resets, conditionals, - and classical gates. - Fixes: * Fix `GuidedPauliSimp` for circuits containing `CircBox` with classical wires. From d6186d93fd59ac1d656b5445e0958d94bfd03db9 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 15 Oct 2024 18:14:14 +0100 Subject: [PATCH 45/57] fix changelog format --- pytket/docs/changelog.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index d103dfb10e..3efb0784d0 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -8,11 +8,15 @@ Features: * Support Python 3.13. * Several updates to `GreedyPauliSimp`: + * Support for mid-circuit measurements, resets, conditionals, - and classical gates. + and classical gates. + * New parameters `max_lookahead` and `max_tqe_candidates` are added to limit the search space. + * New parameter `seed` is added to support random sampling and tie breaking. + * New parameter `allow_zzphase` allows the algorithm to implement 2-qubit rotations using ZZPhase gates when deemed optimal. From 3f1d65ab80d1e6537adb8e0881c06079d6d90654 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 15 Oct 2024 18:22:15 +0100 Subject: [PATCH 46/57] add missing prams in docstrings --- tket/include/tket/Transformations/GreedyPauliOptimisation.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index efa894eeb7..1ec4487fde 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -319,6 +319,7 @@ class PauliRotation : public SingleNode { * @brief Construct a new PauliRotation object. * * @param string the Pauli string + * @param sign the sign of the Pauli string * @param theta the rotation angle in half-turns */ PauliRotation(std::vector string, bool sign, Expr theta); @@ -383,7 +384,8 @@ class ConditionalBlock : public PauliNode { /** * @brief Sum of tqe_cost for each Pauli rotation after the given TQE is * applied - * + * + * @param tqe * @return unsigned */ int tqe_cost_increase(const TQE& tqe) const override; From faa0536048639a38c80c49cae066b073f1a5fcf3 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 15 Oct 2024 18:25:30 +0100 Subject: [PATCH 47/57] cast size_t to unsigned --- tket/include/tket/Transformations/GreedyPauliOptimisation.hpp | 2 +- tket/src/Transformations/GreedyPauliOptimisation.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index 1ec4487fde..de6bb19d5c 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -384,7 +384,7 @@ class ConditionalBlock : public PauliNode { /** * @brief Sum of tqe_cost for each Pauli rotation after the given TQE is * applied - * + * * @param tqe * @return unsigned */ diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 9d82ce3e92..2ec1627241 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -543,7 +543,7 @@ static void consume_nodes( greedy_pauli_optimisation(discount_rate, depth_weight) .apply(cond_circ); Op_ptr cond = std::make_shared( - std::make_shared(cond_circ), cond_bits.size(), + std::make_shared(cond_circ), (unsigned)cond_bits.size(), cond_value); std::vector args = cond_bits; for (unsigned i = 0; i < cond_circ.n_qubits(); i++) { From e31abde912067a3939359303a1c9d5acba69c2b3 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 15 Oct 2024 18:40:29 +0100 Subject: [PATCH 48/57] remove nondeterminism from test --- tket/test/src/test_GreedyPauli.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index ac80643691..86f7990b72 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -459,17 +459,11 @@ SCENARIO("Complete synthesis") { circ.add_op(OpType::Measure, {0, 0}); circ.add_op(ClassicalX(), {0}); circ.add_op(OpType::Reset, {1}); - Circuit d(3, 1); - d.add_op(OpType::CZ, {0, 2}); - d.add_op(OpType::CZ, {0, 1}); - d.add_op(OpType::Rx, 0.3, {0}); - d.add_op(OpType::Measure, {0, 0}); - d.add_op(ClassicalX(), {0}); - d.add_op(OpType::CZ, {1, 0}); - d.add_op(OpType::CZ, {0, 2}); - d.add_op(OpType::Reset, {1}); REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); - REQUIRE(circ == d); + REQUIRE(circ.count_n_qubit_gates(2) == 4); + REQUIRE(circ.count_gates(OpType::ClassicalTransform) == 1); + REQUIRE(circ.count_gates(OpType::Measure) == 1); + REQUIRE(circ.count_gates(OpType::Reset) == 1); } GIVEN("Compile to ZZPhase") { Circuit circ(2); From 488429c1d5a9fe32420a58f43705151cbcd129c1 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 15 Oct 2024 18:53:40 +0100 Subject: [PATCH 49/57] Remove more nondeterminism --- tket/test/src/test_GreedyPauli.cpp | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index 86f7990b72..7478c7fc91 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -363,26 +363,8 @@ SCENARIO("Complete synthesis") { circ.add_op(OpType::Measure, {0, 0}); // can commute to the front circ.add_op(cond2, {1, 0, 1}); - Circuit d(2, 2); - // ZX 0.5 - d.add_conditional_gate(OpType::CX, {}, {0, 1}, {0}, 0); - d.add_conditional_gate(OpType::Sdg, {}, {0}, {0}, 0); - d.add_conditional_gate(OpType::V, {}, {1}, {0}, 0); - d.add_conditional_gate(OpType::Z, {}, {0}, {0}, 0); - d.add_op(OpType::Measure, {0, 0}); - // ZY 0.12 - // compute - d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); - d.add_conditional_gate(OpType::CY, {}, {0, 1}, {1}, 0); - d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); - // action - d.add_conditional_gate(OpType::Rz, {0.12}, {0}, {1}, 0); - // uncompute - d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); - d.add_conditional_gate(OpType::CY, {}, {0, 1}, {1}, 0); - d.add_conditional_gate(OpType::H, {}, {0}, {1}, 0); REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); - REQUIRE(circ == d); + REQUIRE(circ.count_n_qubit_gates(2) == 3); } GIVEN("Conditionals merging") { From 0def7b5084a2e5e1c6295b1fd5a7b58597373b89 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 15 Oct 2024 18:56:55 +0100 Subject: [PATCH 50/57] regen stubs --- pytket/pytket/_tket/passes.pyi | 6 +++++- pytket/pytket/_tket/transform.pyi | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pytket/pytket/_tket/passes.pyi b/pytket/pytket/_tket/passes.pyi index 1b8564f642..efc45514eb 100644 --- a/pytket/pytket/_tket/passes.pyi +++ b/pytket/pytket/_tket/passes.pyi @@ -440,12 +440,16 @@ def GlobalisePhasedX(squash: bool = True) -> BasePass: It is not recommended to use this pass with symbolic expressions, as in certain cases a blow-up in symbolic expression sizes may occur. """ -def GreedyPauliSimp(discount_rate: float = 0.7, depth_weight: float = 0.3) -> BasePass: +def GreedyPauliSimp(discount_rate: float = 0.7, depth_weight: float = 0.3, max_lookahead: int = 500, max_tqe_candidates: int = 500, seed: int = 0, allow_zzphase: bool = False) -> BasePass: """ Construct a pass that converts a circuit into a graph of Pauli gadgets to account for commutation and phase folding, and resynthesises them using a greedy algorithm adapted from arxiv.org/abs/2103.08602. The method for synthesising the final Clifford operator is adapted from arxiv.org/abs/2305.10966. :param discount_rate: Rate used to discount the cost impact from gadgets that are further away. Default to 0.7. :param depth_weight: Degree of depth optimisation. Default to 0.3. + :param max_tqe_candidates: Maximum number of 2-qubit Clifford gate candidates to evaluate at each step. Default to 500. + :param max_lookahead: Maximum lookahead when evaluating each Clifford gate candidate. Default to 500. + :param seed: Unsigned integer seed used for sampling candidates and tie breaking. Default to 0. + :param allow_zzphase: If set to True, allows the algorithm to implement 2-qubit rotations using ZZPhase gates when deemed optimal. Defaults to False. :return: a pass to perform the simplification """ def GuidedPauliSimp(strat: pytket._tket.transform.PauliSynthStrat = pytket._tket.transform.PauliSynthStrat.Sets, cx_config: pytket._tket.circuit.CXConfigType = pytket._tket.circuit.CXConfigType.Snake) -> BasePass: diff --git a/pytket/pytket/_tket/transform.pyi b/pytket/pytket/_tket/transform.pyi index 084c9a9cc5..4801b4238c 100644 --- a/pytket/pytket/_tket/transform.pyi +++ b/pytket/pytket/_tket/transform.pyi @@ -165,12 +165,16 @@ class Transform: It is not recommended to use this transformation with symbolic expressions, as in certain cases a blow-up in symbolic expression sizes may occur. """ @staticmethod - def GreedyPauliSimp(discount_rate: float = 0.7, depth_weight: float = 0.3) -> Transform: + def GreedyPauliSimp(discount_rate: float = 0.7, depth_weight: float = 0.3, max_tqe_candidates: int = 500, max_lookahead: int = 500, seed: int = 0, allow_zzphase: bool = False) -> Transform: """ Convert a circuit into a graph of Pauli gadgets to account for commutation and phase folding, and resynthesises them using a greedy algorithm adapted from arxiv.org/abs/2103.08602. The method for synthesising the final Clifford operator is adapted from arxiv.org/abs/2305.10966. :param discount_rate: Rate used to discount the cost impact from gadgets that are further away. Default to 0.7. :param depth_weight: Degree of depth optimisation. Default to 0.3. + :param max_tqe_candidates: Maximum number of 2-qubit Clifford gate candidates to evaluate at each step. Default to 500. + :param max_lookahead: Maximum lookahead when evaluating each Clifford gate candidate. Default to 500. + :param seed: Unsigned integer seed used for sampling candidates and tie breaking. Default to 0. + :param allow_zzphase: If set to True, allows the algorithm to implement 2-qubit rotations using ZZPhase gates when deemed optimal. Defaults to False. :return: a pass to perform the simplification """ @staticmethod From e5030e28278cc494d055fadc136fe9327668bb54 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Wed, 16 Oct 2024 14:26:41 +0100 Subject: [PATCH 51/57] Add debug info --- .../GreedyPauliOptimisation.hpp | 14 +++ tket/src/Transformations/GreedyPauliOps.cpp | 99 +++++++++++++++++++ .../GreedyPauliOptimisation.cpp | 40 +++++++- 3 files changed, 151 insertions(+), 2 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index de6bb19d5c..a1ab387619 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -92,6 +92,7 @@ struct TQE { bool operator<(const TQE& other) const { return std::tie(type, a, b) < std::tie(other.type, other.a, other.b); } + std::string repr() const; }; /** @@ -106,6 +107,7 @@ struct Rotation2Q { Expr angle; unsigned index; bool operator<(const Rotation2Q& other) const { return index < other.index; } + std::string repr() const; }; /** @@ -132,6 +134,7 @@ class PauliNode { virtual void swap(const unsigned& a, const unsigned& b); virtual CommuteInfo get_commute_info() const = 0; virtual std::vector reduction_tqes() const = 0; + virtual std::string repr() const = 0; virtual ~PauliNode(); }; @@ -304,6 +307,8 @@ class ClassicalNode : public PauliNode { CommuteInfo get_commute_info() const override; + std::string repr() const override; + protected: const std::vector args_; const Op_ptr op_; @@ -332,6 +337,8 @@ class PauliRotation : public SingleNode { CommuteInfo get_commute_info() const override; + std::string repr() const override; + protected: const Expr theta_; }; @@ -354,6 +361,8 @@ class MidMeasure : public SingleNode { CommuteInfo get_commute_info() const override; unsigned bit() const { return bit_; }; + std::string repr() const override; + protected: const unsigned bit_; }; @@ -415,6 +424,8 @@ class ConditionalBlock : public PauliNode { return rotations_; }; + std::string repr() const override; + protected: std::vector, bool, Expr>> rotations_; const std::vector cond_bits_; @@ -453,6 +464,8 @@ class PauliPropagation : public ACPairNode { unsigned qubit_index() const { return qubit_index_; }; + std::string repr() const override; + private: const unsigned qubit_index_; }; @@ -480,6 +493,7 @@ class Reset : public ACPairNode { PauliNodeType get_type() const override { return PauliNodeType::Reset; }; CommuteInfo get_commute_info() const override; + std::string repr() const override; }; typedef boost::adjacency_list< diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index 3e0af85795..84884848a3 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -26,6 +26,82 @@ namespace Transforms { namespace GreedyPauliSimp { +static std::string pauli_str(const Pauli& p) { + switch (p) { + case Pauli::I: + return "I"; + case Pauli::Z: + return "Z"; + case Pauli::X: + return "X"; + case Pauli::Y: + return "Y"; + } +} + +static std::string pauli_vec_str(const std::vector& pvec) { + std::string s; + for (unsigned i = 0; i < pvec.size(); i++) { + s += pauli_str(pvec[i]); + if (i != pvec.size() - 1) s += "-"; + } + return s; +} + +static std::string pauli_pair_str( + const std::vector& zvec, const std::vector& xvec) { + std::string s; + for (unsigned i = 0; i < zvec.size(); i++) { + s += pauli_str(zvec[i]); + s += "|"; + s += pauli_str(xvec[i]); + if (i != zvec.size() - 1) s += "-"; + } + return s; +} + +std::string TQE::repr() const { + std::stringstream ss; + ss << "TQE ["; + switch (type) { + case TQEType::XX: + ss << "XX"; + break; + case TQEType::XY: + ss << "XY"; + break; + case TQEType::XZ: + ss << "XZ"; + break; + case TQEType::YX: + ss << "YX"; + break; + case TQEType::YY: + ss << "YY"; + break; + case TQEType::YZ: + ss << "YZ"; + break; + case TQEType::ZX: + ss << "ZX"; + break; + case TQEType::ZY: + ss << "ZY"; + break; + case TQEType::ZZ: + ss << "ZZ"; + } + ss << " " << a << " " << b << "]"; + return ss.str(); +} + +std::string Rotation2Q::repr() const { + std::stringstream ss; + ss << "Rotation2Q [" << pauli_str(p_a) << pauli_str(p_b) << angle << " " << a + << " " << b << "]"; + return ss.str(); +} + static CommuteType get_pauli_pair_commute_type( const Pauli& p0, const Pauli& p1) { if (p0 == Pauli::I && p1 == Pauli::I) { @@ -306,6 +382,13 @@ PauliRotation::PauliRotation(std::vector string, bool sign, Expr theta) CommuteInfo PauliRotation::get_commute_info() const { return {{string_}, {}}; } +std::string PauliRotation::repr() const { + std::stringstream ss; + ss << "PauliRotation [" << pauli_vec_str(string_) << "," << this->angle() + << "]"; + return ss.str(); +} + ConditionalBlock::ConditionalBlock( std::vector, bool, Expr>> rotations, std::vector cond_bits, unsigned cond_value) @@ -374,6 +457,8 @@ void ConditionalBlock::append(const ConditionalBlock& other) { } } +std::string ConditionalBlock::repr() const { return "ConditionalBlock []"; } + // PauliPropagation PauliPropagation::PauliPropagation( std::vector z_string, std::vector x_string, bool z_sign, @@ -385,6 +470,10 @@ CommuteInfo PauliPropagation::get_commute_info() const { return {{z_string_, x_string_}, {}}; } +std::string PauliPropagation::repr() const { + return "PauliPropagation [" + pauli_pair_str(z_string_, x_string_) + "]"; +} + // ClassicalNode ClassicalNode::ClassicalNode(std::vector args, Op_ptr op) : args_(args), op_(op) {} @@ -397,6 +486,8 @@ CommuteInfo ClassicalNode::get_commute_info() const { return {{}, bits_info}; } +std::string ClassicalNode::repr() const { return "Classical []"; } + // MidMeasure MidMeasure::MidMeasure(std::vector string, bool sign, unsigned bit) : SingleNode(string, sign), bit_(bit) {} @@ -405,6 +496,10 @@ CommuteInfo MidMeasure::get_commute_info() const { return {{string_}, {{Bit(bit_), BitType::WRITE}}}; } +std::string MidMeasure::repr() const { + return "MidMeasure [" + pauli_vec_str(string_) + "]"; +} + // Reset Reset::Reset( std::vector z_string, std::vector x_string, bool z_sign, @@ -415,6 +510,10 @@ CommuteInfo Reset::get_commute_info() const { return {{z_string_, x_string_}, {}}; } +std::string Reset::repr() const { + return "Reset [" + pauli_pair_str(z_string_, x_string_) + "]"; +} + } // namespace GreedyPauliSimp } // namespace Transforms diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 2ec1627241..c357f049c0 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -148,16 +148,21 @@ static double default_pauliexp_tqe_cost( double exp_cost = 0; double tab_cost = 0; unsigned count = 0; + std::cout << "cost tqe " << tqe.repr() << ":\n"; for (const std::vector& rotation_set : rotation_sets) { for (const PauliNode_ptr& node : rotation_set) { - exp_cost += weight * node->tqe_cost_increase(tqe); + int cost = node->tqe_cost_increase(tqe); + exp_cost += weight * cost; + std::cout << node->repr() << " " << weight << " " << cost << "\n"; if (++count >= max_lookahead) break; } if (count >= max_lookahead) break; weight *= discount; } for (const PauliNode_ptr& node : rows) { - tab_cost += weight * node->tqe_cost_increase(tqe); + int cost = node->tqe_cost_increase(tqe); + tab_cost += weight * cost; + std::cout << node->repr() << " " << weight << " " << cost << "\n"; if (++count >= max_lookahead) break; } return exp_cost + tab_cost; @@ -604,6 +609,14 @@ static void pauli_exps_synthesis( DepthTracker& depth_tracker, double discount_rate, double depth_weight, unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed, bool allow_zzphase) { + std::cout << "Initial nodes:\n"; + for (const std::vector& set : rotation_sets) { + std::cout << "{"; + for (const PauliNode_ptr& node : set) { + std::cout << node->repr() << ","; + } + std::cout << "}\n"; + } while (true) { consume_nodes( rotation_sets, circ, depth_tracker, discount_rate, depth_weight); @@ -667,10 +680,32 @@ static void pauli_exps_synthesis( } } // select the best one + std::cout << "first set nodes :\n"; + for (const PauliNode_ptr& node : first_set) { + std::cout << node->repr() << "\n"; + } + std::cout << "tableau rows :\n"; + for (const PauliNode_ptr& node : rows) { + std::cout << node->repr() << "\n"; + } + std::cout << "tqe candidates :\n"; + for (auto it = tqe_candidates_cost.begin(); it != tqe_candidates_cost.end(); + it++) { + std::cout << it->first.repr() << " " << it->second[0] << " " + << it->second[1] << "\n"; + } + std::cout << "rot2q candidates :\n"; + for (auto it = rot2q_gates_cost.begin(); it != rot2q_gates_cost.end(); + it++) { + std::cout << it->first.repr() << " " << it->second[0] << " " + << it->second[1] << "\n"; + } + auto [min_tqes, min_rot2qs] = minmax_selection( tqe_candidates_cost, rot2q_gates_cost, {1, depth_weight}); if (min_rot2qs.empty()) { TQE selected_tqe = sample_random_tqe(min_tqes, seed); + std::cout << "Apply " << selected_tqe.repr() << "\n"; // apply TQE apply_tqe_to_circ(selected_tqe, circ); depth_tracker.add_2q_gate(selected_tqe.a, selected_tqe.b); @@ -690,6 +725,7 @@ static void pauli_exps_synthesis( return r1.index > r2.index; }); for (const Rotation2Q& rot : min_rot2qs) { + std::cout << "Apply " << rot.repr() << "\n"; apply_rot2q_to_circ(rot, circ); first_set.erase(first_set.begin() + rot.index); } From 65ee40b616e07a7a69e74efb732cdf3609dc2be7 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Wed, 16 Oct 2024 14:26:53 +0100 Subject: [PATCH 52/57] Revert "Add debug info" This reverts commit e5030e28278cc494d055fadc136fe9327668bb54. --- .../GreedyPauliOptimisation.hpp | 14 --- tket/src/Transformations/GreedyPauliOps.cpp | 99 ------------------- .../GreedyPauliOptimisation.cpp | 40 +------- 3 files changed, 2 insertions(+), 151 deletions(-) diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp index a1ab387619..de6bb19d5c 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -92,7 +92,6 @@ struct TQE { bool operator<(const TQE& other) const { return std::tie(type, a, b) < std::tie(other.type, other.a, other.b); } - std::string repr() const; }; /** @@ -107,7 +106,6 @@ struct Rotation2Q { Expr angle; unsigned index; bool operator<(const Rotation2Q& other) const { return index < other.index; } - std::string repr() const; }; /** @@ -134,7 +132,6 @@ class PauliNode { virtual void swap(const unsigned& a, const unsigned& b); virtual CommuteInfo get_commute_info() const = 0; virtual std::vector reduction_tqes() const = 0; - virtual std::string repr() const = 0; virtual ~PauliNode(); }; @@ -307,8 +304,6 @@ class ClassicalNode : public PauliNode { CommuteInfo get_commute_info() const override; - std::string repr() const override; - protected: const std::vector args_; const Op_ptr op_; @@ -337,8 +332,6 @@ class PauliRotation : public SingleNode { CommuteInfo get_commute_info() const override; - std::string repr() const override; - protected: const Expr theta_; }; @@ -361,8 +354,6 @@ class MidMeasure : public SingleNode { CommuteInfo get_commute_info() const override; unsigned bit() const { return bit_; }; - std::string repr() const override; - protected: const unsigned bit_; }; @@ -424,8 +415,6 @@ class ConditionalBlock : public PauliNode { return rotations_; }; - std::string repr() const override; - protected: std::vector, bool, Expr>> rotations_; const std::vector cond_bits_; @@ -464,8 +453,6 @@ class PauliPropagation : public ACPairNode { unsigned qubit_index() const { return qubit_index_; }; - std::string repr() const override; - private: const unsigned qubit_index_; }; @@ -493,7 +480,6 @@ class Reset : public ACPairNode { PauliNodeType get_type() const override { return PauliNodeType::Reset; }; CommuteInfo get_commute_info() const override; - std::string repr() const override; }; typedef boost::adjacency_list< diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp index 84884848a3..3e0af85795 100644 --- a/tket/src/Transformations/GreedyPauliOps.cpp +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -26,82 +26,6 @@ namespace Transforms { namespace GreedyPauliSimp { -static std::string pauli_str(const Pauli& p) { - switch (p) { - case Pauli::I: - return "I"; - case Pauli::Z: - return "Z"; - case Pauli::X: - return "X"; - case Pauli::Y: - return "Y"; - } -} - -static std::string pauli_vec_str(const std::vector& pvec) { - std::string s; - for (unsigned i = 0; i < pvec.size(); i++) { - s += pauli_str(pvec[i]); - if (i != pvec.size() - 1) s += "-"; - } - return s; -} - -static std::string pauli_pair_str( - const std::vector& zvec, const std::vector& xvec) { - std::string s; - for (unsigned i = 0; i < zvec.size(); i++) { - s += pauli_str(zvec[i]); - s += "|"; - s += pauli_str(xvec[i]); - if (i != zvec.size() - 1) s += "-"; - } - return s; -} - -std::string TQE::repr() const { - std::stringstream ss; - ss << "TQE ["; - switch (type) { - case TQEType::XX: - ss << "XX"; - break; - case TQEType::XY: - ss << "XY"; - break; - case TQEType::XZ: - ss << "XZ"; - break; - case TQEType::YX: - ss << "YX"; - break; - case TQEType::YY: - ss << "YY"; - break; - case TQEType::YZ: - ss << "YZ"; - break; - case TQEType::ZX: - ss << "ZX"; - break; - case TQEType::ZY: - ss << "ZY"; - break; - case TQEType::ZZ: - ss << "ZZ"; - } - ss << " " << a << " " << b << "]"; - return ss.str(); -} - -std::string Rotation2Q::repr() const { - std::stringstream ss; - ss << "Rotation2Q [" << pauli_str(p_a) << pauli_str(p_b) << angle << " " << a - << " " << b << "]"; - return ss.str(); -} - static CommuteType get_pauli_pair_commute_type( const Pauli& p0, const Pauli& p1) { if (p0 == Pauli::I && p1 == Pauli::I) { @@ -382,13 +306,6 @@ PauliRotation::PauliRotation(std::vector string, bool sign, Expr theta) CommuteInfo PauliRotation::get_commute_info() const { return {{string_}, {}}; } -std::string PauliRotation::repr() const { - std::stringstream ss; - ss << "PauliRotation [" << pauli_vec_str(string_) << "," << this->angle() - << "]"; - return ss.str(); -} - ConditionalBlock::ConditionalBlock( std::vector, bool, Expr>> rotations, std::vector cond_bits, unsigned cond_value) @@ -457,8 +374,6 @@ void ConditionalBlock::append(const ConditionalBlock& other) { } } -std::string ConditionalBlock::repr() const { return "ConditionalBlock []"; } - // PauliPropagation PauliPropagation::PauliPropagation( std::vector z_string, std::vector x_string, bool z_sign, @@ -470,10 +385,6 @@ CommuteInfo PauliPropagation::get_commute_info() const { return {{z_string_, x_string_}, {}}; } -std::string PauliPropagation::repr() const { - return "PauliPropagation [" + pauli_pair_str(z_string_, x_string_) + "]"; -} - // ClassicalNode ClassicalNode::ClassicalNode(std::vector args, Op_ptr op) : args_(args), op_(op) {} @@ -486,8 +397,6 @@ CommuteInfo ClassicalNode::get_commute_info() const { return {{}, bits_info}; } -std::string ClassicalNode::repr() const { return "Classical []"; } - // MidMeasure MidMeasure::MidMeasure(std::vector string, bool sign, unsigned bit) : SingleNode(string, sign), bit_(bit) {} @@ -496,10 +405,6 @@ CommuteInfo MidMeasure::get_commute_info() const { return {{string_}, {{Bit(bit_), BitType::WRITE}}}; } -std::string MidMeasure::repr() const { - return "MidMeasure [" + pauli_vec_str(string_) + "]"; -} - // Reset Reset::Reset( std::vector z_string, std::vector x_string, bool z_sign, @@ -510,10 +415,6 @@ CommuteInfo Reset::get_commute_info() const { return {{z_string_, x_string_}, {}}; } -std::string Reset::repr() const { - return "Reset [" + pauli_pair_str(z_string_, x_string_) + "]"; -} - } // namespace GreedyPauliSimp } // namespace Transforms diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index c357f049c0..2ec1627241 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -148,21 +148,16 @@ static double default_pauliexp_tqe_cost( double exp_cost = 0; double tab_cost = 0; unsigned count = 0; - std::cout << "cost tqe " << tqe.repr() << ":\n"; for (const std::vector& rotation_set : rotation_sets) { for (const PauliNode_ptr& node : rotation_set) { - int cost = node->tqe_cost_increase(tqe); - exp_cost += weight * cost; - std::cout << node->repr() << " " << weight << " " << cost << "\n"; + exp_cost += weight * node->tqe_cost_increase(tqe); if (++count >= max_lookahead) break; } if (count >= max_lookahead) break; weight *= discount; } for (const PauliNode_ptr& node : rows) { - int cost = node->tqe_cost_increase(tqe); - tab_cost += weight * cost; - std::cout << node->repr() << " " << weight << " " << cost << "\n"; + tab_cost += weight * node->tqe_cost_increase(tqe); if (++count >= max_lookahead) break; } return exp_cost + tab_cost; @@ -609,14 +604,6 @@ static void pauli_exps_synthesis( DepthTracker& depth_tracker, double discount_rate, double depth_weight, unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed, bool allow_zzphase) { - std::cout << "Initial nodes:\n"; - for (const std::vector& set : rotation_sets) { - std::cout << "{"; - for (const PauliNode_ptr& node : set) { - std::cout << node->repr() << ","; - } - std::cout << "}\n"; - } while (true) { consume_nodes( rotation_sets, circ, depth_tracker, discount_rate, depth_weight); @@ -680,32 +667,10 @@ static void pauli_exps_synthesis( } } // select the best one - std::cout << "first set nodes :\n"; - for (const PauliNode_ptr& node : first_set) { - std::cout << node->repr() << "\n"; - } - std::cout << "tableau rows :\n"; - for (const PauliNode_ptr& node : rows) { - std::cout << node->repr() << "\n"; - } - std::cout << "tqe candidates :\n"; - for (auto it = tqe_candidates_cost.begin(); it != tqe_candidates_cost.end(); - it++) { - std::cout << it->first.repr() << " " << it->second[0] << " " - << it->second[1] << "\n"; - } - std::cout << "rot2q candidates :\n"; - for (auto it = rot2q_gates_cost.begin(); it != rot2q_gates_cost.end(); - it++) { - std::cout << it->first.repr() << " " << it->second[0] << " " - << it->second[1] << "\n"; - } - auto [min_tqes, min_rot2qs] = minmax_selection( tqe_candidates_cost, rot2q_gates_cost, {1, depth_weight}); if (min_rot2qs.empty()) { TQE selected_tqe = sample_random_tqe(min_tqes, seed); - std::cout << "Apply " << selected_tqe.repr() << "\n"; // apply TQE apply_tqe_to_circ(selected_tqe, circ); depth_tracker.add_2q_gate(selected_tqe.a, selected_tqe.b); @@ -725,7 +690,6 @@ static void pauli_exps_synthesis( return r1.index > r2.index; }); for (const Rotation2Q& rot : min_rot2qs) { - std::cout << "Apply " << rot.repr() << "\n"; apply_rot2q_to_circ(rot, circ); first_set.erase(first_set.begin() + rot.index); } From 38d3c6fc8a5f18ff74149d2b0f25458968a4aa6d Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Tue, 22 Oct 2024 10:16:30 +0100 Subject: [PATCH 53/57] Replace implicit wire swaps in optimised conditional circuits --- tket/src/Transformations/GreedyPauliOptimisation.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 2ec1627241..cb8d857dc3 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -542,6 +542,8 @@ static void consume_nodes( } greedy_pauli_optimisation(discount_rate, depth_weight) .apply(cond_circ); + // replace implicit wire swaps + cond_circ.replace_all_implicit_wire_swaps(); Op_ptr cond = std::make_shared( std::make_shared(cond_circ), (unsigned)cond_bits.size(), cond_value); From a9f148e0a2263efbda677fa8286e943431869567 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Wed, 23 Oct 2024 14:56:30 +0100 Subject: [PATCH 54/57] re-organise changelog --- pytket/docs/changelog.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 6fcb51def6..3b14c469e0 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -8,14 +8,6 @@ Features: * Add new `ClExprOp` operation type as an alternative to `ClassicalExpBox`; add option to use this when converting from QASM. - -Fixes: - -* Fix small default display screen for circuit renderer. - -General: - -* Support Python 3.13. * Several updates to `GreedyPauliSimp`: * Support for mid-circuit measurements, resets, conditionals, @@ -29,6 +21,14 @@ General: * New parameter `allow_zzphase` allows the algorithm to implement 2-qubit rotations using ZZPhase gates when deemed optimal. +Fixes: + +* Fix small default display screen for circuit renderer. + +General: + +* Support Python 3.13. + 1.33.1 (October 2024) --------------------- From 08eb8c810db25ae3b71870c5bfa0fbe7d139f10b Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Wed, 23 Oct 2024 14:56:40 +0100 Subject: [PATCH 55/57] Bump tket version --- pytket/conanfile.py | 2 +- tket/conanfile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 8674f27a3d..80e7e0b31c 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -38,7 +38,7 @@ def requirements(self): self.requires("pybind11_json/0.2.14") self.requires("symengine/0.12.0") self.requires("tkassert/0.3.4@tket/stable") - self.requires("tket/1.3.34@tket/stable") + self.requires("tket/1.3.35@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tktokenswap/0.3.9@tket/stable") diff --git a/tket/conanfile.py b/tket/conanfile.py index 2e7e0e9b43..6439e45b7a 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.3.34" + version = "1.3.35" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" From 92462fc84054d8dc172fc230128baf4bd1c1a882 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Thu, 24 Oct 2024 11:52:48 +0100 Subject: [PATCH 56/57] fix bug in sign correction --- tket/src/Transformations/GreedyPauliOptimisation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index cb8d857dc3..e052102656 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -493,13 +493,13 @@ static void consume_nodes( break; } case Pauli::Y: { - if (node.sign()) { + if (!node.sign()) { circ.add_op(OpType::Vdg, {q_index}); } else { circ.add_op(OpType::V, {q_index}); } circ.add_measure(q_index, node.bit()); - if (node.sign()) { + if (!node.sign()) { circ.add_op(OpType::V, {q_index}); } else { circ.add_op(OpType::Vdg, {q_index}); From b1b93ebd55ea4e31e7f06221b4f1a057b778b568 Mon Sep 17 00:00:00 2001 From: yao-cqc Date: Thu, 24 Oct 2024 11:52:54 +0100 Subject: [PATCH 57/57] Add more tests --- tket/test/src/test_GreedyPauli.cpp | 112 +++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp index 7478c7fc91..366143ece4 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -28,6 +28,20 @@ namespace tket { namespace test_GreedyPauliSimp { +SCENARIO("Exception handling") { + GIVEN("Invalid arguments") { + Circuit circ(1); + REQUIRE_THROWS_MATCHES( + Transforms::greedy_pauli_optimisation(0.3, 0.3, 0, 10).apply(circ), + Transforms::GreedyPauliSimp::GreedyPauliSimpError, + MessageContains("max_lookahead must be greater than 0.")); + REQUIRE_THROWS_MATCHES( + Transforms::greedy_pauli_optimisation(0.3, 0.3, 10, 0).apply(circ), + Transforms::GreedyPauliSimp::GreedyPauliSimpError, + MessageContains("max_tqe_candidates must be greater than 0.")); + } +} + SCENARIO("Clifford synthesis") { GIVEN("Empty circuit") { Circuit circ(3); @@ -425,6 +439,68 @@ SCENARIO("Complete synthesis") { REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } + GIVEN("Circuit with mid-circuit measurements 2") { + // -X + Circuit c1(1, 1); + c1.add_op(OpType::Z, {0}); + c1.add_op(OpType::H, {0}); + c1.add_op(OpType::Measure, {0, 0}); + c1.add_op(OpType::T, {0}); + Circuit d1(1, 1); + d1.add_op(OpType::H, {0}); + d1.add_op(OpType::X, {0}); + d1.add_op(OpType::Measure, {0, 0}); + d1.add_op(OpType::X, {0}); + d1.add_op(OpType::H, {0}); + d1.add_op(OpType::Rx, 3.75, {0}); + d1.add_op(OpType::H, {0}); + d1.add_op(OpType::X, {0}); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(c1)); + REQUIRE(c1 == d1); + // Y + Circuit c2(1, 1); + c2.add_op(OpType::V, {0}); + c2.add_op(OpType::Measure, {0, 0}); + c2.add_op(OpType::T, {0}); + Circuit d2(1, 1); + d2.add_op(OpType::V, {0}); + d2.add_op(OpType::Measure, {0, 0}); + d2.add_op(OpType::Vdg, {0}); + d2.add_op(OpType::Ry, 0.25, {0}); + d2.add_op(OpType::V, {0}); + // Vdg;Ry(0.25);V = T + REQUIRE(Transforms::greedy_pauli_optimisation().apply(c2)); + REQUIRE(c2 == d2); + // -Y + Circuit c3(1, 1); + c3.add_op(OpType::Vdg, {0}); + c3.add_op(OpType::Measure, {0, 0}); + c3.add_op(OpType::T, {0}); + Circuit d3(1, 1); + d3.add_op(OpType::Vdg, {0}); + d3.add_op(OpType::Measure, {0, 0}); + d3.add_op(OpType::V, {0}); + d3.add_op(OpType::Ry, 3.75, {0}); + d3.add_op(OpType::V, {0}); + d3.add_op(OpType::X, {0}); + // V;Ry(3.75);V;X = T + REQUIRE(Transforms::greedy_pauli_optimisation().apply(c3)); + REQUIRE(c3 == d3); + // -Z + Circuit c4(1, 1); + c4.add_op(OpType::X, {0}); + c4.add_op(OpType::Measure, {0, 0}); + c4.add_op(OpType::T, {0}); + Circuit d4(1, 1); + d4.add_op(OpType::X, {0}); + d4.add_op(OpType::Measure, {0, 0}); + d4.add_op(OpType::X, {0}); + d4.add_op(OpType::Rz, 3.75, {0}); + d4.add_op(OpType::X, {0}); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(c4)); + REQUIRE(c4 == d4); + } + GIVEN("Circuit with resets") { Circuit circ(2); circ.add_op(OpType::CX, {0, 1}); @@ -433,6 +509,41 @@ SCENARIO("Complete synthesis") { REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); REQUIRE(circ == d); } + + GIVEN("Circuit with resets 2") { + // -X/Z + Circuit c1(1); + c1.add_op(OpType::Z, {0}); + c1.add_op(OpType::H, {0}); + c1.add_op(OpType::Reset, {0}); + Circuit d1(1); + d1.add_op(OpType::H, {0}); + d1.add_op(OpType::X, {0}); + d1.add_op(OpType::Reset, {0}); + d1.add_op(OpType::X, {0}); + d1.add_op(OpType::H, {0}); + d1.add_op(OpType::H, {0}); + d1.add_op(OpType::X, {0}); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(c1)); + REQUIRE(c1 == d1); + + // X/-Z + Circuit c2(1); + c2.add_op(OpType::X, {0}); + c2.add_op(OpType::H, {0}); + c2.add_op(OpType::Reset, {0}); + Circuit d2(1); + d2.add_op(OpType::H, {0}); + d2.add_op(OpType::Z, {0}); + d2.add_op(OpType::Reset, {0}); + d2.add_op(OpType::Z, {0}); + d2.add_op(OpType::H, {0}); + d2.add_op(OpType::H, {0}); + d2.add_op(OpType::Z, {0}); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(c2)); + REQUIRE(c2 == d2); + } + GIVEN("Circuit with measures, classicals, and resets") { Circuit circ(3, 1); circ.add_box( @@ -533,6 +644,7 @@ SCENARIO("Complete synthesis") { REQUIRE(d.count_n_qubit_gates(2) == 4); } } + SCENARIO("Test GreedyPauliSimp for individual gates") { Circuit circ(1); circ.add_op(OpType::Z, {0});