From 94de5fa82c9a379c90130a765105337be81f95a9 Mon Sep 17 00:00:00 2001 From: yao-cqc <75305462+yao-cqc@users.noreply.github.com> Date: Thu, 24 Oct 2024 13:00:22 +0100 Subject: [PATCH] Refactor greedy pauli simp (#1611) * Initial refactor * Refactor node types * Move nodes definition into a new file * Refactor synthesis * Cleanup * More refactoring * Refactor pauli graph converters * Initial implementation of GPGraph * Migrate to GPGraph * Ignore global phase * Add supports for conditional gates * Add support for classical ops * flatten_registers should only rename qubits and bits * Replace unsigned with Bit * Revert "Replace unsigned with Bit" This reverts commit 9e9a2fd556dfdb47873be8dae01bd04bda1d772e. * Manually check if qubits and bits can be flattened since ``is_simple()`` doesn't work with wasm * Consistent enum names * Add support for mid-circuit measurement * Rename variables * Add support for resets * Update docstrings * Update pass predicate * Add more tests * bump tket version * Add changelog entry * Fix docs errors * bump tket version * Remove unused headers * bump tket version in pytket * uncomment lines * remove consts in SQ_CLIFF_DAGGER * Merging conditionals * Revert "Merging conditionals" This reverts commit f5138f1357edeabd4cee884ce3512535901a447e. * Optimise conditional handling * remove ConditionalPauliRotation * Remove clifford reduction and pass parameters when synthesis conditionals * Seeded tie breaking * Add limits to the search space * Allowing ZZPhase gates * update binder and serialisation * Add test for ops handling in python * add note for AC node cost * bump tket version * update changelog * fix changelog format * add missing prams in docstrings * cast size_t to unsigned * remove nondeterminism from test * Remove more nondeterminism * regen stubs * Add debug info * Revert "Add debug info" This reverts commit e5030e28278cc494d055fadc136fe9327668bb54. * Replace implicit wire swaps in optimised conditional circuits * re-organise changelog * Bump tket version * fix bug in sign correction * Add more tests --- pytket/binders/passes.cpp | 13 +- pytket/binders/transform.cpp | 13 +- pytket/conanfile.py | 2 +- pytket/docs/changelog.rst | 12 + pytket/pytket/_tket/passes.pyi | 6 +- pytket/pytket/_tket/transform.pyi | 6 +- pytket/tests/passes_serialisation_test.py | 10 +- pytket/tests/predicates_test.py | 52 +- schemas/compiler_pass_v1.json | 24 +- tket/CMakeLists.txt | 2 + tket/conanfile.py | 2 +- .../tket/Predicates/PassGenerators.hpp | 9 +- .../GreedyPauliOptimisation.hpp | 574 ++++- .../GreedyPauliOptimisationLookupTables.hpp | 2032 +++++------------ tket/src/Circuit/basic_circ_manip.cpp | 2 +- tket/src/Predicates/CompilerPass.cpp | 14 +- tket/src/Predicates/PassGenerators.cpp | 61 +- .../Transformations/GreedyPauliConverters.cpp | 524 +++++ tket/src/Transformations/GreedyPauliOps.cpp | 422 ++++ .../GreedyPauliOptimisation.cpp | 1172 ++++------ tket/test/src/test_GreedyPauli.cpp | 484 +++- 21 files changed, 3100 insertions(+), 2336 deletions(-) create mode 100644 tket/src/Transformations/GreedyPauliConverters.cpp create mode 100644 tket/src/Transformations/GreedyPauliOps.cpp 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/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/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index c2a77a4d16..755549da4d 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -8,6 +8,18 @@ Features: * Add new `ClExprOp` operation type as an alternative to `ClassicalExpBox`; add option to use this when converting from QASM. +* 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. Fixes: 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 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/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: 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/CMakeLists.txt b/tket/CMakeLists.txt index 13b4c7df97..41808ece11 100644 --- a/tket/CMakeLists.txt +++ b/tket/CMakeLists.txt @@ -250,6 +250,8 @@ target_sources(tket src/Transformations/ContextualReduction.cpp src/Transformations/Decomposition.cpp src/Transformations/GreedyPauliOptimisation.cpp + src/Transformations/GreedyPauliConverters.cpp + src/Transformations/GreedyPauliOps.cpp src/Transformations/MeasurePass.cpp src/Transformations/OptimisationPass.cpp src/Transformations/PauliOptimisation.cpp 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" 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 1a7cdbbf0f..de6bb19d5c 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 { @@ -23,6 +24,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,49 +46,116 @@ enum class TQEType : unsigned { ZZ, }; +enum class PauliNodeType { + // Pauli 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. + PauliPropagation, + // Conditional Pauli rotations + ConditionalBlock, + // Classical operation + ClassicalNode, + // Mid-circuit measurement + MidMeasure, + // Reset + Reset, +}; + /** - * @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 CommuteType : unsigned { + // Both are (I)dentity + I, + // (A)nti-commute + A, + // (C)ommute and not both identity + C, +}; + +enum class BitType : unsigned { + READ, + WRITE, +}; + +/** + * @brief Struct for 2-qubit entangled Clifford gates + * + */ +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 + * 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 Type for 2-qubit entangled Clifford gates + * @brief Base class for nodes in the Greedy Pauli graph * */ -using TQE = std::tuple; +class PauliNode { + public: + 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; + 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(); +}; + +typedef std::shared_ptr PauliNode_ptr; /** - * @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 Base class for nodes defined by a single Pauli string */ -class PauliExpNode { +class SingleNode : public PauliNode { public: /** - * @brief Construct a new PauliExpNode object. + * @brief Construct a new SinglePauliNode object. * - * @param support_vec the support vector - * @param theta the rotation angle in half-turns + * @param string the Pauli string + * @param sign sign of the Pauli string */ - PauliExpNode(std::vector support_vec, 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 tqe_cost_; } + unsigned tqe_cost() const override; /** * @brief Number of TQEs would required to reduce the weight to 1 @@ -89,123 +163,462 @@ class PauliExpNode { * * @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 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_; - Expr theta_; - unsigned tqe_cost_; + bool sign() const { return sign_; }; + + const std::vector& string() const { return string_; }; + + protected: + std::vector string_; + bool sign_; + // 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 Base class for nodes defined by a pair of anti-commuting Pauli strings */ -class TableauRowNode { +class ACPairNode : public PauliNode { public: /** - * @brief Construct a new TableauRowNode object. + * @brief Construct a new ACPairNode object * - * @param support_vec the support vector + * @param z_string + * @param x_string + * @param z_sign + * @param x_sign */ - TableauRowNode(std::vector support_vec); + ACPairNode( + 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 * * @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 + * @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; + int tqe_cost_increase(const TQE& tqe) const override; /** * @brief Update the support vector with a TQE gate * * @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 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 + */ + 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 override; + + /** + * @brief Return the index and value of the first anti-commute entry + */ + std::tuple first_support() const; + + bool z_sign() const { return z_sign_; }; + + bool x_sign() const { return x_sign_; }; + + const std::vector& z_string() const { return z_string_; }; + + const std::vector& x_string() const { return x_string_; }; + + protected: + std::vector z_string_; + std::vector x_string_; + bool z_sign_; + bool x_sign_; + // extra cached data used by greedy synthesis + std::vector commute_type_vec_; + unsigned n_commute_entries_; + unsigned n_anti_commute_entries_; +}; + +/** + * @brief Black box node for classical Ops + */ +class ClassicalNode : public PauliNode { + public: + ClassicalNode(std::vector args, Op_ptr op); + + 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; }; + 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: + const std::vector args_; + const Op_ptr op_; +}; + +/** + * @brief A Pauli exponential defined by a dense Pauli string + * and a rotation angle + */ +class PauliRotation : public SingleNode { + public: + /** + * @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); + + PauliNodeType get_type() const override { + return PauliNodeType::PauliRotation; + }; + + Expr angle() const { return sign_ ? theta_ : -theta_; }; + + CommuteInfo get_commute_info() const override; + + protected: + const Expr theta_; +}; + +/** + * @brief Measurement that has quantum or classical successors + */ +class MidMeasure : public SingleNode { + public: + /** + * @brief Construct a new Mid Measure object + * + * @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); + + PauliNodeType get_type() const override { return PauliNodeType::MidMeasure; }; + CommuteInfo get_commute_info() const override; + unsigned bit() const { return bit_; }; + + protected: + const unsigned bit_; +}; + +/** + * @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 + * + * @param tqe + * @return unsigned + */ + int tqe_cost_increase(const TQE& tqe) const override; + + /** + * @brief Update the all Pauli rotations with the given TQE + * + * @param tqe */ - std::vector reduction_tqes() const; + 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. + * 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 Return the index and value of the first support + * @brief Construct a new PauliPropagation object + * + * @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 */ - std::pair first_support() const; + PauliPropagation( + 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; + }; + + CommuteInfo get_commute_info() const override; + + unsigned qubit_index() const { return qubit_index_; }; private: - std::vector support_vec_; - unsigned n_weaks_; - unsigned n_strongs_; - unsigned tqe_cost_; + const unsigned qubit_index_; +}; + +/** + * @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); + + PauliNodeType get_type() const override { return PauliNodeType::Reset; }; + CommuteInfo get_commute_info() const override; }; +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; + +typedef boost::adj_list_vertex_property_map< + GPDAG, int, int&, boost::vertex_index_t> + GPVIndex; + /** - * @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. + * @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. + * + * - 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. + * + * 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. */ -enum class SupportType : unsigned { - Strong, - Weak, - No, +class GPGraph { + 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> + get_sequence(); + + private: + /** + * 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 + * to the graph. + */ + void apply_gate_at_end( + 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_paulis_at_end( + const std::vector, Expr>>& rotations, + 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); + + /** + * 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_; + 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 end_measures_; + + GPVertSet start_line_; + GPVertSet end_line_; }; /** - * @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 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 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 * @param depth_weight + * @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); + 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, bool allow_zzphase = false); /** * @brief Synthesise a set of unordered Pauli exponentials by applying Clifford @@ -214,16 +627,23 @@ Circuit greedy_pauli_graph_synthesis( * * @param unordered_set * @param depth_weight + * @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); + const std::vector& unordered_set, double depth_weight = 0.3, + unsigned max_lookahead = 500, unsigned max_tqe_candidates = 500, + unsigned seed = 0, bool allow_zzphase = false); } // 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 max_lookahead = 500, unsigned max_tqe_candidates = 500, + unsigned seed = 0, bool allow_zzphase = false); } // namespace Transforms diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp index 2445513636..a85a12b8bf 100644 --- a/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp +++ b/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp @@ -22,1545 +22,563 @@ 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 static_cast(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< + 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}}, + {{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 Given a strong support in a factor support vector, returns the local - * clifford gates that turn it into an identity (i.e. 9). - */ -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 SQ_CLIFF_DAGGER = { + {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 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::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}}, + {{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 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++)}); } } 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 469418a04f..610caabd84 100644 --- a/tket/src/Predicates/PassGenerators.cpp +++ b/tket/src/Predicates/PassGenerators.cpp @@ -1013,25 +1013,46 @@ 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); - 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}; +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, + 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}}; @@ -1042,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); } diff --git a/tket/src/Transformations/GreedyPauliConverters.cpp b/tket/src/Transformations/GreedyPauliConverters.cpp new file mode 100644 index 0000000000..86cbdc56db --- /dev/null +++ b/tket/src/Transformations/GreedyPauliConverters.cpp @@ -0,0 +1,524 @@ +// 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/OpType/OpType.hpp" +#include "tket/Transformations/GreedyPauliOptimisation.hpp" +#include "tket/Transformations/Transform.hpp" + +namespace tket { + +namespace Transforms { + +namespace GreedyPauliSimp { + +// 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; +} + +std::tuple, std::vector> +gpg_from_unordered_set(const std::vector& unordered_set) { + std::vector rotation_set; + 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( + std::make_shared(pauli.string, true, pauli.coeff)); + } + UnitaryRevTableau tab(n_qubits); + std::vector rows = get_nodes_from_tableau(tab, n_qubits); + return {rotation_set, rows}; +} + +// 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 (const auto& pair : pauli.string) { + string[pair.first.index().at(0)] = pair.second; + } + return {string, sign ? angle : -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) { + 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; + } + } + // 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 true; +} + +GPGraph::GPGraph(const Circuit& circ) + : n_qubits_(circ.n_qubits()), n_bits_(circ.n_bits()) { + 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_); + for (const Command& cmd : circ.get_commands()) { + apply_gate_at_end(cmd); + } +} + +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; +} + +// 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; + 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 GPVert& 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]; + // 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 && + compare_node->get_type() == PauliNodeType::PauliRotation) { + 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.angle() + rot2.angle(); + 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(), true, merged_angle); + } + return; + } + } + // Commute and continue searching + GPVertSet 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_paulis_at_end( + const std::vector, Expr>>& rotations, + const qubit_vector_t& qbs, bool conditional, + std::vector cond_bits, unsigned cond_value) { + 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}); + } + + // if conditional we add a ConditionalBlock otherwise we add individual + // rotations. + if (conditional) { + PauliNode_ptr node = std::make_shared( + conj_rotations, cond_bits, cond_value); + apply_node_at_end(node); + } else { + 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); + } + } +} + +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(); + OpType type = op->get_type(); + + for (const UnitID& arg : args) { + if (arg.type() == UnitType::Qubit) { + 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. + 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()) { + 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); + } + } + } + + 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: { + end_measures_.insert( + {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; + } + 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::CX: + case OpType::CY: + 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: { + // ignore global phase + return; + } + case OpType::Rz: { + pauli_rots.push_back({{Pauli::Z}, op->get_params().at(0)}); + break; + } + case OpType::Rx: { + pauli_rots.push_back({{Pauli::X}, op->get_params().at(0)}); + break; + } + case OpType::Ry: { + 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); + 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: { + pauli_rots.push_back({{Pauli::Z}, 0.25}); + break; + } + case OpType::Tdg: { + pauli_rots.push_back({{Pauli::Z}, -0.25}); + break; + } + case OpType::ZZMax: { + 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); + pauli_rots.push_back({paulis, angle}); + break; + } + case OpType::XXPhase: { + Expr angle = op->get_params().at(0); + pauli_rots.push_back({{Pauli::X, Pauli::X}, angle}); + break; + } + case OpType::YYPhase: { + Expr angle = op->get_params().at(0); + pauli_rots.push_back({{Pauli::Y, Pauli::Y}, angle}); + break; + } + case OpType::PauliExpBox: { + const PauliExpBox& peb = static_cast(*op); + 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(); + 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()) { + pauli_rots.push_back({pt.string, pt.coeff}); + } + 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("GreedyPauliSimp doesn't support", type); + } + } + apply_paulis_at_end(pauli_rots, qbs, conditional, cond_bits, cond_value); +} + +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> +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, end_measures_}; +} + +} // namespace GreedyPauliSimp + +} // namespace Transforms + +} // namespace tket diff --git a/tket/src/Transformations/GreedyPauliOps.cpp b/tket/src/Transformations/GreedyPauliOps.cpp new file mode 100644 index 0000000000..3e0af85795 --- /dev/null +++ b/tket/src/Transformations/GreedyPauliOps.cpp @@ -0,0 +1,422 @@ +// 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/OpType/OpType.hpp" +#include "tket/Transformations/CliffordOptimisation.hpp" +#include "tket/Transformations/GreedyPauliOptimisation.hpp" +#include "tket/Transformations/GreedyPauliOptimisationLookupTables.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 + +PauliNode::~PauliNode() {} + +void PauliNode::update(const OpType& /*sq_cliff*/, const unsigned& /*a*/) { + throw GreedyPauliSimpError("Single qubit Clifford update not implemented."); +} + +void 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) { + 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; } + +int SingleNode::tqe_cost_increase(const TQE& tqe) const { + 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}); + return (p0 == Pauli::I) + (p1 == Pauli::I) - (new_p0 == Pauli::I) - + (new_p1 == Pauli::I); +} + +void SingleNode::update(const TQE& 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}); + 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); + } + 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 = + 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_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_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_string_.size(); i++) { + CommuteType commute_type = + 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; + } + if (commute_type == CommuteType::A) { + n_anti_commute_entries_ += 1; + } + } +} + +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_); +} + +int ACPairNode::tqe_cost_increase(const TQE& tqe) const { + 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]; + 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); + 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) { + 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]; + 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); + 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_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_; + } + 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_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_; + } + if (!x_sign) { + x_sign_ = !x_sign_; + } +} + +void ACPairNode::swap(const unsigned& a, const unsigned& 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]); +} + +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); + } + 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; + 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_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_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_string_[b], z_string_[a], x_string_[b], x_string_[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_string_[a], z_string_[b], x_string_[a], x_string_[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_string_[i], x_string_[i]}; + } + } + // Should be impossible to reach here + TKET_ASSERT(false); +} + +// PauliRotation +PauliRotation::PauliRotation(std::vector string, bool sign, Expr theta) + : SingleNode(string, sign), theta_(theta) {} + +CommuteInfo PauliRotation::get_commute_info() const { return {{string_}, {}}; } + +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_) { + 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}); + 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_) { + 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}); + 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, + 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_string_, x_string_}, {}}; +} + +// 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}; +} + +// MidMeasure +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 + +} // namespace tket diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp index 9515ddbbc5..e052102656 100644 --- a/tket/src/Transformations/GreedyPauliOptimisation.cpp +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -15,12 +15,10 @@ #include "tket/Transformations/GreedyPauliOptimisation.hpp" #include +#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" @@ -30,8 +28,61 @@ namespace Transforms { namespace GreedyPauliSimp { +static TQE sample_random_tqe(const std::vector& vec, unsigned seed) { + std::mt19937 rng(seed); + std::uniform_int_distribution dist(0, vec.size() - 1); + size_t random_index = dist(rng); + auto it = vec.begin(); + std::advance(it, random_index); + 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_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}); @@ -71,233 +122,16 @@ 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; - } -} - -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; -} - -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); -} - -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); -} - -std::vector PauliExpNode::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 < 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]]}); - for (const TQEType& tt : tqe_types) { - tqes.push_back({tt, sqs[i], sqs[j]}); - } - } - } - 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]}; - } - } - // 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_++; - } - } - 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 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_); -} - -std::vector TableauRowNode::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 < 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}); - } else { - // TQEs that transform a SW pair to a single strong - tqe_types = FACTOR_PAIR_SW_TO_SN_TQES.at({supp0, supp1}); - } - } 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}); - // flip qubits - a = sqs[j]; - b = sqs[i]; - } else { - // TQEs that transform a WW pair to a single weak, not always - // possible - tqe_types = FACTOR_PAIR_WW_TO_WN_OR_NW_TQES.at({supp0, supp1}); - } - } - for (const TQEType& tt : tqe_types) { - tqes.push_back({tt, a, b}); - } - } - } - 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]}; - } - } - // Should be impossible to reach here - TKET_ASSERT(false); -} - // 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& rows, + const std::vector& remaining_indices, const TQE& tqe, + const unsigned& max_lookahead) { double cost = 0; + unsigned count = 0; for (const unsigned& index : remaining_indices) { - cost += rows[index].tqe_cost_increase(tqe); + cost += rows[index]->tqe_cost_increase(tqe); + if (++count >= max_lookahead) break; } return cost; } @@ -306,28 +140,36 @@ static double default_tableau_tqe_cost( // 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, + const unsigned& max_lookahead) { 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); + 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 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); + if (++count >= max_lookahead) break; } 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::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(); @@ -346,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++) { @@ -355,26 +208,50 @@ 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; + 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) { 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); + } + } + 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_tqe; + return {min_tqes, min_rot2qs}; } // 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] * @@ -392,22 +269,29 @@ 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; -} - -static TQE select_pauliexp_tqe( - const std::map>& tqe_candidates_cost, - double depth_weight) { - return minmax_selection(tqe_candidates_cost, {1, depth_weight}); -} - -static TQE select_tableau_tqe( - const std::map>& tqe_candidates_cost, - double depth_weight) { - return minmax_selection(tqe_candidates_cost, {1, depth_weight}); + 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 @@ -436,103 +320,25 @@ 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, UnitaryRevTableau& tab, 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; - } - } - } - // 2. 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; - unsigned next = perm[k]; - 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}); - done.insert(current); - current = next; - next = perm[current]; - } - } -} - -/** - * @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, + 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++) { - 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) { @@ -545,140 +351,271 @@ 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()); } + // 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), - static_cast(depth_tracker.gate_depth( - std::get<1>(tqe), std::get<2>(tqe)))}}); + {default_tableau_tqe_cost( + rows, remaining_indices, tqe, max_lookahead), + 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); + 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); - 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)); + 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]; - 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); + // 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()); + } + } + } } /** - * @brief Given a vector of sets of PauliExpNode, 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. * * @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 bin; - if (rotation_sets.size() == 0) { - return false; +static void consume_nodes( + std::vector>& rotation_sets, Circuit& circ, + DepthTracker& depth_tracker, double discount_rate, double depth_weight) { + if (rotation_sets.empty()) { + return; } - std::vector& first_set = rotation_sets[0]; - for (unsigned i = 0; i < first_set.size(); i++) { - PauliExpNode& node = first_set[i]; - if (node.tqe_cost() > 0) continue; - unsigned q_index, supp; - std::tie(q_index, supp) = node.first_support(); - Qubit q(q_index); - depth_tracker.add_1q_gate(q_index); - 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}); + while (true) { + 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()) { + 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(); + // 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}); + } + 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; } - 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 PauliNodeType::MidMeasure: { + 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()) { + 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; } - 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 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; + } + 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(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); + std::vector args = cond_bits; + for (unsigned i = 0; i < cond_circ.n_qubits(); i++) { + args.push_back(i); + } + circ.add_op(cond, args); + first_set.erase(first_set.begin() + i); + break; + } + case PauliNodeType::PauliRotation: { + if (node_ptr->tqe_cost() > 0) continue; + PauliRotation& node = dynamic_cast(*node_ptr); + 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); + break; } - break; + default: + TKET_ASSERT(false); } - default: - // support can't be Pauli::I - TKET_ASSERT(false); } - 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; + if (first_set.empty()) { + rotation_sets.erase(rotation_sets.begin()); + if (rotation_sets.empty()) { + return; + } + } else { + return; + } } - return false; } /** * @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, + DepthTracker& depth_tracker, double discount_rate, double depth_weight, + unsigned max_lookahead, unsigned max_tqe_candidates, unsigned seed, + bool allow_zzphase) { while (true) { - while (consume_available_rotations( - rotation_sets, tab, circ, depth_tracker)); // do nothing - if (rotation_sets.size() == 0) break; - std::vector& first_set = rotation_sets[0]; + 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 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) { @@ -688,318 +625,167 @@ 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()); } + // 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), - static_cast(depth_tracker.gate_depth( - std::get<1>(tqe), std::get<2>(tqe)))}}); + {default_pauliexp_tqe_cost( + discount_rate, rotation_sets, rows, tqe, max_lookahead), + static_cast(depth_tracker.gate_depth(tqe.a, tqe.b))}}); + } + 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]))}}); + } + } } // select the best one - 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); + 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); } - } - for (TableauRowNode& row : rows) { - row.update(selected_tqe); } } } -// convert a Pauli exponential to a PauliExpNode -static PauliExpNode 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]}); +Circuit greedy_pauli_set_synthesis( + const std::vector& unordered_set, double depth_weight, + 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."); } - // 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); - - 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); - } + if (max_tqe_candidates == 0) { + throw GreedyPauliSimpError("max_tqe_candidates must be greater than 0."); } - return PauliExpNode(support_vec, sign.real() * theta); -} -// 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{{}}; - std::vector rows; - 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)); - } - 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)); - } + 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, tab, c, 0, depth_weight, depth_tracker); + rotation_sets, rows, c, depth_tracker, 0, depth_weight, max_lookahead, + max_tqe_candidates, seed, allow_zzphase); // synthesise the tableau - tableau_row_nodes_synthesis(rows, tab, c, depth_weight, depth_tracker); + 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) { - // 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()); + const Circuit& circ, double discount_rate, double depth_weight, + 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."); + } + if (max_tqe_candidates == 0) { + throw GreedyPauliSimpError("max_tqe_candidates must be greater than 0."); } - 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; - 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(); - unsigned n_qubits = c.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, forward_tab, tab)}); - } - 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, forward_tab, tab)); - } - 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)); - } - 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, forward_tab, tab)); - } - } - if (rotation_set.size() > 0) { - rotation_sets.push_back(rotation_set); - } - break; - } - default: - break; - } + 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()); } - // 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)); + 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] = gpg.get_sequence(); 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, new_circ, depth_tracker, discount_rate, depth_weight, + max_lookahead, max_tqe_candidates, seed, allow_zzphase); // synthesise the tableau - tableau_row_nodes_synthesis(rows, tab, 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(); - return c; + tableau_row_nodes_synthesis( + 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); + } + 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); +Transform greedy_pauli_optimisation( + double discount_rate, double depth_weight, unsigned max_lookahead, + unsigned max_tqe_candidates, unsigned seed, bool allow_zzphase) { + return Transform([discount_rate, depth_weight, max_lookahead, + max_tqe_candidates, seed, allow_zzphase](Circuit& circ) { circ = GreedyPauliSimp::greedy_pauli_graph_synthesis( - circ, discount_rate, depth_weight); - singleq_clifford_sweep().apply(circ); + circ, discount_rate, depth_weight, max_lookahead, max_tqe_candidates, + 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 16250ca8c8..366143ece4 100644 --- a/tket/test/src/test_GreedyPauli.cpp +++ b/tket/test/src/test_GreedyPauli.cpp @@ -20,7 +20,7 @@ #include "tket/Circuit/PauliExpBoxes.hpp" #include "tket/Circuit/Simulation/CircuitSimulator.hpp" #include "tket/Gate/SymTable.hpp" -#include "tket/PauliGraph/PauliGraph.hpp" +#include "tket/Ops/ClassicalOps.hpp" #include "tket/Predicates/PassGenerators.hpp" #include "tket/Transformations/GreedyPauliOptimisation.hpp" #include "tket/Utils/Expression.hpp" @@ -28,36 +28,20 @@ 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") { +SCENARIO("Exception handling") { + GIVEN("Invalid arguments") { 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 PauliGraph")); - } - GIVEN("Circuit with conditional gates") { - Circuit circ(2, 2); - circ.add_conditional_gate(OpType::Rz, {0.5}, {0}, {0}, 0); + 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().apply(circ), BadOpType, - MessageContains( - "Can only make a PauliGraph from a circuit of basic gates")); + 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); @@ -130,6 +114,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") { @@ -213,6 +217,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}); @@ -276,6 +329,385 @@ 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_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); + } + 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}); + 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") { + 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::Z, Pauli::Y}, 0.12)), + 1, 0); + circ.add_op(cond1, {0, 0, 1}); + circ.add_op(OpType::Measure, {0, 0}); + // can commute to the front + circ.add_op(cond2, {1, 0, 1}); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + REQUIRE(circ.count_n_qubit_gates(2) == 3); + } + + 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::H, {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); + } + 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); + } + 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}); + circ.add_op(OpType::Reset, {0}); + Circuit d(circ); + 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( + 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}); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(circ)); + 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); + 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); + 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