From 25d0a600a2f3a5aadd0f3429ad6661a5015c5b00 Mon Sep 17 00:00:00 2001 From: Callum Macpherson <93673602+CalMacCQ@users.noreply.github.com> Date: Wed, 17 Apr 2024 09:49:39 +0100 Subject: [PATCH 01/33] add yet more missing circuit methods (#1352) --- pytket/docs/circuit_class.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pytket/docs/circuit_class.rst b/pytket/docs/circuit_class.rst index 30f2ddc84d..1340b34c16 100644 --- a/pytket/docs/circuit_class.rst +++ b/pytket/docs/circuit_class.rst @@ -327,6 +327,12 @@ condition on a specified set of bit values.) .. automethod:: ZZMax + .. automethod:: AAMS + + .. automethod:: GPI + + .. automethod:: GPI2 + Methods for appending circuit boxes ----------------------------------- @@ -347,6 +353,10 @@ condition on a specified set of bit values.) .. automethod:: add_circbox + .. automethod:: add_circbox_regwise + + .. automethod:: add_circbox_with_regmap + .. automethod:: add_unitary1qbox .. automethod:: add_unitary2qbox @@ -361,6 +371,8 @@ condition on a specified set of bit values.) .. automethod:: add_pauliexpcommutingsetbox + .. automethod:: add_termsequencebox + .. automethod:: add_phasepolybox .. automethod:: add_toffolibox From 49f684c30a18f483af0e82905e1ded9c4ddf91e9 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:08:26 +0100 Subject: [PATCH 02/33] Update to pytket-circuit-renderer 0.8 (#1354) --- pytket/docs/changelog.rst | 7 +++++++ pytket/pytket/circuit/display/static/head_imports.html | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 29ea0efdf9..97a0e4e449 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -1,6 +1,13 @@ Changelog ========= +Unreleased +---------- + +Features: + +* Update to pytket-circuit-renderer 0.8. + 1.27.0 (April 2024) ------------------- diff --git a/pytket/pytket/circuit/display/static/head_imports.html b/pytket/pytket/circuit/display/static/head_imports.html index d560b50085..b72cacdb86 100644 --- a/pytket/pytket/circuit/display/static/head_imports.html +++ b/pytket/pytket/circuit/display/static/head_imports.html @@ -1,5 +1,5 @@ - - + + From 02887463250ce5f2acec49a2773b035ba2f700b6 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Fri, 19 Apr 2024 10:52:09 +0100 Subject: [PATCH 03/33] Ensure MacOS arm64 wheels are compatible with MacOS 12 and above (#1359) --- .github/workflows/release.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 269c9b0d28..6cca5016ab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -107,6 +107,8 @@ jobs: build_macos_arm64_wheels: name: Build macos arm64 wheels runs-on: macos-14 + env: + MACOSX_DEPLOYMENT_TARGET: '12.0' strategy: matrix: python-version: ['3.10', '3.11', '3.12'] @@ -130,7 +132,7 @@ jobs: cp ${PROFILE_PATH} ${DEFAULT_PROFILE_PATH} conan remote add tket-libs https://quantinuumsw.jfrog.io/artifactory/api/conan/tket1-libs --index 0 - name: Build tket C++ - run: conan create tket --user tket --channel stable --build=missing -o boost/*:header_only=True -o tklog/*:shared=True -o tket/*:shared=True -tf "" + run: conan create tket --user tket --channel stable --build="*" -o boost/*:header_only=True -o tklog/*:shared=True -o tket/*:shared=True -tf "" - name: Build wheel run: | conan create recipes/pybind11 @@ -138,8 +140,7 @@ jobs: cd pytket # Ensure wheels are compatible with MacOS 12.0 and later: export WHEEL_PLAT_NAME=macosx_12_0_arm64 - python${{ matrix.python-version }} -m pip install -U pip build - python${{ matrix.python-version }} -m pip install delocate~=0.10.7 + python${{ matrix.python-version }} -m pip install -U pip build delocate python${{ matrix.python-version }} -m build delocate-wheel -v -w "$GITHUB_WORKSPACE/wheelhouse/" "dist/pytket-"*".whl" - uses: actions/upload-artifact@v4 From 297a0fb2abf93c1a8353febba93bfb1b957e4c20 Mon Sep 17 00:00:00 2001 From: Richard Morrison <141632498+quantinuum-richard-morrison@users.noreply.github.com> Date: Thu, 25 Apr 2024 14:57:55 +0100 Subject: [PATCH 04/33] Add "CANCELLING" and "RETRYING" statuses (#1364) --- pytket/docs/changelog.rst | 1 + pytket/pytket/backends/status.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 97a0e4e449..a771bc3d77 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -7,6 +7,7 @@ Unreleased Features: * Update to pytket-circuit-renderer 0.8. +* Add two new status values for circuits on backends: "CANCELLING" and "RETRYING". 1.27.0 (April 2024) ------------------- diff --git a/pytket/pytket/backends/status.py b/pytket/pytket/backends/status.py index 880bb95145..022f6387d6 100644 --- a/pytket/pytket/backends/status.py +++ b/pytket/pytket/backends/status.py @@ -26,6 +26,8 @@ class StatusEnum(Enum): QUEUED = "Circuit is queued." SUBMITTED = "Circuit has been submitted." RUNNING = "Circuit is running." + RETRYING = "Circuit is being retried." + CANCELLING = "Cancellation has been requested." CANCELLED = "Circuit has been cancelled." ERROR = "Circuit has errored. Check CircuitStatus.message for error message." From 16514cc9a21da0b9887e84d0ecf98c550b704522 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Mon, 29 Apr 2024 09:08:52 +0100 Subject: [PATCH 05/33] Run do-clang-format with clang-format 18.1.4 (#1367) * run do-clang-format with clang-format 18.1.4 * bump --- pytket/binders/include/UnitRegister.hpp | 2 +- pytket/conanfile.py | 2 +- tket/conanfile.py | 2 +- tket/include/tket/Characterisation/Cycles.hpp | 2 +- tket/include/tket/Circuit/ClassicalExpBox.hpp | 2 +- tket/include/tket/Mapping/AASLabelling.hpp | 2 +- tket/include/tket/Mapping/LexiLabelling.hpp | 2 +- tket/include/tket/Mapping/RoutingMethod.hpp | 2 +- tket/include/tket/Placement/Placement.hpp | 4 ++-- tket/include/tket/Predicates/CompilerPass.hpp | 2 +- tket/include/tket/Predicates/Predicates.hpp | 2 +- tket/src/Mapping/BoxDecomposition.cpp | 2 +- tket/src/Mapping/LexiRouteRoutingMethod.cpp | 2 +- tket/src/Mapping/RoutingMethodCircuit.cpp | 2 +- tket/src/Placement/Placement.cpp | 2 +- tket/test/src/test_MappingManager.cpp | 2 +- 16 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pytket/binders/include/UnitRegister.hpp b/pytket/binders/include/UnitRegister.hpp index 32c7fed8b5..0db7009179 100644 --- a/pytket/binders/include/UnitRegister.hpp +++ b/pytket/binders/include/UnitRegister.hpp @@ -30,7 +30,7 @@ template class UnitRegister { public: UnitRegister(const std::string &name, const std::size_t size) - : name_(name), size_(size){}; + : name_(name), size_(size) {}; std::string name() const { return name_; } std::size_t size() const { return size_; } diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 0f2b30f322..84f221acbc 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.2.117@tket/stable") + self.requires("tket/1.2.118@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") diff --git a/tket/conanfile.py b/tket/conanfile.py index cb0f27dcba..32c996febb 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.117" + version = "1.2.118" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" diff --git a/tket/include/tket/Characterisation/Cycles.hpp b/tket/include/tket/Characterisation/Cycles.hpp index e62533baec..4ceb70d544 100644 --- a/tket/include/tket/Characterisation/Cycles.hpp +++ b/tket/include/tket/Characterisation/Cycles.hpp @@ -90,7 +90,7 @@ struct CycleHistory { class CycleFinder { public: CycleFinder(const Circuit& _circ, const OpTypeSet& _cycle_types) - : circ(_circ), cycle_types_(_cycle_types){}; + : circ(_circ), cycle_types_(_cycle_types) {}; // Cycles are sub-circuits of circ where every gate has OpType in cycle_types_ // get_cycles() returns the minimum number of cycles such that every diff --git a/tket/include/tket/Circuit/ClassicalExpBox.hpp b/tket/include/tket/Circuit/ClassicalExpBox.hpp index f4dcc7c839..d3cbc96309 100644 --- a/tket/include/tket/Circuit/ClassicalExpBox.hpp +++ b/tket/include/tket/Circuit/ClassicalExpBox.hpp @@ -81,7 +81,7 @@ class ClassicalExpBox : public ClassicalExpBoxBase { n_io_(other.n_io_), n_o_(other.n_o_), exp_(other.exp_), - sig_(other.sig_){}; + sig_(other.sig_) {}; ~ClassicalExpBox() override {} Op_ptr symbol_substitution( diff --git a/tket/include/tket/Mapping/AASLabelling.hpp b/tket/include/tket/Mapping/AASLabelling.hpp index 23179ae9e4..27b88d6f65 100644 --- a/tket/include/tket/Mapping/AASLabelling.hpp +++ b/tket/include/tket/Mapping/AASLabelling.hpp @@ -25,7 +25,7 @@ class AASLabellingMethod : public RoutingMethod { * Checking and Routing methods redefined for dynamically assigning qubits to * some Architecture. */ - AASLabellingMethod(){}; + AASLabellingMethod() {}; /** * will place all the qubits of the given circuit that are not placed at the diff --git a/tket/include/tket/Mapping/LexiLabelling.hpp b/tket/include/tket/Mapping/LexiLabelling.hpp index bb57248e71..ef49e08f40 100644 --- a/tket/include/tket/Mapping/LexiLabelling.hpp +++ b/tket/include/tket/Mapping/LexiLabelling.hpp @@ -25,7 +25,7 @@ class LexiLabellingMethod : public RoutingMethod { * Checking and Routing methods redefined for dynamically assigning qubits to * some Architecture. */ - LexiLabellingMethod(){}; + LexiLabellingMethod() {}; /** * @param mapping_frontier Contains boundary of routed/unrouted circuit for diff --git a/tket/include/tket/Mapping/RoutingMethod.hpp b/tket/include/tket/Mapping/RoutingMethod.hpp index d9181eb774..365f81c05d 100644 --- a/tket/include/tket/Mapping/RoutingMethod.hpp +++ b/tket/include/tket/Mapping/RoutingMethod.hpp @@ -21,7 +21,7 @@ namespace tket { class RoutingMethod { public: - RoutingMethod(){}; + RoutingMethod() {}; virtual ~RoutingMethod() {} /** diff --git a/tket/include/tket/Placement/Placement.hpp b/tket/include/tket/Placement/Placement.hpp index bc6d9f287a..fa918c57cf 100644 --- a/tket/include/tket/Placement/Placement.hpp +++ b/tket/include/tket/Placement/Placement.hpp @@ -27,7 +27,7 @@ class Placement { explicit Placement(const Architecture& _architecture); - Placement(){}; + Placement() {}; /** * Reassigns some UnitID in circ_ as UnitID in architecture_ @@ -90,7 +90,7 @@ class Placement { */ const Architecture& get_architecture_ref() { return architecture_; } - virtual ~Placement(){}; + virtual ~Placement() {}; static const std::string& unplaced_reg(); diff --git a/tket/include/tket/Predicates/CompilerPass.hpp b/tket/include/tket/Predicates/CompilerPass.hpp index 26ac7def6c..4b266b7d92 100644 --- a/tket/include/tket/Predicates/CompilerPass.hpp +++ b/tket/include/tket/Predicates/CompilerPass.hpp @@ -132,7 +132,7 @@ class BasePass { static Guarantee get_guarantee( const std::type_index& ti, const PassConditions& conditions); - virtual ~BasePass(){}; + virtual ~BasePass() {}; protected: BasePass(const PredicatePtrMap& precons, const PostConditions& postcons) diff --git a/tket/include/tket/Predicates/Predicates.hpp b/tket/include/tket/Predicates/Predicates.hpp index e03e1d0db8..cad531ed29 100644 --- a/tket/include/tket/Predicates/Predicates.hpp +++ b/tket/include/tket/Predicates/Predicates.hpp @@ -60,7 +60,7 @@ class Predicate { virtual bool implies(const Predicate& other) const = 0; virtual PredicatePtr meet(const Predicate& other) const = 0; virtual std::string to_string() const = 0; - virtual ~Predicate(){}; // satisfy compiler + virtual ~Predicate() {}; // satisfy compiler }; // all Predicate subclasses must inherit from `Predicate` diff --git a/tket/src/Mapping/BoxDecomposition.cpp b/tket/src/Mapping/BoxDecomposition.cpp index 1aae01c200..dc7f812789 100644 --- a/tket/src/Mapping/BoxDecomposition.cpp +++ b/tket/src/Mapping/BoxDecomposition.cpp @@ -53,7 +53,7 @@ bool BoxDecomposition::solve() { return true; } -BoxDecompositionRoutingMethod::BoxDecompositionRoutingMethod(){}; +BoxDecompositionRoutingMethod::BoxDecompositionRoutingMethod() {}; std::pair BoxDecompositionRoutingMethod::routing_method( MappingFrontier_ptr &mapping_frontier, diff --git a/tket/src/Mapping/LexiRouteRoutingMethod.cpp b/tket/src/Mapping/LexiRouteRoutingMethod.cpp index 16c3cdbab3..07ecafa8f6 100644 --- a/tket/src/Mapping/LexiRouteRoutingMethod.cpp +++ b/tket/src/Mapping/LexiRouteRoutingMethod.cpp @@ -17,7 +17,7 @@ namespace tket { LexiRouteRoutingMethod::LexiRouteRoutingMethod(unsigned _max_depth) - : max_depth_(_max_depth){}; + : max_depth_(_max_depth) {}; std::pair LexiRouteRoutingMethod::routing_method( MappingFrontier_ptr& mapping_frontier, diff --git a/tket/src/Mapping/RoutingMethodCircuit.cpp b/tket/src/Mapping/RoutingMethodCircuit.cpp index e5da4e4c7b..f2610eef79 100644 --- a/tket/src/Mapping/RoutingMethodCircuit.cpp +++ b/tket/src/Mapping/RoutingMethodCircuit.cpp @@ -23,7 +23,7 @@ RoutingMethodCircuit::RoutingMethodCircuit( unsigned _max_size, unsigned _max_depth) : route_subcircuit_(_route_subcircuit), max_size_(_max_size), - max_depth_(_max_depth){}; + max_depth_(_max_depth) {}; std::pair RoutingMethodCircuit::routing_method( MappingFrontier_ptr& mapping_frontier, diff --git a/tket/src/Placement/Placement.cpp b/tket/src/Placement/Placement.cpp index 53a7f4a299..41e1b1cfad 100644 --- a/tket/src/Placement/Placement.cpp +++ b/tket/src/Placement/Placement.cpp @@ -48,7 +48,7 @@ void fill_partial_mapping( } Placement::Placement(const Architecture& _architecture) - : architecture_(_architecture){}; + : architecture_(_architecture) {}; bool Placement::place( Circuit& circ_, std::shared_ptr compilation_map) const { diff --git a/tket/test/src/test_MappingManager.cpp b/tket/test/src/test_MappingManager.cpp index f0b30a523c..1e0ab6544d 100644 --- a/tket/test/src/test_MappingManager.cpp +++ b/tket/test/src/test_MappingManager.cpp @@ -23,7 +23,7 @@ namespace tket { class TokenSwappingTester : public RoutingMethod { public: - TokenSwappingTester(){}; + TokenSwappingTester() {}; /** * @param mapping_frontier Contains boundary of routed/unrouted circuit for From cf12bd02f2a3dd192651100addc6896445ec78ca Mon Sep 17 00:00:00 2001 From: Dan Mills <52407433+daniel-mills-cqc@users.noreply.github.com> Date: Mon, 29 Apr 2024 11:06:35 +0100 Subject: [PATCH 06/33] ci: Add automated benchmarking workflow (#1365) * Add automated benchmarking workflow * Rename jobs * Remove tket checkout * Remove mac os 12 compatability * Revert "Remove mac os 12 compatability" This reverts commit ecca33b1ec68657f3fd8f948debe3b3575f7c8c7. --- .github/workflows/pytket_benchmarking.yml | 107 ++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 .github/workflows/pytket_benchmarking.yml diff --git a/.github/workflows/pytket_benchmarking.yml b/.github/workflows/pytket_benchmarking.yml new file mode 100644 index 0000000000..e8dc45974f --- /dev/null +++ b/.github/workflows/pytket_benchmarking.yml @@ -0,0 +1,107 @@ +name: Automated Benchmarks + +on: + pull_request: + branches: + - develop + workflow_dispatch: + +jobs: + + build_wheels: + name: Build macos wheels + runs-on: macos-14 + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: '0' + + - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: 3.11 + + - name: Install conan + uses: turtlebrowser/get-conan@v1.2 + + - name: Set up conan + run: | + conan profile detect + DEFAULT_PROFILE_PATH=`conan profile path default` + PROFILE_PATH=./conan-profiles/macos-14 + diff ${DEFAULT_PROFILE_PATH} ${PROFILE_PATH} || true + cp ${PROFILE_PATH} ${DEFAULT_PROFILE_PATH} + conan remote add tket-libs https://quantinuumsw.jfrog.io/artifactory/api/conan/tket1-libs --index 0 + + - name: Build tket C++ + run: conan create tket --user tket --channel stable --build=missing -o boost/*:header_only=True -o tklog/*:shared=True -o tket/*:shared=True -tf "" + + - name: Build wheel + run: | + conan create recipes/pybind11 + conan create recipes/pybind11_json/all --version 0.2.13 + cd pytket + # Ensure wheels are compatible with MacOS 12.0 and later: + export WHEEL_PLAT_NAME=macosx_12_0_arm64 + python3.11 -m pip install -U pip build delocate + python3.11 -m build + delocate-wheel -v -w "$GITHUB_WORKSPACE/wheelhouse/" "dist/pytket-"*".whl" + + - name: Save Wheel + uses: actions/upload-artifact@v4 + with: + name: pytket_wheel + path: wheelhouse/ + + compile-and-compare: + name: Compile and compare + runs-on: macos-14 + needs: build_wheels + + steps: + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Download wheel + uses: actions/download-artifact@v4 + with: + name: pytket_wheel + + - name: Install dependencies + run: | + pip install pytket-*.whl + pip install --pre --index-url https://github_actions:${{ secrets.PRIVATE_PYPI_PASS }}@cqcpythonrepository.azurewebsites.net/simple/ pytket_benchmarking + + - name: Checkout pytket-benchmarking-store + uses: actions/checkout@v4 + with: + repository: CQCL/pytket-benchmarking-store + path: pytket-benchmarking-store + + - name: Perform Compilation + run: | + pytket_benchmarking compile QiskitIBMQ pytket-benchmarking-store/benchmarking_circuits/quantum_volume automated_benchmarks_compiled + pytket_benchmarking compile PytketIBMQ pytket-benchmarking-store/benchmarking_circuits/quantum_volume automated_benchmarks_compiled + + - name: Save compiled circuits + uses: actions/upload-artifact@v4 + with: + name: automated_benchmarks_compiled + path: automated_benchmarks_compiled/ + + - name: Calculate percentage better + run: echo "RETURN_TEST=$(pytket_benchmarking percentage-better pytket-benchmarking-store/benchmarking_circuits/quantum_volume automated_benchmarks_compiled PytketIBMQ)" >> $GITHUB_ENV + + - name: Create comment + uses: peter-evans/create-or-update-comment@v4 + with: + issue-number: ${{ github.event.pull_request.number }} + body: '${{ env.RETURN_TEST }}' + env: + RETURN_TEST: ${{ env.RETURN_TEST }} From 26050e60f20f4701d6600af9951566aa810f1739 Mon Sep 17 00:00:00 2001 From: Jake Arkinstall <65358059+jake-arkinstall@users.noreply.github.com> Date: Thu, 2 May 2024 15:46:46 +0100 Subject: [PATCH 07/33] Set Nix workflow to run on PRs and commits (not just weekly) (#1370) --- .github/workflows/build-with-nix.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/build-with-nix.yml b/.github/workflows/build-with-nix.yml index 08ac988e19..0280785bde 100644 --- a/.github/workflows/build-with-nix.yml +++ b/.github/workflows/build-with-nix.yml @@ -1,9 +1,15 @@ name: build with nix + on: + pull_request: + branches: + - main + - develop schedule: # 01:00 every Sunday morning - cron: '0 1 * * 0' workflow_dispatch: {} + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true From 9b16ccfabe0a5ee6649413da9d08ac61abe0705d Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Fri, 3 May 2024 10:41:56 +0100 Subject: [PATCH 08/33] Switch workflows to target `main` only (#1371) --- .github/workflows/build-with-nix.yml | 1 - .github/workflows/build_and_test.yml | 21 ++++++++++----------- .github/workflows/build_libs.yml | 4 ++-- .github/workflows/coverage.yml | 8 ++++---- .github/workflows/docs.yml | 2 +- .github/workflows/pytket_benchmarking.yml | 2 +- .github/workflows/test_libs.yml | 8 ++++---- .github/workflows/valgrind.yml | 2 +- CONTRIBUTING.md | 16 ++++++++-------- 9 files changed, 31 insertions(+), 33 deletions(-) diff --git a/.github/workflows/build-with-nix.yml b/.github/workflows/build-with-nix.yml index 0280785bde..7a83d0b784 100644 --- a/.github/workflows/build-with-nix.yml +++ b/.github/workflows/build-with-nix.yml @@ -4,7 +4,6 @@ on: pull_request: branches: - main - - develop schedule: # 01:00 every Sunday morning - cron: '0 1 * * 0' diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 9df57f11f7..9651a62c7b 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -4,10 +4,9 @@ on: pull_request: branches: - main - - develop push: branches: - - develop + - main schedule: # 03:00 every Saturday morning - cron: '0 3 * * 6' @@ -63,7 +62,7 @@ jobs: tket_package_exists=`conan search -r tket-libs "tket/${{ steps.tket_ver.outputs.tket_ver }}@tket/stable" | grep "not found" > /dev/null 2>&1 && echo false || echo true` echo "tket_package_exists=${tket_package_exists}" >> $GITHUB_OUTPUT - name: Check tket version bump - if: github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'develop' && steps.filter.outputs.tket == 'true' && steps.test_package_exists.outputs.tket_package_exists == 'true' + if: github.event_name == 'pull_request' && steps.filter.outputs.tket == 'true' && steps.test_package_exists.outputs.tket_package_exists == 'true' run: exit 1 check_docs_tket: @@ -138,7 +137,7 @@ jobs: - name: Build tket run: conan create tket -s build_type=Release --user=tket --channel=stable -o boost/*:header_only=True -o with_all_tests=True - name: Upload package - if: github.event_name == 'push' && github.ref == 'refs/heads/develop' + if: github.event_name == 'push' run: | ccache --set-config namespace=WITHOUT_TESTS conan create tket --user=tket --channel=stable -o boost/*:header_only=True @@ -207,7 +206,7 @@ jobs: conan build tket --user=tket --channel=stable -o boost/*:header_only=True -o with_all_tests=True -c tools.cmake.cmaketoolchain:generator=Ninja conan export-pkg tket --user=tket --channel=stable -o boost/*:header_only=True -o with_all_tests=True -c tools.cmake.cmaketoolchain:generator=Ninja - name: Upload package - if: github.event_name == 'push' && github.ref == 'refs/heads/develop' + if: github.event_name == 'push' run: | ccache --set-config namespace=WITHOUT_TESTS conan build tket --user=tket --channel=stable -o boost/*:header_only=True @@ -319,7 +318,7 @@ jobs: name: pytket_test_coverage path: pytket/tests/htmlcov - name: Upload package - if: github.event_name == 'push' && github.ref == 'refs/heads/develop' && needs.check_changes.outputs.tket_changed == 'true' + if: github.event_name == 'push && needs.check_changes.outputs.tket_changed == 'true' run: | conan remote login -p ${{ secrets.JFROG_ARTIFACTORY_TOKEN_3 }} tket-libs ${{ secrets.JFROG_ARTIFACTORY_USER_3 }} conan upload tket/${{ needs.check_changes.outputs.tket_ver }}@tket/stable -r=tket-libs @@ -409,7 +408,7 @@ jobs: git diff --quiet pytket/_tket && echo "Stubs are up-to-date" || exit 1 # fail if stubs change after regeneration python -m mypy --config-file=mypy.ini --no-incremental -p pytket -p tests - name: Upload package - if: github.event_name == 'push' && github.ref == 'refs/heads/develop' && needs.check_changes.outputs.tket_changed == 'true' + if: github.event_name == 'push' && needs.check_changes.outputs.tket_changed == 'true' run: | conan remote login -p ${{ secrets.JFROG_ARTIFACTORY_TOKEN_3 }} tket-libs ${{ secrets.JFROG_ARTIFACTORY_USER_3 }} conan upload tket/${{ needs.check_changes.outputs.tket_ver }}@tket/stable -r=tket-libs @@ -521,7 +520,7 @@ jobs: pip install -r requirements.txt pytest --ignore=simulator/ - name: Upload package - if: github.event_name == 'push' && github.ref == 'refs/heads/develop' && needs.check_changes.outputs.tket_changed == 'true' + if: github.event_name == 'push' && needs.check_changes.outputs.tket_changed == 'true' run: | conan remote login -p ${{ secrets.JFROG_ARTIFACTORY_TOKEN_3 }} tket-libs ${{ secrets.JFROG_ARTIFACTORY_USER_3 }} conan upload tket/${{ needs.check_changes.outputs.tket_ver }}@tket/stable -r=tket-libs @@ -534,7 +533,7 @@ jobs: name: Publish pytket coverage needs: build_test_pytket_ubuntu concurrency: gh_pages - if: github.event_name == 'push' && github.ref == 'refs/heads/develop' + if: github.event_name == 'push' runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -562,7 +561,7 @@ jobs: check_pytket_coverage: name: Check pytket line and branch coverage needs: build_test_pytket_ubuntu - if: (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'develop') || github.event_name == 'workflow_dispatch' + if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -571,7 +570,7 @@ jobs: with: name: pytket_test_coverage path: pytket-test-coverage/ - - name: Compare with latest report from develop + - name: Compare with latest report from main run: | wget https://cqcl.github.io/tket/pytket/test-coverage/cov.xml -O oldcov.xml ./.github/workflows/compare-pytket-coverage oldcov.xml pytket-test-coverage/cov.xml diff --git a/.github/workflows/build_libs.yml b/.github/workflows/build_libs.yml index 735058d515..eb0ac8cb91 100644 --- a/.github/workflows/build_libs.yml +++ b/.github/workflows/build_libs.yml @@ -2,10 +2,10 @@ name: build libraries on: push: branches: - - develop + - main pull_request: branches: - - develop + - main workflow_dispatch: {} jobs: diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 59dea72bb4..be2ebec17a 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -3,10 +3,10 @@ name: Analyse tket C++ test coverage on: pull_request: branches: - - develop + - main push: branches: - - develop + - main schedule: # 03:00 every Saturday morning - cron: '0 3 * * 6' @@ -99,12 +99,12 @@ jobs: with: name: test_coverage path: test-coverage/ - - name: Compare with latest report from develop (short tests) + - name: Compare with latest report from main (short tests) if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' run: | wget https://cqcl.github.io/tket/tket/test-coverage-short/summary.txt ./.github/workflows/compare-coverage summary.txt test-coverage/summary.txt - - name: Compare with latest report from develop (full suite) + - name: Compare with latest report from main (full suite) if: github.event_name == 'schedule' run: | wget https://cqcl.github.io/tket/tket/test-coverage/summary.txt diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d661c5743d..6edc995828 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -3,7 +3,7 @@ name: Deploy tket C++ documentation on: push: branches: - - develop + - main paths: - 'tket/src/**' diff --git a/.github/workflows/pytket_benchmarking.yml b/.github/workflows/pytket_benchmarking.yml index e8dc45974f..171e4188d4 100644 --- a/.github/workflows/pytket_benchmarking.yml +++ b/.github/workflows/pytket_benchmarking.yml @@ -3,7 +3,7 @@ name: Automated Benchmarks on: pull_request: branches: - - develop + - main workflow_dispatch: jobs: diff --git a/.github/workflows/test_libs.yml b/.github/workflows/test_libs.yml index abe72ad419..2091bafaf8 100644 --- a/.github/workflows/test_libs.yml +++ b/.github/workflows/test_libs.yml @@ -2,10 +2,10 @@ name: test libraries on: push: branches: - - develop + - main pull_request: branches: - - develop + - main workflow_dispatch: {} env: @@ -135,7 +135,7 @@ jobs: with: name: ${{ matrix.lib }}_coverage path: ${{ matrix.lib }}-coverage/ - - name: check coverage against latest published data from develop + - name: check coverage against latest published data from main if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' run: | # File may not exist if this is the very first time, so don't error. @@ -146,7 +146,7 @@ jobs: publish_coverage: name: Publish coverage needs: [set_libs_matrix, generate_coverage] - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' && needs.set_libs_matrix.outputs.libs != '[]' && needs.set_libs_matrix.outputs.libs != '' }} + if: ${{ github.event_name == 'push' && needs.set_libs_matrix.outputs.libs != '[]' && needs.set_libs_matrix.outputs.libs != '' }} strategy: matrix: lib: ${{ fromJson(needs.set_libs_matrix.outputs.libs) }} diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml index 8b2159e081..eaa78cca44 100644 --- a/.github/workflows/valgrind.yml +++ b/.github/workflows/valgrind.yml @@ -2,7 +2,7 @@ name: valgrind check on: pull_request: branches: - - develop + - main workflow_dispatch: {} schedule: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b6bb21e212..c420c90c0f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,7 +1,7 @@ # Contributing to TKET Pull requests are welcome. To make a PR, first fork the repo, make your proposed -changes on the `develop` branch, and open a PR from your fork. If it passes +changes on the `main` branch, and open a PR from your fork. If it passes tests and is accepted after review, it will be merged in. When adding a new feature, please add tests for it. When fixing a bug, please @@ -9,11 +9,11 @@ add a test that demonstrates the fix. If you make a change to one of the libraries in the `libs` directory, please increase the version number and make a PR with that change only: the component -will then be tested on the CI, and on merge to `develop` the new version will be +will then be tested on the CI, and on merge to `main` the new version will be uploaded. Then it will be possible to update conan requirements to use the new version. -A new version of TKET is uploaded to our conan repo with each push to `develop` +A new version of TKET is uploaded to our conan repo with each push to `main` that changes the core library. This process is managed by CI workflows. If you are making changes only to TKET tests or pytket, you do not need to build TKET locally: the right version should be downloaded automatically from the conan @@ -80,7 +80,7 @@ If you make any changes in `tket/src`, you should bump the version number of `tket` in `recipes/tket/conanfile.py`, and also the `tket` versions in the `requires` field in `recipes/tket-test/conanfile.py`, `recipes/tket-proptests/conanfile.py` and `pytket/conanfile.txt` so that they -match the new version. (This is checked on the CI for all PRs to `develop`.) +match the new version. (This is checked on the CI for all PRs to `main`.) Follow the "semantic versioning" convention: any backwards-incompatible changes to the C++ API require a major version bump; new API features that maintain backwards compatibility require a minor version bump; internal improvements and @@ -92,21 +92,21 @@ bugfixes require a patch version bump. The code coverage of the `tket` tests is reported [here](https://cqcl.github.io/tket/tket/test-coverage/index.html). This report -is generated weekly from the `develop` branch. +is generated weekly from the `main` branch. The libraries' coverage (from their own unit tests) is also reported: for example [tklog](https://cqcl.github.io/tket/tket/tklog-coverage/index.html). (For other libraries, just replace "tklog" with the library name in the URL.) -In both cases, PRs to `develop` check that the coverage has not decreased, and +In both cases, PRs to `main` check that the coverage has not decreased, and merging is blocked until the coverage is at least as good as before. ### pytket The code coverage of the `pytket` tests is reported [here](https://cqcl.github.io/tket/pytket/test-coverage/index.html). This report -reflects the coverage of the `develop` branch, and is updated with every push. +reflects the coverage of the `main` branch, and is updated with every push. The same report can be found in XML format [here](https://cqcl.github.io/tket/pytket/test-coverage/cov.xml). -Lines and branch coverage results are also checked with every PR to `develop`. +Lines and branch coverage results are also checked with every PR to `main`. From e599bf3a9548768cb6a8447bfd08be58cad92fc3 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Fri, 3 May 2024 11:26:28 +0100 Subject: [PATCH 09/33] Fix typo. --- .github/workflows/build_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 9651a62c7b..a8a738ef5b 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -318,7 +318,7 @@ jobs: name: pytket_test_coverage path: pytket/tests/htmlcov - name: Upload package - if: github.event_name == 'push && needs.check_changes.outputs.tket_changed == 'true' + if: github.event_name == 'push' && needs.check_changes.outputs.tket_changed == 'true' run: | conan remote login -p ${{ secrets.JFROG_ARTIFACTORY_TOKEN_3 }} tket-libs ${{ secrets.JFROG_ARTIFACTORY_USER_3 }} conan upload tket/${{ needs.check_changes.outputs.tket_ver }}@tket/stable -r=tket-libs From 1a273e5514daf4d7b4ed8cdbe56ed3b7579f0e14 Mon Sep 17 00:00:00 2001 From: Alec Edgington Date: Fri, 3 May 2024 11:28:03 +0100 Subject: [PATCH 10/33] Bump tket version. --- pytket/conanfile.py | 2 +- tket/conanfile.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 84f221acbc..53331101db 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.2.118@tket/stable") + self.requires("tket/1.2.119@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") diff --git a/tket/conanfile.py b/tket/conanfile.py index 32c996febb..aa82ae4190 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.118" + version = "1.2.119" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" From f55ef839924e6d7c6fc680329f5181311f247f4c Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Fri, 3 May 2024 14:16:52 +0100 Subject: [PATCH 11/33] Show diff from clang-format. (#1373) --- .github/workflows/build_and_test.yml | 3 ++- libs/tklog/src/TketLog.cpp | 4 ++-- pytket/conanfile.py | 2 +- tket/conanfile.py | 2 +- tket/src/Architecture/DistancesFromArchitecture.cpp | 4 ++-- tket/src/Clifford/UnitaryTableau.cpp | 8 ++++---- tket/src/Predicates/Predicates.cpp | 3 +-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index a8a738ef5b..c4ff3ef09f 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -88,7 +88,8 @@ jobs: run: | brew update brew install clang-format@18 - git ls-files "*.cpp" "*.hpp" | xargs clang-format -style=file --dry-run --Werror + git ls-files "*.cpp" "*.hpp" | xargs clang-format -style=file -i + git diff --exit-code build_test_tket: name: Build and test (tket) diff --git a/libs/tklog/src/TketLog.cpp b/libs/tklog/src/TketLog.cpp index 4d8cbe2444..7dc53059b0 100644 --- a/libs/tklog/src/TketLog.cpp +++ b/libs/tklog/src/TketLog.cpp @@ -63,8 +63,8 @@ void Logger::log(const char *levstr, const std::string &s, std::ostream &os) { #else plt = std::localtime(&t); #endif - os << "[" << std::put_time(plt, "%Y-%m-%d %H:%M:%S") << "]" << " [tket] [" - << levstr << "] " << s << std::endl; + os << "[" << std::put_time(plt, "%Y-%m-%d %H:%M:%S") << "]" + << " [tket] [" << levstr << "] " << s << std::endl; } void Logger::set_level(LogLevel lev) { level = lev; } diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 53331101db..8eff94e165 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.2.119@tket/stable") + self.requires("tket/1.2.120@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") diff --git a/tket/conanfile.py b/tket/conanfile.py index aa82ae4190..1e948b370d 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.119" + version = "1.2.120" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" diff --git a/tket/src/Architecture/DistancesFromArchitecture.cpp b/tket/src/Architecture/DistancesFromArchitecture.cpp index 412c777356..5c17a7917c 100644 --- a/tket/src/Architecture/DistancesFromArchitecture.cpp +++ b/tket/src/Architecture/DistancesFromArchitecture.cpp @@ -80,8 +80,8 @@ std::size_t DistancesFromArchitecture::operator()( distance_entry > 0 || AssertMessage() << "DistancesFromArchitecture: architecture has " << arch.n_nodes() << " vertices, " - << arch.n_connections() << " edges; " << " and d(" - << vertex1 << "," << vertex2 + << arch.n_connections() << " edges; " + << " and d(" << vertex1 << "," << vertex2 << ")=0. " "Is the graph connected?"); // GCOVR_EXCL_STOP diff --git a/tket/src/Clifford/UnitaryTableau.cpp b/tket/src/Clifford/UnitaryTableau.cpp index e73a1d1203..50a5dcc76c 100644 --- a/tket/src/Clifford/UnitaryTableau.cpp +++ b/tket/src/Clifford/UnitaryTableau.cpp @@ -715,16 +715,16 @@ std::ostream& operator<<(std::ostream& os, const UnitaryRevTableau& tab) { for (unsigned i = 0; i < nqs; ++i) { Qubit qi = tab.tab_.qubits_.right.at(i); os << tab.tab_.tab_.xmat.row(i) << " " << tab.tab_.tab_.zmat.row(i) - << " " << tab.tab_.tab_.phase(i) << "\t->\t" << "X@" << qi.repr() - << std::endl; + << " " << tab.tab_.tab_.phase(i) << "\t->\t" + << "X@" << qi.repr() << std::endl; } os << "--" << std::endl; for (unsigned i = 0; i < nqs; ++i) { Qubit qi = tab.tab_.qubits_.right.at(i); os << tab.tab_.tab_.xmat.row(i + nqs) << " " << tab.tab_.tab_.zmat.row(i + nqs) << " " - << tab.tab_.tab_.phase(i + nqs) << "\t->\t" << "Z@" << qi.repr() - << std::endl; + << tab.tab_.tab_.phase(i + nqs) << "\t->\t" + << "Z@" << qi.repr() << std::endl; } return os; } diff --git a/tket/src/Predicates/Predicates.cpp b/tket/src/Predicates/Predicates.cpp index eaa5f2cd82..e5a6207921 100644 --- a/tket/src/Predicates/Predicates.cpp +++ b/tket/src/Predicates/Predicates.cpp @@ -52,8 +52,7 @@ static std::string auto_name(const T&) { return predicate_name(typeid(T)); } -#define SET_PRED_NAME(a) \ - { typeid(a), #a } +#define SET_PRED_NAME(a) {typeid(a), #a} const std::string& predicate_name(std::type_index idx) { static const std::map predicate_names = { From 8fdb139880a1cd76f01d7af1ef6ec5729ec727e5 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Tue, 7 May 2024 09:12:04 +0100 Subject: [PATCH 12/33] Remove workflow to automatically create Jira issues. (#1378) --- .github/workflows/issue.yml | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 .github/workflows/issue.yml diff --git a/.github/workflows/issue.yml b/.github/workflows/issue.yml deleted file mode 100644 index b694b63bbd..0000000000 --- a/.github/workflows/issue.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: New issue - -on: - issues: - types: [opened] - -jobs: - jira_issue: - name: Create Jira issue - runs-on: ubuntu-22.04 - steps: - - name: Login - uses: atlassian/gajira-login@v3.0.1 - env: - JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} - JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} - JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} - - name: Create Bug - uses: atlassian/gajira-create@v3.0.1 - if: contains(github.event.issue.labels.*.name, 'bug') - with: - project: TKET - issuetype: Bug - summary: «${{ github.event.issue.title }}» - description: ${{ github.event.issue.html_url }} - - name: Create Task - uses: atlassian/gajira-create@v3.0.1 - if: "! contains(github.event.issue.labels.*.name, 'bug')" - with: - project: TKET - issuetype: Task - summary: «${{ github.event.issue.title }}» - description: ${{ github.event.issue.html_url }} From 145b70dfd0374ecf577c6a2583c4426b8e30add1 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Tue, 7 May 2024 11:20:01 +0100 Subject: [PATCH 13/33] Update libraries to boost 1.85.0 (#1379) --- libs/tktokenswap/conanfile.py | 4 ++-- libs/tktokenswap/test/conanfile.py | 2 +- libs/tkwsm/conanfile.py | 4 ++-- libs/tkwsm/test/conanfile.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libs/tktokenswap/conanfile.py b/libs/tktokenswap/conanfile.py index db0d5a6d1a..3c3410586c 100644 --- a/libs/tktokenswap/conanfile.py +++ b/libs/tktokenswap/conanfile.py @@ -19,7 +19,7 @@ class TktokenswapConan(ConanFile): name = "tktokenswap" - version = "0.3.7" + version = "0.3.8" package_type = "library" license = "Apache 2" url = "https://github.com/CQCL/tket" @@ -73,4 +73,4 @@ def requirements(self): self.requires("tklog/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable", transitive_headers=True) self.requires("tkrng/0.3.3@tket/stable") - self.requires("boost/1.84.0", transitive_libs=False) + self.requires("boost/1.85.0", transitive_libs=False) diff --git a/libs/tktokenswap/test/conanfile.py b/libs/tktokenswap/test/conanfile.py index ec9cdaf165..9835d66138 100644 --- a/libs/tktokenswap/test/conanfile.py +++ b/libs/tktokenswap/test/conanfile.py @@ -59,6 +59,6 @@ def package(self): cmake.install() def requirements(self): - self.requires("tktokenswap/0.3.7") + self.requires("tktokenswap/0.3.8") self.requires("tkrng/0.3.3@tket/stable") self.requires("catch2/3.5.4") diff --git a/libs/tkwsm/conanfile.py b/libs/tkwsm/conanfile.py index 613655278d..ed1190d48b 100644 --- a/libs/tkwsm/conanfile.py +++ b/libs/tkwsm/conanfile.py @@ -19,7 +19,7 @@ class TkwsmConan(ConanFile): name = "tkwsm" - version = "0.3.7" + version = "0.3.8" package_type = "library" license = "Apache 2" url = "https://github.com/CQCL/tket" @@ -72,4 +72,4 @@ def package_info(self): def requirements(self): self.requires("tkassert/0.3.4@tket/stable") self.requires("tkrng/0.3.3@tket/stable") - self.requires("boost/1.84.0", transitive_headers=True, transitive_libs=False) + self.requires("boost/1.85.0", transitive_headers=True, transitive_libs=False) diff --git a/libs/tkwsm/test/conanfile.py b/libs/tkwsm/test/conanfile.py index 89a0e3b97a..3041b8878e 100644 --- a/libs/tkwsm/test/conanfile.py +++ b/libs/tkwsm/test/conanfile.py @@ -59,7 +59,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tkwsm/0.3.7") + self.requires("tkwsm/0.3.8") self.requires("tkassert/0.3.4@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("catch2/3.5.4") From 6bcfa0a5c2b6288c74f4f14ab8703f8e3509ea95 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Tue, 7 May 2024 11:54:11 +0100 Subject: [PATCH 14/33] Update to catch2 3.6.0 (#1375) --- .github/workflows/build-without-conan.yml | 6 +++--- build-without-conan.md | 6 +++--- libs/tkassert/test/conanfile.py | 2 +- libs/tklog/test/conanfile.py | 2 +- libs/tkrng/test/conanfile.py | 2 +- libs/tktokenswap/test/conanfile.py | 2 +- libs/tkwsm/test/conanfile.py | 2 +- pytket/conanfile.py | 2 +- tket/conanfile.py | 4 ++-- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build-without-conan.yml b/.github/workflows/build-without-conan.yml index 1454e4d032..3a4fa6a4b9 100644 --- a/.github/workflows/build-without-conan.yml +++ b/.github/workflows/build-without-conan.yml @@ -74,9 +74,9 @@ jobs: - name: Install catch2 run: | cd ${TMP_DIR} - wget https://github.com/catchorg/Catch2/archive/refs/tags/v3.5.4.tar.gz - tar xzvf v3.5.4.tar.gz - cd Catch2-3.5.4/ + wget https://github.com/catchorg/Catch2/archive/refs/tags/v3.6.0.tar.gz + tar xzvf v3.6.0.tar.gz + cd Catch2-3.6.0/ mkdir build cd build cmake -GNinja -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} .. diff --git a/build-without-conan.md b/build-without-conan.md index 5a40b5d2c4..2968e8e486 100644 --- a/build-without-conan.md +++ b/build-without-conan.md @@ -91,9 +91,9 @@ cmake --install . ``` cd ${TMP_DIR} -wget https://github.com/catchorg/Catch2/archive/refs/tags/v3.5.4.tar.gz -tar xzvf v3.5.4.tar.gz -cd Catch2-3.5.4/ +wget https://github.com/catchorg/Catch2/archive/refs/tags/v3.6.0.tar.gz +tar xzvf v3.6.0.tar.gz +cd Catch2-3.6.0/ mkdir build cd build cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} .. diff --git a/libs/tkassert/test/conanfile.py b/libs/tkassert/test/conanfile.py index c3fc9af371..d92e5450b7 100644 --- a/libs/tkassert/test/conanfile.py +++ b/libs/tkassert/test/conanfile.py @@ -60,4 +60,4 @@ def package(self): def requirements(self): self.requires("tkassert/0.3.4") - self.requires("catch2/3.5.4") + self.requires("catch2/3.6.0") diff --git a/libs/tklog/test/conanfile.py b/libs/tklog/test/conanfile.py index b250a0bbbd..007d315926 100644 --- a/libs/tklog/test/conanfile.py +++ b/libs/tklog/test/conanfile.py @@ -60,4 +60,4 @@ def package(self): def requirements(self): self.requires("tklog/0.3.3") - self.requires("catch2/3.5.4") + self.requires("catch2/3.6.0") diff --git a/libs/tkrng/test/conanfile.py b/libs/tkrng/test/conanfile.py index eea5bec9f4..bcec0b0f04 100644 --- a/libs/tkrng/test/conanfile.py +++ b/libs/tkrng/test/conanfile.py @@ -60,4 +60,4 @@ def package(self): def requirements(self): self.requires("tkrng/0.3.3") - self.requires("catch2/3.5.4") + self.requires("catch2/3.6.0") diff --git a/libs/tktokenswap/test/conanfile.py b/libs/tktokenswap/test/conanfile.py index 9835d66138..180daf55bb 100644 --- a/libs/tktokenswap/test/conanfile.py +++ b/libs/tktokenswap/test/conanfile.py @@ -61,4 +61,4 @@ def package(self): def requirements(self): self.requires("tktokenswap/0.3.8") self.requires("tkrng/0.3.3@tket/stable") - self.requires("catch2/3.5.4") + self.requires("catch2/3.6.0") diff --git a/libs/tkwsm/test/conanfile.py b/libs/tkwsm/test/conanfile.py index 3041b8878e..ab74d320ad 100644 --- a/libs/tkwsm/test/conanfile.py +++ b/libs/tkwsm/test/conanfile.py @@ -62,4 +62,4 @@ def requirements(self): self.requires("tkwsm/0.3.8") self.requires("tkassert/0.3.4@tket/stable") self.requires("tkrng/0.3.3@tket/stable") - self.requires("catch2/3.5.4") + self.requires("catch2/3.6.0") diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 8eff94e165..74890df4fe 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.2.120@tket/stable") + self.requires("tket/1.2.121@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") diff --git a/tket/conanfile.py b/tket/conanfile.py index 1e948b370d..60a0a9624d 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.120" + version = "1.2.121" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" @@ -121,7 +121,7 @@ def requirements(self): self.requires("tktokenswap/0.3.7@tket/stable") self.requires("tkwsm/0.3.7@tket/stable") if self.build_test(): - self.test_requires("catch2/3.5.4") + self.test_requires("catch2/3.6.0") if self.build_proptest(): self.test_requires("rapidcheck/cci.20230815") From 18ceee4ed7118ea714dabfbe1b07829b50e177e2 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Tue, 7 May 2024 13:28:59 +0100 Subject: [PATCH 15/33] Update to boost 1.85.0 (#1381) --- .github/workflows/build-without-conan.yml | 6 +++--- build-without-conan.md | 6 +++--- pytket/conanfile.py | 6 +++--- tket/conanfile.py | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-without-conan.yml b/.github/workflows/build-without-conan.yml index 3a4fa6a4b9..dfacbaae7b 100644 --- a/.github/workflows/build-without-conan.yml +++ b/.github/workflows/build-without-conan.yml @@ -30,9 +30,9 @@ jobs: - name: Install boost run: | cd ${TMP_DIR} - wget -O boost_1_84_0.tar.gz https://sourceforge.net/projects/boost/files/boost/1.84.0/boost_1_84_0.tar.gz/download - tar xzvf boost_1_84_0.tar.gz - cd boost_1_84_0/ + wget -O boost_1_85_0.tar.gz https://sourceforge.net/projects/boost/files/boost/1.85.0/boost_1_85_0.tar.gz/download + tar xzvf boost_1_85_0.tar.gz + cd boost_1_85_0/ ./bootstrap.sh --prefix=${INSTALL_DIR} ./b2 ./b2 install diff --git a/build-without-conan.md b/build-without-conan.md index 2968e8e486..b6cff2efe9 100644 --- a/build-without-conan.md +++ b/build-without-conan.md @@ -22,9 +22,9 @@ The versions should match the current requirements as specified in the relevant ``` cd ${TMP_DIR} -wget -O boost_1_84_0.tar.gz https://sourceforge.net/projects/boost/files/boost/1.84.0/boost_1_84_0.tar.gz/download -tar xzvf boost_1_84_0.tar.gz -cd boost_1_84_0/ +wget -O boost_1_85_0.tar.gz https://sourceforge.net/projects/boost/files/boost/1.85.0/boost_1_85_0.tar.gz/download +tar xzvf boost_1_85_0.tar.gz +cd boost_1_85_0/ ./bootstrap.sh --prefix=${INSTALL_DIR} ./b2 ./b2 install diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 74890df4fe..e222e20965 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,12 +32,12 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.2.121@tket/stable") + self.requires("tket/1.2.122@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") - self.requires("tkwsm/0.3.7@tket/stable") - self.requires("tktokenswap/0.3.7@tket/stable") + self.requires("tkwsm/0.3.8@tket/stable") + self.requires("tktokenswap/0.3.8@tket/stable") self.requires("symengine/0.11.2") self.requires("gmp/6.3.0") self.requires("pybind11/2.12.0") diff --git a/tket/conanfile.py b/tket/conanfile.py index 60a0a9624d..ed514e1483 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.121" + version = "1.2.122" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" @@ -111,15 +111,15 @@ def package_info(self): def requirements(self): # libraries installed from remote: # https://quantinuumsw.jfrog.io/artifactory/api/conan/tket1-libs - self.requires("boost/1.84.0", transitive_headers=True) + self.requires("boost/1.85.0", transitive_headers=True) self.requires("symengine/0.11.2", transitive_headers=True) self.requires("eigen/3.4.0", transitive_headers=True) self.requires("nlohmann_json/3.11.3", transitive_headers=True) self.requires("tklog/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable", transitive_headers=True) self.requires("tkrng/0.3.3@tket/stable") - self.requires("tktokenswap/0.3.7@tket/stable") - self.requires("tkwsm/0.3.7@tket/stable") + self.requires("tktokenswap/0.3.8@tket/stable") + self.requires("tkwsm/0.3.8@tket/stable") if self.build_test(): self.test_requires("catch2/3.6.0") if self.build_proptest(): From 23bb57e809afe79a689a799e559bb6c780179911 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Fri, 10 May 2024 07:47:30 +0100 Subject: [PATCH 16/33] Use `lark` package instead of deprecated `lark-parser` (#1386) --- nix-support/pytket.nix | 2 +- nix-support/third-party-python-packages.nix | 12 +++++++----- pytket/docs/changelog.rst | 1 + pytket/pytket/qasm/qasm.py | 4 ++-- pytket/pytket/quipper/quipper.py | 19 +++++++++++-------- pytket/setup.py | 2 +- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/nix-support/pytket.nix b/nix-support/pytket.nix index 6368ac7a1f..5291e423af 100644 --- a/nix-support/pytket.nix +++ b/nix-support/pytket.nix @@ -39,7 +39,7 @@ in { inherit version; propagatedBuildInputs = with super.python3.pkgs; [ self.binders - super.lark-parser + super.lark super.types-pkg_resources super.qwasm graphviz diff --git a/nix-support/third-party-python-packages.nix b/nix-support/third-party-python-packages.nix index b462a4c0b7..de9b5e2e61 100644 --- a/nix-support/third-party-python-packages.nix +++ b/nix-support/third-party-python-packages.nix @@ -19,15 +19,17 @@ self: super: { sha256 = "sha256:g/QA5CpAR3exRDgVQMnXGIH8bEGtwGFBjjSblbdXRkU="; }; }; - lark-parser = super.python3.pkgs.buildPythonPackage { - pname = "lark-parser"; - version = "0.12.0"; + lark = super.python3.pkgs.buildPythonPackage { + pname = "lark"; + version = "1.1.9"; + format = "pyproject"; src = super.fetchFromGitHub { owner = "lark-parser"; repo = "lark"; - rev = "refs/tags/0.12.0"; - hash = "sha256-zcMGCn3ixD3dJg3GlC/ijs+U1JN1BodHLTXZc/5UR7Y="; + rev = "refs/tags/1.1.9"; + hash = "sha256:pWLKjELy10VNumpBHjBYCO2TltKsZx1GhQcGMHsYJNk="; }; + nativeBuildInputs = with super.python3Packages; [ setuptools ]; doCheck = false; }; types-pkg_resources = let diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index a771bc3d77..5b47149167 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -8,6 +8,7 @@ Features: * Update to pytket-circuit-renderer 0.8. * Add two new status values for circuits on backends: "CANCELLING" and "RETRYING". +* Use `lark` package instead of deprecated `lark-parser`. 1.27.0 (April 2024) ------------------- diff --git a/pytket/pytket/qasm/qasm.py b/pytket/pytket/qasm/qasm.py index 68847d5481..07965b7e4b 100644 --- a/pytket/pytket/qasm/qasm.py +++ b/pytket/pytket/qasm/qasm.py @@ -793,7 +793,7 @@ def assign(self, tree: List) -> Iterable[CommandDict]: else: raise QASMParseError(f"Unexpected expression in assignment {exp}", line) - def extern(self, tree: List[Any]) -> Type[Discard]: + def extern(self, tree: List[Any]) -> Any: # TODO parse extern defs return Discard @@ -899,7 +899,7 @@ def gdef(self, tree: List) -> None: opaq = gdef - def oqasm(self, tree: List) -> Type[Discard]: + def oqasm(self, tree: List) -> Any: return Discard def incl(self, tree: List[Token]) -> None: diff --git a/pytket/pytket/quipper/quipper.py b/pytket/pytket/quipper/quipper.py index 30543bd275..f2be0abcf8 100644 --- a/pytket/pytket/quipper/quipper.py +++ b/pytket/pytket/quipper/quipper.py @@ -183,6 +183,7 @@ class Subroutine_Control(Enum): ) Start = NamedTuple("Start", [("circuit", Program), ("subroutines", List[Subroutine])]) + # Transformer class QuipperTransformer(Transformer): def int(self, t: List) -> int: @@ -284,18 +285,20 @@ def cdiscard(self, t: List) -> CDiscard: return CDiscard(wire=t[0]) def subroutine_call(self, t: List) -> SubroutineCall: + for i, ti in enumerate(t): + print(f"t[{i}] = {ti}") repetitions = 1 - if isinstance(t[0], int): + if t[0] is not None: + assert isinstance(t[0], int) repetitions = t[0] - t.pop(0) return SubroutineCall( repetitions=repetitions, - name=t[0], - shape=t[1], - inverted=len(t[2].children) > 0, - inputs=t[3], - outputs=t[4], - control=t[5], + name=t[1], + shape=t[2], + inverted=len(t[3].children) > 0, + inputs=t[4], + outputs=t[5], + control=t[6], ) def comment(self, t: List) -> Comment: diff --git a/pytket/setup.py b/pytket/setup.py index bd9ff5d8dd..a99ff83bd0 100755 --- a/pytket/setup.py +++ b/pytket/setup.py @@ -192,7 +192,7 @@ def finalize_options(self): install_requires=[ "sympy ~=1.6", "numpy >=1.21.4, <2.0", - "lark-parser ~=0.7", + "lark ~=1.1", "scipy ~=1.13", "networkx >= 2.8.8", "graphviz ~= 0.14", From 9f2a073fe85e77ffa32423cfd1395f81d37975c7 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Mon, 13 May 2024 10:16:52 +0100 Subject: [PATCH 17/33] Escape underscores when converting to latex (#1385) --- pytket/conanfile.py | 2 +- pytket/docs/changelog.rst | 4 ++++ tket/conanfile.py | 2 +- tket/src/Circuit/latex_drawing.cpp | 7 +++++-- tket/test/src/Circuit/test_Circ.cpp | 8 ++++++++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/pytket/conanfile.py b/pytket/conanfile.py index e222e20965..4360bf35ea 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.2.122@tket/stable") + self.requires("tket/1.2.123@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 5b47149167..b8c043a465 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -10,6 +10,10 @@ Features: * Add two new status values for circuits on backends: "CANCELLING" and "RETRYING". * Use `lark` package instead of deprecated `lark-parser`. +Fixes: + +* Escape underscores in qubit and bit names when converting to latex. + 1.27.0 (April 2024) ------------------- diff --git a/tket/conanfile.py b/tket/conanfile.py index ed514e1483..91176e68ed 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.122" + version = "1.2.123" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" diff --git a/tket/src/Circuit/latex_drawing.cpp b/tket/src/Circuit/latex_drawing.cpp index bd47547283..56fab702bc 100644 --- a/tket/src/Circuit/latex_drawing.cpp +++ b/tket/src/Circuit/latex_drawing.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include "tket/Circuit/Boxes.hpp" @@ -287,14 +288,16 @@ std::string Circuit::to_latex_str() const { unsigned n_lines = lines.size(); line_ids.insert({qb, n_lines}); LineBufferInfo& line = *lines.emplace(lines.end()); - line.buffer << "\\lstick{" + qb.repr() + "} & "; + line.buffer << "\\lstick{" + + boost::replace_all_copy(qb.repr(), "_", "\\_") + "} & "; line.is_quantum = true; } for (const Bit& cb : this->all_bits()) { unsigned n_lines = lines.size(); line_ids.insert({cb, n_lines}); LineBufferInfo& line = *lines.emplace(lines.end()); - line.buffer << "\\lstick{" + cb.repr() + "} & "; + line.buffer << "\\lstick{" + + boost::replace_all_copy(cb.repr(), "_", "\\_") + "} & "; line.is_quantum = false; } diff --git a/tket/test/src/Circuit/test_Circ.cpp b/tket/test/src/Circuit/test_Circ.cpp index a59d77692e..c09122a9f2 100644 --- a/tket/test/src/Circuit/test_Circ.cpp +++ b/tket/test/src/Circuit/test_Circ.cpp @@ -37,6 +37,7 @@ #include "tket/Transformations/Replacement.hpp" #include "tket/Transformations/Transform.hpp" #include "tket/Utils/MatrixAnalysis.hpp" +#include "tket/Utils/UnitID.hpp" namespace tket { namespace test_Circ { @@ -2694,6 +2695,13 @@ SCENARIO("Confirm that LaTeX output compiles", "[latex][.long]") { c.add_conditional_gate( OpType::CU3, {1.04, 0.36, -0.36}, {0, 4}, {}, 0); + // https://github.com/CQCL/tket/issues/1363 + Qubit q1("q_1", 0); + Bit c1("c_1", 0); + c.add_qubit(q1); + c.add_bit(c1); + c.add_measure(q1, c1); + c.to_latex_file("circ.tex"); int response = std::system("latexmk -pdf circ.tex -quiet"); REQUIRE(response == 0); From fede38dd2e2afdf4b91101feeae724bb4928c2bd Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Mon, 13 May 2024 11:00:48 +0100 Subject: [PATCH 18/33] Remove debug prints. (#1387) --- pytket/pytket/quipper/quipper.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pytket/pytket/quipper/quipper.py b/pytket/pytket/quipper/quipper.py index f2be0abcf8..5c0d4d8328 100644 --- a/pytket/pytket/quipper/quipper.py +++ b/pytket/pytket/quipper/quipper.py @@ -285,8 +285,6 @@ def cdiscard(self, t: List) -> CDiscard: return CDiscard(wire=t[0]) def subroutine_call(self, t: List) -> SubroutineCall: - for i, ti in enumerate(t): - print(f"t[{i}] = {ti}") repetitions = 1 if t[0] is not None: assert isinstance(t[0], int) From aafcd59d85c6c94707dc9669cadf28b4ce87ef03 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Tue, 14 May 2024 09:05:44 +0100 Subject: [PATCH 19/33] Update to pybind11_json 0.2.14 (#1389) --- .github/workflows/build-without-conan.yml | 6 +++--- .github/workflows/build_and_test.yml | 6 +++--- .github/workflows/linuxbuildwheel | 2 +- .github/workflows/pytket_benchmarking.yml | 2 +- .github/workflows/release.yml | 6 +++--- build-without-conan.md | 6 +++--- nix-support/third-party-python-packages.nix | 4 ++-- pytket/CMakeLists.txt | 4 ---- pytket/README.md | 2 +- pytket/conanfile.py | 4 ++-- recipes/pybind11_json/all/conandata.yml | 3 +++ recipes/pybind11_json/config.yml | 2 ++ tket/conanfile.py | 2 +- 13 files changed, 25 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build-without-conan.yml b/.github/workflows/build-without-conan.yml index dfacbaae7b..311d153b41 100644 --- a/.github/workflows/build-without-conan.yml +++ b/.github/workflows/build-without-conan.yml @@ -107,9 +107,9 @@ jobs: - name: Install pybind11_json run: | cd ${TMP_DIR} - wget https://github.com/pybind/pybind11_json/archive/refs/tags/0.2.13.tar.gz - tar xzvf 0.2.13.tar.gz - cd pybind11_json-0.2.13/ + wget https://github.com/pybind/pybind11_json/archive/refs/tags/0.2.14.tar.gz + tar xzvf 0.2.14.tar.gz + cd pybind11_json-0.2.14/ mkdir build cd build cmake -GNinja -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} .. diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index c4ff3ef09f..eca21c7f2a 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -257,7 +257,7 @@ jobs: - name: Install pytket requirements run: | conan create recipes/pybind11 - conan create recipes/pybind11_json/all --version 0.2.13 + conan create recipes/pybind11_json/all --version 0.2.14 - name: Set up Python (pull request) if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' uses: actions/setup-python@v5 @@ -370,7 +370,7 @@ jobs: - name: Install pytket requirements run: | conan create recipes/pybind11 - conan create recipes/pybind11_json/all --version 0.2.13 + conan create recipes/pybind11_json/all --version 0.2.14 - name: Set up Python (pull request) if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' uses: actions/setup-python@v5 @@ -490,7 +490,7 @@ jobs: - name: Install pytket requirements run: | conan create recipes/pybind11 - conan create recipes/pybind11_json/all --version 0.2.13 + conan create recipes/pybind11_json/all --version 0.2.14 - name: Set up Python 3.10 if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' uses: actions/setup-python@v5 diff --git a/.github/workflows/linuxbuildwheel b/.github/workflows/linuxbuildwheel index b79de7f6c5..1b75adae19 100755 --- a/.github/workflows/linuxbuildwheel +++ b/.github/workflows/linuxbuildwheel @@ -42,7 +42,7 @@ mkdir /tket/pytket/audited # Install pybind11 and pybind11_json ${CONAN_CMD} create recipes/pybind11 -${CONAN_CMD} create recipes/pybind11_json/all --version 0.2.13 +${CONAN_CMD} create recipes/pybind11_json/all --version 0.2.14 # build pytket cd pytket diff --git a/.github/workflows/pytket_benchmarking.yml b/.github/workflows/pytket_benchmarking.yml index 171e4188d4..afae4750b2 100644 --- a/.github/workflows/pytket_benchmarking.yml +++ b/.github/workflows/pytket_benchmarking.yml @@ -42,7 +42,7 @@ jobs: - name: Build wheel run: | conan create recipes/pybind11 - conan create recipes/pybind11_json/all --version 0.2.13 + conan create recipes/pybind11_json/all --version 0.2.14 cd pytket # Ensure wheels are compatible with MacOS 12.0 and later: export WHEEL_PLAT_NAME=macosx_12_0_arm64 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6cca5016ab..8b70da70c5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -92,7 +92,7 @@ jobs: - name: Build wheel run: | conan create recipes/pybind11 - conan create recipes/pybind11_json/all --version 0.2.13 + conan create recipes/pybind11_json/all --version 0.2.14 cd pytket # Ensure wheels are compatible with MacOS 12.0 and later: export WHEEL_PLAT_NAME=macosx_12_0_x86_64 @@ -136,7 +136,7 @@ jobs: - name: Build wheel run: | conan create recipes/pybind11 - conan create recipes/pybind11_json/all --version 0.2.13 + conan create recipes/pybind11_json/all --version 0.2.14 cd pytket # Ensure wheels are compatible with MacOS 12.0 and later: export WHEEL_PLAT_NAME=macosx_12_0_arm64 @@ -179,7 +179,7 @@ jobs: - name: Build wheel run: | conan create recipes/pybind11 - conan create recipes/pybind11_json/all --version 0.2.13 + conan create recipes/pybind11_json/all --version 0.2.14 cd pytket python -m pip install -U pip build python -m build --outdir "${{ github.workspace }}/wheelhouse" diff --git a/build-without-conan.md b/build-without-conan.md index b6cff2efe9..16668345b4 100644 --- a/build-without-conan.md +++ b/build-without-conan.md @@ -133,9 +133,9 @@ cmake --install . ``` cd ${TMP_DIR} -wget https://github.com/pybind/pybind11_json/archive/refs/tags/0.2.13.tar.gz -tar xzvf 0.2.13.tar.gz -cd pybind11_json-0.2.13/ +wget https://github.com/pybind/pybind11_json/archive/refs/tags/0.2.14.tar.gz +tar xzvf 0.2.14.tar.gz +cd pybind11_json-0.2.14/ mkdir build cd build cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} .. diff --git a/nix-support/third-party-python-packages.nix b/nix-support/third-party-python-packages.nix index de9b5e2e61..6349663014 100644 --- a/nix-support/third-party-python-packages.nix +++ b/nix-support/third-party-python-packages.nix @@ -4,8 +4,8 @@ self: super: { src = super.fetchFromGitHub { owner = "pybind"; repo = "pybind11_json"; - rev = "0.2.13"; - sha256 = "sha256:Kl/QflV2bBoH72/LW03K8JDlhBF+DYYXL47A5s1nmTw="; + rev = "0.2.14"; + sha256 = "sha256-6L675DsfafzRv0mRR3b0eUFFjUpll3jCPoBAAffk7U0="; }; nativeBuildInputs = [ super.cmake ]; buildInputs = [ super.python3Packages.pybind11 super.nlohmann_json ]; diff --git a/pytket/CMakeLists.txt b/pytket/CMakeLists.txt index 455fc2d297..9b66d5c6c2 100644 --- a/pytket/CMakeLists.txt +++ b/pytket/CMakeLists.txt @@ -61,10 +61,6 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(Apple)?Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-declarations") # remove -Wno-deprecated-declarations once https://github.com/boostorg/boost/issues/688 is resolved endif() -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - # Suppress warnings coming from headers in pybind11_json/0.2.13: - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-redundant-move") -endif() if (UNIX) # Allow binder libraries to load other shared libraries from same directory. diff --git a/pytket/README.md b/pytket/README.md index aae8a9e915..31c8a1b5a1 100644 --- a/pytket/README.md +++ b/pytket/README.md @@ -23,7 +23,7 @@ It is also currently necessary to use the local `pybind11_json` recipe, since the recipe on the `conan-center` is not yet compatible with conan 2: ```shell -conan create recipes/pybind11_json/all --version=0.2.13 +conan create recipes/pybind11_json/all --version=0.2.14 ``` Then build the pytket module: diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 4360bf35ea..3e6f3676e6 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.2.123@tket/stable") + self.requires("tket/1.2.124@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") @@ -42,4 +42,4 @@ def requirements(self): self.requires("gmp/6.3.0") self.requires("pybind11/2.12.0") self.requires("nlohmann_json/3.11.3") - self.requires("pybind11_json/0.2.13") + self.requires("pybind11_json/0.2.14") diff --git a/recipes/pybind11_json/all/conandata.yml b/recipes/pybind11_json/all/conandata.yml index 58e86b8427..bbb34dfcf9 100644 --- a/recipes/pybind11_json/all/conandata.yml +++ b/recipes/pybind11_json/all/conandata.yml @@ -1,4 +1,7 @@ sources: + "0.2.14": + url: "https://github.com/pybind/pybind11_json/archive/0.2.14.tar.gz" + sha256: "bc4ad7e308add59886a961c21f3ba431e43fe7faa2ef5bd9925c66d042d28cde" "0.2.13": url: "https://github.com/pybind/pybind11_json/archive/0.2.13.tar.gz" sha256: "6b12ddb4930a3135322890318fc15c4a69134f21120ea82163827c11411107a3" diff --git a/recipes/pybind11_json/config.yml b/recipes/pybind11_json/config.yml index d889e66f48..53c6e6ca1f 100644 --- a/recipes/pybind11_json/config.yml +++ b/recipes/pybind11_json/config.yml @@ -1,4 +1,6 @@ versions: + "0.2.14": + folder: all "0.2.13": folder: all "0.2.12": diff --git a/tket/conanfile.py b/tket/conanfile.py index 91176e68ed..cd153cc891 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.123" + version = "1.2.124" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" From 385d14936491a0f080219676fadf575163cafde9 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Tue, 14 May 2024 15:37:31 +0100 Subject: [PATCH 20/33] Add workflow to automatically add new issues to project. (#1390) --- .github/workflows/issue-to-project.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/issue-to-project.yml diff --git a/.github/workflows/issue-to-project.yml b/.github/workflows/issue-to-project.yml new file mode 100644 index 0000000000..5c8ba372c5 --- /dev/null +++ b/.github/workflows/issue-to-project.yml @@ -0,0 +1,16 @@ +name: Add issues to project + +on: + issues: + types: + - opened + +jobs: + add-to-project: + name: Add issue to project + runs-on: ubuntu-latest + steps: + - uses: actions/add-to-project@v0.6.1 + with: + project-url: https://github.com/orgs/CQCL-DEV/projects/19 + github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} From 8aa12f140dfdf8959712fa6fce90669222038e66 Mon Sep 17 00:00:00 2001 From: cqc-melf <70640934+cqc-melf@users.noreply.github.com> Date: Tue, 14 May 2024 16:27:26 +0100 Subject: [PATCH 21/33] update tket email (#1391) --- pytket/package.md | 2 +- pytket/setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pytket/package.md b/pytket/package.md index 5bd7ee3317..1d253af59c 100644 --- a/pytket/package.md +++ b/pytket/package.md @@ -35,7 +35,7 @@ For worked examples using TKET see our [notebook examples](https://tket.quantinu For bugs and feature requests we recommend creating an issue on the [github repository](https://github.com/CQCL/tket). -User support: tket-support@cambridgequantum.com +User support: tket-support@quantinuum.com For discussion, join the public slack channel [here](https://join.slack.com/t/tketusers/shared_invite/zt-18qmsamj9-UqQFVdkRzxnXCcKtcarLRA). diff --git a/pytket/setup.py b/pytket/setup.py index a99ff83bd0..243e3c43fe 100755 --- a/pytket/setup.py +++ b/pytket/setup.py @@ -177,7 +177,7 @@ def finalize_options(self): setup( name="pytket", author="TKET development team", - author_email="tket-support@cambridgequantum.com", + author_email="tket-support@quantinuum.com", python_requires=">=3.10", project_urls={ "Documentation": "https://tket.quantinuum.com/api-docs/index.html", From 436d3e372aea20b41df7756f64383fb4c01089ec Mon Sep 17 00:00:00 2001 From: yao-cqc <75305462+yao-cqc@users.noreply.github.com> Date: Tue, 14 May 2024 17:05:54 +0100 Subject: [PATCH 22/33] Feature/greedy pauli simp (#1384) * Add header and lookup data * Implement GreedyPauliSimp * Add to pass generators * Add c++ tests * Add to python binder * Add python tests * Regenerate stubs * Bump tket version * Update changelog * Rename the header file for lookups * Replace pace with gate_depth * Remove unused includes * Minor changes & fix grammar issues --- pytket/binders/passes.cpp | 13 + pytket/binders/transform.cpp | 16 + pytket/conanfile.py | 2 +- pytket/docs/changelog.rst | 1 + pytket/pytket/_tket/passes.pyi | 10 +- pytket/pytket/_tket/transform.pyi | 9 + pytket/tests/passes_serialisation_test.py | 4 + pytket/tests/predicates_test.py | 15 + schemas/compiler_pass_v1.json | 27 +- tket/CMakeLists.txt | 3 + tket/conanfile.py | 2 +- .../tket/Predicates/PassGenerators.hpp | 9 + .../GreedyPauliOptimisation.hpp | 216 +++ .../GreedyPauliOptimisationLookupTables.hpp | 1569 +++++++++++++++++ tket/src/Predicates/CompilerPass.cpp | 4 + tket/src/Predicates/PassGenerators.cpp | 34 + .../GreedyPauliOptimisation.cpp | 947 ++++++++++ tket/test/CMakeLists.txt | 1 + tket/test/src/test_GreedyPauli.cpp | 294 +++ tket/test/src/test_json.cpp | 1 + 20 files changed, 3173 insertions(+), 4 deletions(-) create mode 100644 tket/include/tket/Transformations/GreedyPauliOptimisation.hpp create mode 100644 tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp create mode 100644 tket/src/Transformations/GreedyPauliOptimisation.cpp create mode 100644 tket/test/src/test_GreedyPauli.cpp diff --git a/pytket/binders/passes.cpp b/pytket/binders/passes.cpp index 22f07ff06f..bd621e0cae 100644 --- a/pytket/binders/passes.cpp +++ b/pytket/binders/passes.cpp @@ -902,6 +902,19 @@ PYBIND11_MODULE(passes, m) { "\n:return: a pass to perform the simplification", py::arg("strat") = Transforms::PauliSynthStrat::Sets, py::arg("cx_config") = CXConfigType::Snake); + m.def( + "GreedyPauliSimp", &gen_greedy_pauli_simp, + "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." + "\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:return: a pass to perform the simplification", + py::arg("discount_rate") = 0.7, py::arg("depth_weight") = 0.3); m.def( "PauliSquash", &PauliSquash, "Applies :py:meth:`PauliSimp` followed by " diff --git a/pytket/binders/transform.cpp b/pytket/binders/transform.cpp index 91a1c12276..b912b4ebec 100644 --- a/pytket/binders/transform.cpp +++ b/pytket/binders/transform.cpp @@ -26,6 +26,7 @@ #include "tket/Transformations/Combinator.hpp" #include "tket/Transformations/ContextualReduction.hpp" #include "tket/Transformations/Decomposition.hpp" +#include "tket/Transformations/GreedyPauliOptimisation.hpp" #include "tket/Transformations/OptimisationPass.hpp" #include "tket/Transformations/PauliOptimisation.hpp" #include "tket/Transformations/Rebase.hpp" @@ -407,6 +408,21 @@ PYBIND11_MODULE(transform, m) { "provides them.", py::arg("synth_strat") = Transforms::PauliSynthStrat::Sets, py::arg("cx_config") = CXConfigType::Snake) + .def_static( + "GreedyPauliSimp", &Transforms::greedy_pauli_optimisation, + "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." + "\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:return: a pass to perform the simplification", + py::arg("discount_rate") = 0.7, py::arg("depth_weight") = 0.3) .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 3e6f3676e6..0fc08c2376 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.2.124@tket/stable") + self.requires("tket/1.2.125@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index b8c043a465..a238901f10 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -9,6 +9,7 @@ Features: * Update to pytket-circuit-renderer 0.8. * Add two new status values for circuits on backends: "CANCELLING" and "RETRYING". * Use `lark` package instead of deprecated `lark-parser`. +* Add ``GreedyPauliSimp`` optimisation pass. Fixes: diff --git a/pytket/pytket/_tket/passes.pyi b/pytket/pytket/_tket/passes.pyi index 22bc51873e..a73bbee83c 100644 --- a/pytket/pytket/_tket/passes.pyi +++ b/pytket/pytket/_tket/passes.pyi @@ -9,7 +9,7 @@ import pytket._tket.transform import pytket._tket.unit_id import sympy import typing -__all__ = ['AASRouting', 'Audit', 'BasePass', 'CNotSynthType', 'CXMappingPass', 'CliffordPushThroughMeasures', 'CliffordResynthesis', 'CliffordSimp', 'CnXPairwiseDecomposition', 'CommuteThroughMultis', 'ComposePhasePolyBoxes', 'ContextSimp', 'CustomPass', 'CustomRoutingPass', 'DecomposeArbitrarilyControlledGates', 'DecomposeBoxes', 'DecomposeClassicalExp', 'DecomposeMultiQubitsCX', 'DecomposeSingleQubitsTK1', 'DecomposeSwapsToCXs', 'DecomposeSwapsToCircuit', 'DecomposeTK2', 'Default', 'DefaultMappingPass', 'DelayMeasures', 'EulerAngleReduction', 'FlattenRegisters', 'FlattenRelabelRegistersPass', 'FullMappingPass', 'FullPeepholeOptimise', 'GlobalisePhasedX', 'GuidedPauliSimp', 'HamPath', 'KAKDecomposition', 'NaivePlacementPass', 'NormaliseTK2', 'OptimisePhaseGadgets', 'PauliExponentials', 'PauliSimp', 'PauliSquash', 'PeepholeOptimise2Q', 'PlacementPass', 'RebaseCustom', 'RebaseTket', 'Rec', 'RemoveBarriers', 'RemoveDiscarded', 'RemoveImplicitQubitPermutation', 'RemoveRedundancies', 'RenameQubitsPass', 'RepeatPass', 'RepeatUntilSatisfiedPass', 'RepeatWithMetricPass', 'RoundAngles', 'RoutingPass', 'SWAP', 'SafetyMode', 'SequencePass', 'SimplifyInitial', 'SimplifyMeasured', 'SquashCustom', 'SquashRzPhasedX', 'SquashTK1', 'SynthesiseOQC', 'SynthesiseTK', 'SynthesiseTket', 'SynthesiseUMD', 'ThreeQubitSquash', 'ZXGraphlikeOptimisation', 'ZZPhaseToRz'] +__all__ = ['AASRouting', 'Audit', 'BasePass', 'CNotSynthType', 'CXMappingPass', 'CliffordPushThroughMeasures', 'CliffordResynthesis', 'CliffordSimp', 'CnXPairwiseDecomposition', 'CommuteThroughMultis', 'ComposePhasePolyBoxes', 'ContextSimp', 'CustomPass', 'CustomRoutingPass', 'DecomposeArbitrarilyControlledGates', 'DecomposeBoxes', 'DecomposeClassicalExp', 'DecomposeMultiQubitsCX', 'DecomposeSingleQubitsTK1', 'DecomposeSwapsToCXs', 'DecomposeSwapsToCircuit', 'DecomposeTK2', 'Default', 'DefaultMappingPass', 'DelayMeasures', 'EulerAngleReduction', 'FlattenRegisters', 'FlattenRelabelRegistersPass', 'FullMappingPass', 'FullPeepholeOptimise', 'GlobalisePhasedX', 'GreedyPauliSimp', 'GuidedPauliSimp', 'HamPath', 'KAKDecomposition', 'NaivePlacementPass', 'NormaliseTK2', 'OptimisePhaseGadgets', 'PauliExponentials', 'PauliSimp', 'PauliSquash', 'PeepholeOptimise2Q', 'PlacementPass', 'RebaseCustom', 'RebaseTket', 'Rec', 'RemoveBarriers', 'RemoveDiscarded', 'RemoveImplicitQubitPermutation', 'RemoveRedundancies', 'RenameQubitsPass', 'RepeatPass', 'RepeatUntilSatisfiedPass', 'RepeatWithMetricPass', 'RoundAngles', 'RoutingPass', 'SWAP', 'SafetyMode', 'SequencePass', 'SimplifyInitial', 'SimplifyMeasured', 'SquashCustom', 'SquashRzPhasedX', 'SquashTK1', 'SynthesiseOQC', 'SynthesiseTK', 'SynthesiseTket', 'SynthesiseUMD', 'ThreeQubitSquash', 'ZXGraphlikeOptimisation', 'ZZPhaseToRz'] class BasePass: """ Base class for passes. @@ -404,6 +404,14 @@ 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: + """ + 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. + :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: """ Applies the ``PauliSimp`` optimisation pass to any region of the circuit contained within a :py:class:`CircBox`. This can be useful to focus the synthesis to target specific sets of commuting operations, rather than the default greedy approach. diff --git a/pytket/pytket/_tket/transform.pyi b/pytket/pytket/_tket/transform.pyi index a3b2e88c29..5fd1e85040 100644 --- a/pytket/pytket/_tket/transform.pyi +++ b/pytket/pytket/_tket/transform.pyi @@ -159,6 +159,15 @@ 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: + """ + 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. + :return: a pass to perform the simplification + """ + @staticmethod @typing.overload def KAKDecomposition(target_2qb_gate: pytket._tket.circuit.OpType = pytket._tket.circuit.OpType.CX, cx_fidelity: float = 1.0, allow_swaps: bool = True) -> Transform: """ diff --git a/pytket/tests/passes_serialisation_test.py b/pytket/tests/passes_serialisation_test.py index 07b0cab816..e4c389c2ac 100644 --- a/pytket/tests/passes_serialisation_test.py +++ b/pytket/tests/passes_serialisation_test.py @@ -41,6 +41,7 @@ DefaultMappingPass, AASRouting, SquashCustom, + GreedyPauliSimp, ) from pytket.mapping import ( LexiLabellingMethod, @@ -289,6 +290,9 @@ def nonparam_predicate_dict(name: str) -> Dict[str, Any]: "RoundAngles": standard_pass_dict( {"name": "RoundAngles", "n": 6, "only_zeros": False} ), + "GreedyPauliSimp": standard_pass_dict( + {"name": "GreedyPauliSimp", "discount_rate": 0.4, "depth_weight": 0.5} + ), } # non-parametrized passes that satisfy pass.from_dict(d).to_dict()==d diff --git a/pytket/tests/predicates_test.py b/pytket/tests/predicates_test.py index 2afc1d625b..8dc46ad3f0 100644 --- a/pytket/tests/predicates_test.py +++ b/pytket/tests/predicates_test.py @@ -74,6 +74,7 @@ CliffordSimp, SynthesiseOQC, ZXGraphlikeOptimisation, + GreedyPauliSimp, ) from pytket.predicates import ( GateSetPredicate, @@ -1047,6 +1048,20 @@ def test_clifford_push_through_measures() -> None: assert coms[7].op.type == OpType.CopyBits +def greedy_pauli_synth() -> None: + circ = Circuit(4, 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]) + pss = GreedyPauliSimp(0.5, 0.5) + assert pss.apply(d) + assert np.allclose(circ.get_unitary(), d.get_unitary()) + assert d.name == "test" + + def test_SynthesiseOQC_deprecation(capfd: Any) -> None: logging.set_level(logging.level.warn) p = SynthesiseOQC() diff --git a/schemas/compiler_pass_v1.json b/schemas/compiler_pass_v1.json index 404e19f954..0720c6c157 100644 --- a/schemas/compiler_pass_v1.json +++ b/schemas/compiler_pass_v1.json @@ -163,7 +163,8 @@ "DecomposeTK2", "CnXPairwiseDecomposition", "RemoveImplicitQubitPermutation", - "RoundAngles" + "RoundAngles", + "GreedyPauliSimp" ], "description": "The name of the compiler pass. Matches the name of the pytket method used to generate it. List all the passes as enum." }, @@ -334,6 +335,14 @@ "type": "string" }, "description": "opgroups excluded in \"DecomposeBoxes\"" + }, + "discount_rate": { + "type": "number", + "definition": "parameter controlling cost discount in \"GreedyPauliSimp\"" + }, + "depth_weight": { + "type": "number", + "definition": "parameter controlling the degree of depth optimisation in \"GreedyPauliSimp\"" } }, "required": [ @@ -818,6 +827,22 @@ "maxProperties": 3 } }, + { + "if": { + "properties": { + "name": { + "const": "GreedyPauliSimp" + } + } + }, + "then": { + "required": [ + "discount_rate", + "depth_weight" + ], + "maxProperties": 3 + } + }, { "if": { "properties": { diff --git a/tket/CMakeLists.txt b/tket/CMakeLists.txt index 0185f4ece6..2d5a40a0d3 100644 --- a/tket/CMakeLists.txt +++ b/tket/CMakeLists.txt @@ -268,6 +268,7 @@ target_sources(tket src/Transformations/OptimisationPass.cpp src/Transformations/PhaseOptimisation.cpp src/Transformations/Decomposition.cpp + src/Transformations/GreedyPauliOptimisation.cpp src/Transformations/Replacement.cpp src/Transformations/MeasurePass.cpp src/Transformations/ContextualReduction.cpp @@ -409,6 +410,8 @@ target_sources(tket include/tket/Transformations/Combinator.hpp include/tket/Transformations/ContextualReduction.hpp include/tket/Transformations/Decomposition.hpp + include/tket/Transformations/GreedyPauliOptimisation.hpp + include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp include/tket/Transformations/MeasurePass.hpp include/tket/Transformations/OptimisationPass.hpp include/tket/Transformations/PauliOptimisation.hpp diff --git a/tket/conanfile.py b/tket/conanfile.py index cd153cc891..180dfffec2 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.124" + version = "1.2.125" 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 55c17bd55c..6bd2d7a7c4 100644 --- a/tket/include/tket/Predicates/PassGenerators.hpp +++ b/tket/include/tket/Predicates/PassGenerators.hpp @@ -325,6 +325,15 @@ PassPtr gen_special_UCC_synthesis( Transforms::PauliSynthStrat strat = Transforms::PauliSynthStrat::Sets, CXConfigType cx_config = CXConfigType::Snake); +/** + * @brief Greedy synthesis for Pauli graphs. + * + * @param discount_rate + * @param depth_weight + * @return PassPtr + */ +PassPtr gen_greedy_pauli_simp(double discount_rate, double depth_weight); + /** * 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 new file mode 100644 index 0000000000..a8c05934e9 --- /dev/null +++ b/tket/include/tket/Transformations/GreedyPauliOptimisation.hpp @@ -0,0 +1,216 @@ +// 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. + +#pragma once + +#include "Transform.hpp" +#include "tket/Circuit/Circuit.hpp" + +namespace tket { + +namespace Transforms { + +namespace GreedyPauliSimp { + +/** + * @brief Types of 2-qubit entangled Clifford gates + * + */ +enum class TQEType : unsigned { + XX, + XY, + XZ, + YX, + YY, + YZ, + ZX, + ZY, + ZZ, +}; + +/** + * @brief Local Clifford + * + */ +enum class LocalCliffordType { + H, + S, + V, +}; + +/** + * @brief Type for 2-qubit entangled Clifford gates + * + */ +using TQE = std::tuple; + +/** + * @brief A Pauli exponential described by its commutation relations + * with the rows in a reference Clifford tableau. + * We store the commutation relations using an n-dimensional + * vector with entries in {0,1,2,3}, where + * 0: commute with ith Z row and ith X row + * 1: commute with ith Z row and anti-commute with ith X row + * 2: anti-commute with ith Z row and commute with ith X row + * 3: anti-commute with ith Z row and anti-commute with ith X row + * We call such vector a support vector + */ +class PauliExpNode { + public: + /** + * @brief Construct a new PauliExpNode object. + * + * @param support_vec the support vector + * @param theta the rotation angle in half-turns + */ + PauliExpNode(std::vector support_vec, Expr theta); + + /** + * @brief Number of TQEs required to reduce the weight to 1 + * + * @return unsigned + */ + unsigned tqe_cost() const { return tqe_cost_; } + + /** + * @brief Number of TQEs would required to reduce the weight to 1 + * after the given TQE is applied + * + * @return unsigned + */ + int tqe_cost_increase(const TQE& tqe) const; + + /** + * @brief Update the support vector with a TQE gate + * + * @param tqe + */ + void update(const TQE& tqe); + + Expr theta() const { return theta_; }; + + /** + * @brief Return all possible TQE gates that will reduce the tqe cost by 1 + * + * @return std::vector> + */ + std::vector reduction_tqes() const; + + /** + * @brief Return the index and value of the first support + * + * @return std::pair + */ + std::pair first_support() const; + + private: + std::vector support_vec_; + Expr theta_; + unsigned tqe_cost_; +}; + +/** + * @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 + */ +class TableauRowNode { + public: + /** + * @brief Construct a new TableauRowNode object. + * + * @param support_vec the support vector + */ + TableauRowNode(std::vector support_vec); + + /** + * @brief Number of TQEs required to reduce the weight to 1 + * + * @return unsigned + */ + unsigned tqe_cost() const { return tqe_cost_; }; + + /** + * @brief Number of TQEs would required to reduce the weight to 1 + * after the given TQE is applied + * + * @return unsigned + */ + int tqe_cost_increase(const TQE& tqe) const; + + /** + * @brief Update the support vector with a TQE gate + * + * @param tqe + */ + void update(const TQE& tqe); + + /** + * @brief Return all possible TQE gates that will reduce the tqe cost + * + * @return std::vector> + */ + std::vector reduction_tqes() const; + + /** + * @brief Return the index and value of the first support + */ + std::pair first_support() const; + + private: + std::vector support_vec_; + unsigned n_weaks_; + unsigned n_strongs_; + unsigned tqe_cost_; +}; + +/** + * @brief The commutation relation between a TableauRowNode (p0,p1) and the ith + * row of the reference Tableau can be further classified as Strong, Weak or + * No-support. + */ +enum class SupportType : unsigned { + Strong, + Weak, + No, +}; + +/** + * @brief Given a circuit consists of PauliExpBoxes followed by clifford gates, + * 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. + * + * @param circ + * @param discount_rate + * @param depth_weight + * @return Circuit + */ +Circuit greedy_pauli_graph_synthesis( + const Circuit& circ, double discount_rate = 0.7, double depth_weight = 0.3); +} // namespace GreedyPauliSimp + +Transform greedy_pauli_optimisation( + double discount_rate = 0.7, double depth_weight = 0.3); + +} // namespace Transforms + +} // namespace tket diff --git a/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp b/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp new file mode 100644 index 0000000000..2445513636 --- /dev/null +++ b/tket/include/tket/Transformations/GreedyPauliOptimisationLookupTables.hpp @@ -0,0 +1,1569 @@ +// 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. + +#pragma once + +#include "GreedyPauliOptimisation.hpp" + +namespace tket { + +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_pair { + size_t operator()(const std::pair& pair) const { + return pair.first * 100 + pair.second; + } +}; + +/** + * @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 + */ +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}}; + +/** + * @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}}, +}; + +/** + * @brief Transform a pair of entries in a singlet support vector using a TQE + */ +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}}}; + +/** + * @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 + */ +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}}}; + +/** + * @brief Transform a pair of entries in a factor support vector using a TQE + */ +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}}}; + +/** + * @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) + */ +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}}}; + +/** + * @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 + */ +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}}}; + +/** + * @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 + */ +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}}}; + +} // namespace GreedyPauliSimp + +} // namespace Transforms + +} // namespace tket diff --git a/tket/src/Predicates/CompilerPass.cpp b/tket/src/Predicates/CompilerPass.cpp index e6d3522f9d..6a709ab9ed 100644 --- a/tket/src/Predicates/CompilerPass.cpp +++ b/tket/src/Predicates/CompilerPass.cpp @@ -487,6 +487,10 @@ void from_json(const nlohmann::json& j, PassPtr& pp) { content.at("pauli_synth_strat").get(); CXConfigType cxc = content.at("cx_config").get(); pp = gen_pauli_exponentials(pss, cxc); + } 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); } 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 d75f48be49..2467796bcc 100644 --- a/tket/src/Predicates/PassGenerators.cpp +++ b/tket/src/Predicates/PassGenerators.cpp @@ -36,6 +36,7 @@ #include "tket/Transformations/CliffordResynthesis.hpp" #include "tket/Transformations/ContextualReduction.hpp" #include "tket/Transformations/Decomposition.hpp" +#include "tket/Transformations/GreedyPauliOptimisation.hpp" #include "tket/Transformations/OptimisationPass.hpp" #include "tket/Transformations/PauliOptimisation.hpp" #include "tket/Transformations/Rebase.hpp" @@ -811,6 +812,39 @@ 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}; + 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)}; + PredicateClassGuarantees g_postcons = { + {typeid(ConnectivityPredicate), Guarantee::Clear}, + {typeid(NoWireSwapsPredicate), Guarantee::Clear}}; + PostConditions postcon{{}, g_postcons, Guarantee::Preserve}; + + // record pass config + nlohmann::json j; + j["name"] = "GreedyPauliSimp"; + j["discount_rate"] = discount_rate; + j["depth_weight"] = depth_weight; + + return std::make_shared(precons, t, postcon, j); +} + PassPtr gen_special_UCC_synthesis( Transforms::PauliSynthStrat strat, CXConfigType cx_config) { Transform t = Transforms::special_UCC_synthesis(strat, cx_config); diff --git a/tket/src/Transformations/GreedyPauliOptimisation.cpp b/tket/src/Transformations/GreedyPauliOptimisation.cpp new file mode 100644 index 0000000000..2fcc62a7c6 --- /dev/null +++ b/tket/src/Transformations/GreedyPauliOptimisation.cpp @@ -0,0 +1,947 @@ +// Copyright 2019-2024 Cambridge Quantum Computing +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tket/Transformations/GreedyPauliOptimisation.hpp" + +#include + +#include "tket/Circuit/PauliExpBoxes.hpp" +#include "tket/Converters/Converters.hpp" +#include "tket/OpType/OpType.hpp" +#include "tket/OpType/OpTypeInfo.hpp" +#include "tket/Ops/Op.hpp" +#include "tket/PauliGraph/PauliGraph.hpp" +#include "tket/Transformations/CliffordOptimisation.hpp" +#include "tket/Transformations/GreedyPauliOptimisationLookupTables.hpp" +#include "tket/Transformations/Transform.hpp" + +namespace tket { + +namespace Transforms { + +namespace GreedyPauliSimp { + +static void apply_tqe_to_circ(const TQE& tqe, Circuit& circ) { + auto [gate_type, a, b] = tqe; + switch (gate_type) { + case TQEType::XX: + circ.add_op(OpType::H, {a}); + circ.add_op(OpType::CX, {a, b}); + circ.add_op(OpType::H, {a}); + break; + case TQEType::XY: + circ.add_op(OpType::H, {a}); + circ.add_op(OpType::CY, {a, b}); + circ.add_op(OpType::H, {a}); + break; + case TQEType::XZ: + circ.add_op(OpType::CX, {b, a}); + break; + case TQEType::YX: + circ.add_op(OpType::H, {b}); + circ.add_op(OpType::CY, {b, a}); + circ.add_op(OpType::H, {b}); + break; + case TQEType::YY: + circ.add_op(OpType::V, {a}); + circ.add_op(OpType::CY, {a, b}); + circ.add_op(OpType::Vdg, {a}); + break; + case TQEType::YZ: + circ.add_op(OpType::CY, {b, a}); + break; + case TQEType::ZX: + circ.add_op(OpType::CX, {a, b}); + break; + case TQEType::ZY: + circ.add_op(OpType::CY, {a, b}); + break; + case TQEType::ZZ: + circ.add_op(OpType::CZ, {a, b}); + break; + } +} + +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) { + double cost = 0; + for (const unsigned& index : remaining_indices) { + cost += rows[index].tqe_cost_increase(tqe); + } + return cost; +} + +// return the weighted sum of the cost increases on remaining nodes +// we discount the weight after each set +static double default_pauliexp_tqe_cost( + const double discount_rate, + const std::vector>& rotation_sets, + const std::vector& rows, const TQE& tqe) { + double discount = 1 / (1 + discount_rate); + double weight = 1; + double exp_cost = 0; + double tab_cost = 0; + for (const std::vector& rotation_set : rotation_sets) { + for (const PauliExpNode& node : rotation_set) { + exp_cost += weight * node.tqe_cost_increase(tqe); + } + weight *= discount; + } + for (const TableauRowNode& node : rows) { + tab_cost += weight * node.tqe_cost_increase(tqe); + } + 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( + const std::map>& tqe_candidates_cost, + const std::vector& weights) { + TKET_ASSERT(tqe_candidates_cost.size() > 0); + size_t n_costs = tqe_candidates_cost.begin()->second.size(); + TKET_ASSERT(n_costs == weights.size()); + // for each cost type, store its min and max + std::vector mins = tqe_candidates_cost.begin()->second; + std::vector maxs = tqe_candidates_cost.begin()->second; + 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++) { + if (mins[cost_index] != maxs[cost_index]) { + valid_indices.push_back(cost_index); + } + } + // 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; + } + // 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; + for (; it != tqe_candidates_cost.end(); it++) { + if (it->second[valid_indices[0]] < min_cost) { + min_tqe = it->first; + min_cost = it->second[valid_indices[0]]; + } + } + return min_tqe; + } + // find the tqe with the minimum normalised cost + auto it = tqe_candidates_cost.begin(); + double min_cost = 0; + TQE min_tqe = it->first; + // initialise min_cost + for (const auto& cost_index : valid_indices) { + min_cost += weights[cost_index] * + (it->second[cost_index] - mins[cost_index]) / + (maxs[cost_index] - mins[cost_index]); + } + it++; + // iterate all tqes + for (; it != tqe_candidates_cost.end(); it++) { + double cost = 0; + for (const auto& cost_index : valid_indices) { + cost += weights[cost_index] * + (it->second[cost_index] - mins[cost_index]) / + (maxs[cost_index] - mins[cost_index]); + } + if (cost < min_cost) { + min_cost = cost; + min_tqe = 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}); +} + +// simple struct that tracks the depth on each qubit +struct DepthTracker { + std::vector qubit_depth; + unsigned max_depth; + DepthTracker(unsigned n) : qubit_depth(n, 0), max_depth(0) {}; + + unsigned gate_depth(unsigned a, unsigned b) const { + return std::max(qubit_depth[a], qubit_depth[b]) + 1; + }; + void add_1q_gate(unsigned a) { + qubit_depth[a]++; + if (qubit_depth[a] > max_depth) { + max_depth = qubit_depth[a]; + } + }; + void add_2q_gate(unsigned a, unsigned b) { + unsigned new_gate_depth = gate_depth(a, b); + qubit_depth[a] = new_gate_depth; + qubit_depth[b] = new_gate_depth; + if (new_gate_depth > max_depth) { + max_depth = new_gate_depth; + } + }; +}; + +/** + * @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 + */ +static void tableau_row_nodes_synthesis( + std::vector& rows, UnitaryRevTableau& tab, Circuit& circ, + double depth_weight, DepthTracker& depth_tracker) { + // only consider nodes with a non-zero cost + std::vector remaining_indices; + for (unsigned i = 0; i < rows.size(); i++) { + if (rows[i].tqe_cost() > 0) { + 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(); + for (unsigned i = 1; i < remaining_indices.size(); i++) { + 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) { + min_nodes_indices = {remaining_indices[i]}; + min_cost = node_cost; + } + } + // for each node with min cost, find the list of tqe gates that can reduce + // its cost + 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(); + tqe_candidates.insert( + node_reducing_tqes.begin(), node_reducing_tqes.end()); + } + // 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) { + 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)))}}); + } + TKET_ASSERT(tqe_candidates_cost.size() > 0); + // select the best one + TQE selected_tqe = select_tableau_tqe(tqe_candidates_cost, depth_weight); + // apply TQE + apply_tqe_to_circ(selected_tqe, circ); + apply_tqe_to_tableau(selected_tqe, tab); + // update depth tracker + depth_tracker.add_2q_gate( + std::get<1>(selected_tqe), std::get<2>(selected_tqe)); + // remove finished nodes + for (unsigned i = remaining_indices.size(); i-- > 0;) { + unsigned node_index = remaining_indices[i]; + rows[node_index].update(selected_tqe); + if (rows[node_index].tqe_cost() == 0) { + remaining_indices.erase(remaining_indices.begin() + i); + } + } + } + tableau_cleanup(rows, tab, circ); +} + +/** + * @brief Given a vector of sets of PauliExpNode, 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; + } + 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}); + } + 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}); + } + 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}); + } + break; + } + 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; + } + 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) { + 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]; + // get nodes with min cost + std::vector min_nodes_indices = {0}; + 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(); + if (node_cost == min_cost) { + min_nodes_indices.push_back(i); + } else if (node_cost < min_cost) { + min_nodes_indices = {i}; + min_cost = node_cost; + } + } + std::set tqe_candidates; + for (const unsigned& index : min_nodes_indices) { + std::vector node_reducing_tqes = first_set[index].reduction_tqes(); + tqe_candidates.insert( + node_reducing_tqes.begin(), node_reducing_tqes.end()); + } + // for each tqe we compute costs which might subject to normalisation + std::map> tqe_candidates_cost; + for (const TQE& tqe : tqe_candidates) { + 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)))}}); + } + // 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); + } + } + 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]}); + } + // 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); + } + } + 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_graph_synthesis( + const Circuit& circ, double discount_rate, double depth_weight) { + // c is the circuit we are trying to build + Circuit c(circ.all_qubits(), circ.all_bits()); + std::optional name = circ.get_name(); + if (name != std::nullopt) { + c.set_name(name.value()); + } + c.add_phase(circ.get_phase()); + unit_map_t unit_map = c.flatten_registers(); + Circuit measure_circ(c.n_qubits(), c.n_bits()); + Circuit cliff(c.n_qubits()); + + // circuit used to iterate the original commands with flattened registers + Circuit circ_flat(circ); + circ_flat.flatten_registers(); + std::vector commands = circ_flat.get_commands(); + // extract the final clifford and the measurement circuits + for (const Command& cmd : commands) { + OpType optype = cmd.get_op_ptr()->get_type(); + switch (optype) { + case OpType::Measure: { + measure_circ.add_op(OpType::Measure, cmd.get_args()); + break; + } + default: { + if (optype == OpType::PauliExpBox || + optype == OpType::PauliExpPairBox || + optype == OpType::PauliExpCommutingSetBox) + break; + TKET_ASSERT(is_clifford_type(optype) && is_gate_type(optype)); + cliff.add_op(optype, cmd.get_args()); + } + } + } + std::vector> rotation_sets; + 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; + } + } + // add identity TableauRowNodes + for (unsigned i = 0; i < n_qubits; i++) { + std::vector support_vec; + // identity rows + std::map p; + std::map q; + for (unsigned j = 0; j < n_qubits; j++) { + if (j == i) { + p.insert({Qubit(j), Pauli::Z}); + q.insert({Qubit(j), Pauli::X}); + } else { + p.insert({Qubit(j), Pauli::I}); + q.insert({Qubit(j), Pauli::I}); + } + } + SpPauliStabiliser stab_p(p); + SpPauliStabiliser stab_q(q); + for (unsigned row_index = 0; row_index < n_qubits; row_index++) { + SpPauliStabiliser zrow = tab.get_zrow(Qubit(row_index)); + SpPauliStabiliser xrow = tab.get_xrow(Qubit(row_index)); + bool lpx = !xrow.commutes_with(stab_p); + bool lpz = !zrow.commutes_with(stab_p); + bool lqx = !xrow.commutes_with(stab_q); + bool lqz = !zrow.commutes_with(stab_q); + support_vec.push_back(8 * lpx + 4 * lpz + 2 * lqx + lqz); + } + rows.push_back(TableauRowNode(support_vec)); + } + DepthTracker depth_tracker(n_qubits); + // synthesise Pauli exps + pauli_exps_synthesis( + rotation_sets, rows, tab, c, discount_rate, depth_weight, depth_tracker); + // 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; +} + +} // namespace GreedyPauliSimp + +Transform greedy_pauli_optimisation(double discount_rate, double depth_weight) { + return Transform([discount_rate, depth_weight](Circuit& circ) { + synthesise_pauli_graph(PauliSynthStrat::Sets, CXConfigType::Snake) + .apply(circ); + circ = GreedyPauliSimp::greedy_pauli_graph_synthesis( + circ, discount_rate, depth_weight); + singleq_clifford_sweep().apply(circ); + return true; + }); +} + +} // namespace Transforms + +} // namespace tket diff --git a/tket/test/CMakeLists.txt b/tket/test/CMakeLists.txt index 65cf8824ef..ecfa0dd601 100644 --- a/tket/test/CMakeLists.txt +++ b/tket/test/CMakeLists.txt @@ -169,6 +169,7 @@ add_executable(test-tket src/test_SteinerForest.cpp src/test_wasm.cpp src/test_RoundAngles.cpp + src/test_GreedyPauli.cpp src/ZX/test_ZXDiagram.cpp src/ZX/test_ZXAxioms.cpp src/ZX/test_ZXSimp.cpp diff --git a/tket/test/src/test_GreedyPauli.cpp b/tket/test/src/test_GreedyPauli.cpp new file mode 100644 index 0000000000..2cca0e178a --- /dev/null +++ b/tket/test/src/test_GreedyPauli.cpp @@ -0,0 +1,294 @@ +// 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 +#include + +#include "testutil.hpp" +#include "tket/Circuit/Circuit.hpp" +#include "tket/Circuit/PauliExpBoxes.hpp" +#include "tket/Circuit/Simulation/CircuitSimulator.hpp" +#include "tket/Gate/SymTable.hpp" +#include "tket/PauliGraph/PauliGraph.hpp" +#include "tket/Predicates/PassGenerators.hpp" +#include "tket/Transformations/Decomposition.hpp" +#include "tket/Transformations/GreedyPauliOptimisation.hpp" +#include "tket/Utils/Expression.hpp" + +namespace tket { +namespace test_GreedyPauliSimp { + +SCENARIO("Unsupported circuits") { + GIVEN("Circuit with mid-circ measurements") { + Circuit circ(2, 2); + circ.add_op(OpType::H, {0}); + circ.add_op(OpType::Rx, 0.5, {1}); + circ.add_op(OpType::Measure, {0, 0}); + circ.add_op(OpType::CX, {0, 1}); + REQUIRE_THROWS_MATCHES( + Transforms::greedy_pauli_optimisation().apply(circ), + MidCircuitMeasurementNotAllowed, + MessageContains( + "PauliGraph does not support mid-circuit measurements")); + } + GIVEN("Circuit with resets") { + Circuit circ(1); + circ.add_op(OpType::H, {0}); + 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); + REQUIRE_THROWS_MATCHES( + Transforms::greedy_pauli_optimisation().apply(circ), BadOpType, + MessageContains( + "Can only make a PauliGraph from a circuit of basic gates")); + } +} +SCENARIO("Clifford synthesis") { + GIVEN("Empty circuit") { + Circuit circ(3); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); + REQUIRE(test_unitary_comparison(circ, d, true)); + } + GIVEN("1Q Simple Clifford") { + Circuit circ(1); + circ.add_op(OpType::Sdg, {0}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); + REQUIRE(test_unitary_comparison(circ, d, true)); + } + GIVEN("2Q Simple Clifford") { + Circuit circ(2); + circ.add_op(OpType::Y, {0}); + circ.add_op(OpType::Vdg, {1}); + circ.add_op(OpType::CX, {0, 1}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); + REQUIRE(test_unitary_comparison(circ, d, true)); + } + GIVEN("3Q Simple Clifford") { + Circuit circ(3); + circ.add_op(OpType::Y, {0}); + circ.add_op(OpType::Sdg, {2}); + circ.add_op(OpType::H, {1}); + circ.add_op(OpType::CX, {1, 2}); + circ.add_op(OpType::CZ, {0, 2}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); + REQUIRE(test_unitary_comparison(circ, d, true)); + } + GIVEN("5Q Simple Clifford") { + Circuit circ(5); + circ.add_op(OpType::H, {0}); + circ.add_op(OpType::S, {1}); + circ.add_op(OpType::CX, {2, 3}); + circ.add_op(OpType::CZ, {1, 2}); + circ.add_op(OpType::V, {1}); + circ.add_op(OpType::X, {3}); + circ.add_op(OpType::CZ, {0, 4}); + circ.add_op(OpType::CY, {0, 1}); + circ.add_op(OpType::H, {2}); + circ.add_op(OpType::Z, {2}); + circ.add_op(OpType::Y, {4}); + circ.add_op(OpType::CY, {3, 4}); + circ.add_op(OpType::CX, {2, 0}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); + REQUIRE(test_unitary_comparison(circ, d, true)); + } + GIVEN("Clifford with swaps") { + 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::SWAP, {2, 3}); + circ.add_op(OpType::H, {3}); + circ.add_op(OpType::CZ, {1, 3}); + circ.add_op(OpType::H, {2}); + circ.add_op(OpType::X, {0}); + circ.add_op(OpType::SWAP, {0, 1}); + circ.add_op(OpType::Z, {2}); + circ.add_op(OpType::SWAP, {3, 1}); + circ.add_op(OpType::CY, {0, 2}); + circ.add_op(OpType::SWAP, {1, 2}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); + REQUIRE(test_unitary_comparison(circ, d, true)); + } +} +SCENARIO("Complete synthesis") { + GIVEN("1Q Simple Circuit") { + Circuit circ(1); + circ.add_op(OpType::Sdg, {0}); + circ.add_op(OpType::Rx, 0.3, {0}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); + REQUIRE(test_unitary_comparison(circ, d, true)); + } + GIVEN("Symbolic Circuit") { + Circuit circ(2); + auto a = SymTable::fresh_symbol("a"); + auto b = SymTable::fresh_symbol("b"); + auto ea = Expr(a); + auto eb = Expr(b); + circ.add_op(OpType::Sdg, {0}); + circ.add_op(OpType::H, {0}); + circ.add_op(OpType::Ry, eb, {1}); + circ.add_op(OpType::CX, {0, 1}); + circ.add_op(OpType::Rx, ea, {0}); + Circuit d(circ); + symbol_map_t symbol_map; + symbol_map[a] = Expr(0.5); + symbol_map[b] = Expr(0.7); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); + circ.symbol_substitution(symbol_map); + d.symbol_substitution(symbol_map); + REQUIRE(test_unitary_comparison(circ, d, true)); + } + GIVEN("4Q PauliExp Circuit") { + Circuit circ(4); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::X, Pauli::X}, 0.3)), {0, 1}); + circ.add_box( + PauliExpBox(SymPauliTensor({Pauli::Z, Pauli::Y}, -0.1)), {2, 3}); + circ.add_box( + PauliExpPairBox( + SymPauliTensor({Pauli::X, Pauli::Z}, 1.0), + SymPauliTensor({Pauli::Z, Pauli::X}, 0.4)), + {0, 2}); + 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_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 d = Transforms::GreedyPauliSimp::greedy_pauli_graph_synthesis(circ); + REQUIRE(test_unitary_comparison(circ, d, true)); + } + GIVEN("Arbitrary Circuit") { + Circuit circ(5); + circ.add_op(OpType::X, {0}); + circ.add_op(OpType::SWAP, {1, 2}); + circ.add_op(OpType::Rz, 0.1, {1}); + circ.add_op(OpType::CX, {0, 2}); + circ.add_op(OpType::SWAP, {2, 3}); + circ.add_op(OpType::Ry, 0.2, {3}); + circ.add_op(OpType::Ry, 0.15, {2}); + circ.add_op(OpType::H, {3}); + circ.add_op(OpType::Rz, 0.3, {4}); + circ.add_op(OpType::CZ, {1, 4}); + circ.add_op(OpType::ZZMax, {1, 2}); + circ.add_op(OpType::T, {4}); + circ.add_op(OpType::X, {0}); + circ.add_op(OpType::ZZPhase, 0.7, {3, 2}); + circ.add_op(OpType::T, {3}); + circ.add_op(OpType::SWAP, {0, 1}); + circ.add_op(OpType::Z, {2}); + circ.add_op(OpType::SWAP, {3, 1}); + circ.add_op(OpType::CX, {1, 4}); + circ.add_op(OpType::T, {0}); + circ.add_op(OpType::CY, {0, 2}); + circ.add_op(OpType::SWAP, {1, 2}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); + REQUIRE(test_unitary_comparison(circ, d, true)); + } + GIVEN("Circuit with trivial Pauli exps") { + Circuit circ(4); + circ.add_box(PauliExpBox(SymPauliTensor({Pauli::X, Pauli::X}, 2)), {0, 1}); + circ.add_box( + PauliExpPairBox( + SymPauliTensor({Pauli::I, Pauli::I}, 1.2), + SymPauliTensor({Pauli::Z, Pauli::X}, -2)), + {0, 2}); + circ.add_box( + PauliExpCommutingSetBox({ + {{Pauli::I, Pauli::Y, Pauli::I}, 0}, + {{Pauli::X, Pauli::Y, Pauli::Z}, 0}, + {{Pauli::I, Pauli::I, Pauli::I}, 0.5}, + }), + {1, 2, 3}); + Circuit d = Transforms::GreedyPauliSimp::greedy_pauli_graph_synthesis(circ); + REQUIRE(test_unitary_comparison(circ, d, true)); + REQUIRE(d.n_gates() == 0); + } + GIVEN("Circuit with non-default UnitIDs") { + Circuit circ; + register_t reg_a = circ.add_q_register("a", 2); + register_t reg_b = circ.add_q_register("b", 2); + circ.add_op(OpType::CX, {reg_a[0], reg_b[1]}); + circ.add_op(OpType::SWAP, {reg_b[0], reg_a[1]}); + circ.add_op(OpType::Rz, 0.3, {reg_a[1]}); + circ.add_op(OpType::CX, {reg_a[1], reg_b[1]}); + circ.add_op(OpType::Ry, 0.2, {reg_b[1]}); + circ.add_op(OpType::H, {reg_b[1]}); + circ.add_op(OpType::Rz, 0.3, {reg_a[0]}); + circ.add_op(OpType::CY, {reg_a[0], reg_a[1]}); + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); + REQUIRE(test_unitary_comparison(circ, d, true)); + } + GIVEN("Circuit with measurements") { + Circuit circ(4, 4); + circ.add_op(OpType::X, {0}); + circ.add_op(OpType::SWAP, {1, 2}); + circ.add_op(OpType::Rz, 0.1, {1}); + circ.add_op(OpType::CX, {0, 2}); + circ.add_op(OpType::SWAP, {2, 3}); + circ.add_op(OpType::Ry, 0.2, {3}); + circ.add_op(OpType::Ry, 0.15, {2}); + circ.add_op(OpType::H, {3}); + circ.add_op(OpType::Rz, 0.3, {0}); + circ.add_op(OpType::CZ, {0, 1}); + circ.add_op(OpType::ZZMax, {1, 2}); + // For circuit d, we add measurement after synthesis + Circuit d(circ); + REQUIRE(Transforms::greedy_pauli_optimisation().apply(d)); + REQUIRE(d.has_implicit_wireswaps()); + for (unsigned i = 0; i < 4; i++) { + d.add_op(OpType::Measure, {Qubit(i), Bit(i)}); + } + // For circuit g, we add measurement before synthesis + Circuit g(circ); + for (unsigned i = 0; i < 4; i++) { + g.add_op(OpType::Measure, {Qubit(i), Bit(i)}); + } + REQUIRE(Transforms::greedy_pauli_optimisation().apply(g)); + REQUIRE(d == g); + } +} +SCENARIO("Test GreedyPauliSimp pass construction") { + // test pass construction + GIVEN("A circuit") { + Circuit c(2); + c.add_op(OpType::CX, {0, 1}); + c.add_op(OpType::Rz, 0.5, {1}); + CompilationUnit cu(c); + CHECK(gen_greedy_pauli_simp(0.3, 0.5)->apply(cu)); + REQUIRE(test_unitary_comparison(c, cu.get_circ_ref(), true)); + } +} +} // namespace test_GreedyPauliSimp +} // namespace tket diff --git a/tket/test/src/test_json.cpp b/tket/test/src/test_json.cpp index be596579fb..177ab0f177 100644 --- a/tket/test/src/test_json.cpp +++ b/tket/test/src/test_json.cpp @@ -964,6 +964,7 @@ SCENARIO("Test compiler pass serializations") { Transforms::AllowClassical::No, Transforms::CreateAllQubits::Yes, std::make_shared(CircPool::X()))) COMPPASSJSONTEST(PlacementPass, gen_placement_pass(place)) + COMPPASSJSONTEST(GreedyPauliSimp, gen_greedy_pauli_simp(0.3, 0.18)) // TKET-1419 COMPPASSJSONTEST(NoiseAwarePlacement, gen_placement_pass(na_place)) COMPPASSJSONTEST(NaivePlacementPass, gen_naive_placement_pass(arc)) From 6969d48cb501546f612c598501ffb7a95295963e Mon Sep 17 00:00:00 2001 From: cqc-melf <70640934+cqc-melf@users.noreply.github.com> Date: Wed, 15 May 2024 14:22:28 +0100 Subject: [PATCH 23/33] Update extensions.rst (#1394) --- pytket/docs/extensions.rst | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/pytket/docs/extensions.rst b/pytket/docs/extensions.rst index 9f305762a9..ab9ef929d2 100644 --- a/pytket/docs/extensions.rst +++ b/pytket/docs/extensions.rst @@ -2,7 +2,7 @@ pytket extensions ================= The pytket extensions are separate python modules which allow pytket to interface with backends from a range of providers including quantum devices from Quantinuum and IBM. -In pytket a ``Backend`` represents a connection to a QPU (Quantum Processing Unit) or simulator for processing quantum circuits. One can also access additional quantum devices and simulators via the cloud through the extensions for `Azure `_ and `Braket `_ . +In pytket a ``Backend`` represents a connection to a QPU (Quantum Processing Unit) or simulator for processing quantum circuits. One can also access additional quantum devices and simulators via the cloud through the extensions for `Braket `_ . Additionally, the extensions allow pytket to cross-compile circuits from different quantum computing libraries with the extensions for `qiskit `_, `cirq `_ and `pennylane `_ . This enables pytket's compilation features to be used in conjunction with other software tools. @@ -48,9 +48,6 @@ QPUs Cloud Access ------------ -`AzureBackend `_ -- Backend for running circuits remotely using Azure Quantum devices and simulators. - `BraketBackend `_ - Interface to Amazon Braket service. @@ -119,13 +116,6 @@ Other `QulacsBackend `_ - Backend for running simulations of variational quantum circuits on the Qulacs simulator. -`QsharpSimulatorBackend `_ -- Backend for simulating a circuit using the QDK. - -`QsharpToffoliSimulatorBackend `_ -- Backend for simulating a Toffoli circuit using the QDK. - - .. toctree:: :caption: Extensions: :maxdepth: 0 @@ -141,7 +131,6 @@ Other pytket-pyzx pytket-qir pytket-qiskit - pytket-qsharp pytket-quantinuum pytket-cutensornet pytket-qulacs @@ -157,4 +146,4 @@ Other .. _AerUnitaryBackend: https://tket.quantinuum.com/extensions/pytket-qiskit/api.html#pytket.extensions.qiskit.AerUnitaryBackend .. _CirqDensityMatrixSampleBackend: https://tket.quantinuum.com/extensions/pytket-cirq/api.html#pytket.extensions.cirq.CirqDensityMatrixSampleBackend .. _SimplexBackend: https://tket.quantinuum.com/extensions/pytket-pysimplex/api.html#pytket.extensions.pysimplex.SimplexBackend -.. _QulacsBackend: https://tket.quantinuum.com/extensions/pytket-qulacs/api.html#pytket.extensions.qulacs.QulacsBackend \ No newline at end of file +.. _QulacsBackend: https://tket.quantinuum.com/extensions/pytket-qulacs/api.html#pytket.extensions.qulacs.QulacsBackend From 4df750b9894e193ee99faa88b058fc6d17e8e5e5 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Wed, 15 May 2024 16:06:20 +0100 Subject: [PATCH 24/33] Allow constant `BitLogicExp` as condition (#1397) --- pytket/docs/changelog.rst | 2 ++ pytket/pytket/circuit/logic_exp.py | 39 ++++++++++++++++++++++++++++++ pytket/tests/classical_test.py | 19 +++++++++------ pytket/tests/qasm_test.py | 23 ++++++++++++++++++ 4 files changed, 76 insertions(+), 7 deletions(-) diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index a238901f10..9606ea010b 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -10,6 +10,8 @@ Features: * Add two new status values for circuits on backends: "CANCELLING" and "RETRYING". * Use `lark` package instead of deprecated `lark-parser`. * Add ``GreedyPauliSimp`` optimisation pass. +* Add ``BitWiseOp.ZERO`` and ``BitWiseOp.ONE`` to allow construction of constant + conditional expressions. Fixes: diff --git a/pytket/pytket/circuit/logic_exp.py b/pytket/pytket/circuit/logic_exp.py index e2b288a3e6..3c74473471 100644 --- a/pytket/pytket/circuit/logic_exp.py +++ b/pytket/pytket/circuit/logic_exp.py @@ -51,6 +51,8 @@ class BitWiseOp(Enum): EQ = "==" NEQ = "!=" NOT = "~" + ZERO = "0" + ONE = "1" class RegWiseOp(Enum): @@ -110,6 +112,10 @@ def factory(cls, op: Ops) -> Type["LogicExp"]: return BitEq if op == BitWiseOp.NEQ: return BitNeq + if op == BitWiseOp.ZERO: + return BitZero + if op == BitWiseOp.ONE: + return BitOne if op == RegWiseOp.AND: return RegAnd if op == RegWiseOp.OR: @@ -368,6 +374,13 @@ def __str__(self) -> str: return f"({self.op.value} {self.args[0]})" +class NullaryOp(LogicExp): + """Expression for operation on no arguments (i.e. constant).""" + + def __str__(self) -> str: + return f"({self.op.value})" + + class And(BinaryOp): @staticmethod def _const_eval(args: List[Constant]) -> Constant: @@ -426,6 +439,26 @@ def _const_eval(args: List[Constant]) -> Constant: return 1 - args[0] +class BitZero(NullaryOp, BitLogicExp): + def __init__(self) -> None: + self.op = BitWiseOp.ZERO + self.args = [] + + @staticmethod + def _const_eval(args: List[Constant]) -> Constant: + return 0 + + +class BitOne(NullaryOp, BitLogicExp): + def __init__(self) -> None: + self.op = BitWiseOp.ONE + self.args = [] + + @staticmethod + def _const_eval(args: List[Constant]) -> Constant: + return 1 + + class RegAnd(And, RegLogicExp): def __init__(self, arg1: RegArgType, arg2: RegArgType) -> None: self.op = RegWiseOp.AND @@ -647,6 +680,12 @@ def create_bit_logic_exp(op: BitWiseOp, args: Sequence[BitArgType]) -> BitLogicE if op == BitWiseOp.NEQ: assert len(args) == 2 return BitNeq(args[0], args[1]) + if op == BitWiseOp.ZERO: + assert len(args) == 0 + return BitZero() + if op == BitWiseOp.ONE: + assert len(args) == 0 + return BitOne() def create_reg_logic_exp(op: RegWiseOp, args: Sequence[RegArgType]) -> RegLogicExp: diff --git a/pytket/tests/classical_test.py b/pytket/tests/classical_test.py index 1c5cd4c81a..63bfad4cae 100644 --- a/pytket/tests/classical_test.py +++ b/pytket/tests/classical_test.py @@ -56,6 +56,7 @@ RegPow, RegWiseOp, UnaryOp, + NullaryOp, reg_eq, reg_geq, reg_gt, @@ -763,14 +764,16 @@ def primitive_bit_logic_exps( bits: SearchStrategy[Bit] = bits(), ) -> BitLogicExp: op = draw(ops) - args = [draw(bits)] + args = [] exp_type = LogicExp.factory(op) - if issubclass(exp_type, BinaryOp): - if issubclass(exp_type, PredicateExp): - const_compare = draw(binary_digits) - args.append(Bit(const_compare)) - else: - args.append(draw(bits)) + if not issubclass(exp_type, NullaryOp): + args.append(draw(bits)) + if issubclass(exp_type, BinaryOp): + if issubclass(exp_type, PredicateExp): + const_compare = draw(binary_digits) + args.append(Bit(const_compare)) + else: + args.append(draw(bits)) exp = create_bit_logic_exp(op, args) assert isinstance(exp, BitLogicExp) return exp @@ -798,6 +801,8 @@ def test_bit_exp(bit_exp: BitLogicExp, constants: Tuple[int, int]) -> None: BitWiseOp.NOT: operator.not_, BitWiseOp.EQ: operator.eq, BitWiseOp.NEQ: operator.ne, + BitWiseOp.ZERO: lambda: 0, + BitWiseOp.ONE: lambda: 1, } op_map = {key: overflow_wrapper(val, 2) for key, val in op_map.items()} eval_val = bit_exp.eval_vals() diff --git a/pytket/tests/qasm_test.py b/pytket/tests/qasm_test.py index 93e55912d1..e05c287374 100644 --- a/pytket/tests/qasm_test.py +++ b/pytket/tests/qasm_test.py @@ -37,6 +37,7 @@ CustomGate, ) from pytket.circuit.decompose_classical import DecomposeClassicalError +from pytket.circuit.logic_exp import BitWiseOp, create_bit_logic_exp from pytket.qasm import ( circuit_from_qasm, circuit_to_qasm, @@ -932,6 +933,28 @@ def test_conditional_nonstandard_gates() -> None: assert "if(c==1) zzmax q[0],q[1];" in qasm +def test_const_condition() -> None: + # https://github.com/CQCL/tket/issues/1383 + circ = Circuit(1, 1) + exp = create_bit_logic_exp(BitWiseOp.ONE, []) + circ.H(0, condition=exp) + circ.measure_all() + qasm = circuit_to_qasm_str(circ, header="hqslib1") + assert ( + qasm + == """OPENQASM 2.0; +include "hqslib1.inc"; + +qreg q[1]; +creg c[1]; +creg tk_SCRATCH_BIT[1]; +tk_SCRATCH_BIT[0] = (1); +if(tk_SCRATCH_BIT[0]==1) h q[0]; +measure q[0] -> c[0]; +""" + ) + + if __name__ == "__main__": test_qasm_correct() test_qasm_qubit() From bc8c11c40d57d9d1758a470d4428e53f8ecf593f Mon Sep 17 00:00:00 2001 From: michael <65613226+michaelontiveros@users.noreply.github.com> Date: Thu, 16 May 2024 02:07:04 -0600 Subject: [PATCH 25/33] Methods for GPI, GPI2, and AAMS gates. (#1395) --- pytket/binders/circuit/Circuit/add_op.cpp | 2 +- pytket/binders/circuit_library.cpp | 27 +++ pytket/binders/transform.cpp | 4 + pytket/conanfile.py | 2 +- pytket/docs/changelog.rst | 2 + pytket/pytket/_tket/circuit_library.pyi | 38 +++- pytket/pytket/_tket/transform.pyi | 5 + pytket/pytket/circuit_library/__init__.py | 9 + pytket/pytket/passes/auto_rebase.py | 3 + pytket/pytket/utils/symbolic.py | 45 ++++ pytket/tests/transform_test.py | 10 + tket/conanfile.py | 2 +- tket/include/tket/Circuit/CircPool.hpp | 27 +++ tket/include/tket/Transformations/Rebase.hpp | 4 + tket/src/Circuit/CircPool.cpp | 88 ++++++++ tket/src/Transformations/Rebase.cpp | 8 + tket/test/src/Circuit/test_CircPool.cpp | 210 +++++++++++++++++++ tket/test/src/Circuit/test_Symbolic.cpp | 52 +++++ 18 files changed, 534 insertions(+), 4 deletions(-) diff --git a/pytket/binders/circuit/Circuit/add_op.cpp b/pytket/binders/circuit/Circuit/add_op.cpp index c26e0d7614..20447d429d 100644 --- a/pytket/binders/circuit/Circuit/add_op.cpp +++ b/pytket/binders/circuit/Circuit/add_op.cpp @@ -1333,7 +1333,7 @@ void init_circuit_add_op(py::class_> &c) { [](Circuit *circ, const Expr &angle, unsigned qb, const py::kwargs &kwargs) { return add_gate_method_oneparam( - circ, OpType::GPI, angle, {qb}, kwargs); + circ, OpType::GPI2, angle, {qb}, kwargs); }, "Appends a GPI2 gate with a possibly symbolic angle " "(specified in half-turns)." diff --git a/pytket/binders/circuit_library.cpp b/pytket/binders/circuit_library.cpp index 0fcf6706c7..61a879f2ba 100644 --- a/pytket/binders/circuit_library.cpp +++ b/pytket/binders/circuit_library.cpp @@ -294,5 +294,32 @@ PYBIND11_MODULE(circuit_library, library_m) { library_m.def( "TK1_to_TK1", &CircPool::tk1_to_tk1, "A circuit of a single tk1 gate with given parameters"); + library_m.def( + "Rx_using_GPI", &CircPool::Rx_using_GPI, + "Equivalent to Rx, using GPI and GPI2 gates"); + library_m.def( + "Ry_using_GPI", &CircPool::Ry_using_GPI, + "Equivalent to Ry, using GPI and GPI2 gates"); + library_m.def( + "Rz_using_GPI", &CircPool::Rz_using_GPI, + "Equivalent to Rz, using GPI gates"); + library_m.def( + "XXPhase_using_AAMS", &CircPool::XXPhase_using_AAMS, + "Equivalent to XXPhase, using AAMS gates"); + library_m.def( + "YYPhase_using_AAMS", &CircPool::YYPhase_using_AAMS, + "Equivalent to YYPhase, using AAMS gates"); + library_m.def( + "ZZPhase_using_AAMS", &CircPool::ZZPhase_using_AAMS, + "Equivalent to ZZPhase, using AAMS, GPI and GPI2 gates"); + library_m.def( + "CX_using_AAMS", &CircPool::CX_using_AAMS, + "Equivalent to CX, using AAMS, GPI and GPI2 gates"); + library_m.def( + "TK1_using_GPI", &CircPool::TK1_using_GPI, + "Equivalent to TK1, using GPI and GPI2 gates"); + library_m.def( + "TK2_using_AAMS", &CircPool::TK2_using_AAMS, + "Equivalent to TK2, using AAMS, GPI and GPI2 gates"); } } // namespace tket diff --git a/pytket/binders/transform.cpp b/pytket/binders/transform.cpp index b912b4ebec..8c074c3a1e 100644 --- a/pytket/binders/transform.cpp +++ b/pytket/binders/transform.cpp @@ -152,6 +152,10 @@ PYBIND11_MODULE(transform, m) { "Rebase from any gate set into the gate set supported by " "ProjectQ (Rx, Ry, Rz, X, Y, Z, S, T, V, H, CX, CZ, CRz, " "SWAP).") + .def_static( + "RebaseToIonQ", &Transforms::rebase_ionq, + "Rebase from any gate set into the gate set supported by " + "IonQ (GPI, GPI2, AAMS).") .def_static( "DecomposeCCX", &Transforms::decomp_CCX, "Decomposes all 3-qubit Toffoli (CCX) gates into " diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 0fc08c2376..2dc87bfd8b 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.2.125@tket/stable") + self.requires("tket/1.2.126@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 9606ea010b..3140ed281d 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -12,6 +12,8 @@ Features: * Add ``GreedyPauliSimp`` optimisation pass. * Add ``BitWiseOp.ZERO`` and ``BitWiseOp.ONE`` to allow construction of constant conditional expressions. +* Add target gateset ``(GPI, GPI2, AAMS)`` to ``auto_rebase_pass``. +* Add ``RebaseToIonQ`` transform. Fixes: diff --git a/pytket/pytket/_tket/circuit_library.pyi b/pytket/pytket/_tket/circuit_library.pyi index 92d11c805b..66d86bf18a 100644 --- a/pytket/pytket/_tket/circuit_library.pyi +++ b/pytket/pytket/_tket/circuit_library.pyi @@ -2,7 +2,7 @@ from __future__ import annotations import pytket._tket.circuit import sympy import typing -__all__ = ['BRIDGE', 'BRIDGE_using_CX_0', 'BRIDGE_using_CX_1', 'C3X_normal_decomp', 'C4X_normal_decomp', 'CCX', 'CCX_modulo_phase_shift', 'CCX_normal_decomp', 'CH_using_CX', 'CRx_using_CX', 'CRx_using_TK2', 'CRy_using_CX', 'CRy_using_TK2', 'CRz_using_CX', 'CRz_using_TK2', 'CSWAP_using_CX', 'CSX_using_CX', 'CSXdg_using_CX', 'CS_using_CX', 'CSdg_using_CX', 'CU1_using_CX', 'CU1_using_TK2', 'CU3_using_CX', 'CV_using_CX', 'CVdg_using_CX', 'CX', 'CX_S_CX_reduced', 'CX_S_V_XC_reduced', 'CX_VS_CX_reduced', 'CX_V_CX_reduced', 'CX_V_S_XC_reduced', 'CX_XC_reduced', 'CX_using_ECR', 'CX_using_TK2', 'CX_using_XXPhase_0', 'CX_using_XXPhase_1', 'CX_using_ZZMax', 'CX_using_ZZPhase', 'CX_using_flipped_CX', 'CY_using_CX', 'CZ_using_CX', 'ECR_using_CX', 'ESWAP_using_CX', 'ESWAP_using_TK2', 'FSim_using_CX', 'FSim_using_TK2', 'H_CZ_H', 'ISWAP_using_CX', 'ISWAP_using_TK2', 'NPhasedX_using_PhasedX', 'PhasedISWAP_using_CX', 'PhasedISWAP_using_TK2', 'SWAP_using_CX_0', 'SWAP_using_CX_1', 'TK1_to_PhasedXRz', 'TK1_to_RzH', 'TK1_to_RzRx', 'TK1_to_RzSX', 'TK1_to_TK1', 'TK2_using_3xCX', 'TK2_using_CX', 'TK2_using_CX_and_swap', 'TK2_using_TK2_or_swap', 'TK2_using_ZZMax', 'TK2_using_ZZMax_and_swap', 'TK2_using_ZZPhase', 'TK2_using_ZZPhase_and_swap', 'TK2_using_normalised_TK2', 'X', 'X1_CX', 'XXPhase3_using_CX', 'XXPhase3_using_TK2', 'XXPhase_using_CX', 'XXPhase_using_TK2', 'YYPhase_using_CX', 'YYPhase_using_TK2', 'Z0_CX', 'ZZMax_using_CX', 'ZZPhase_using_CX', 'ZZPhase_using_TK2', 'approx_TK2_using_1xCX', 'approx_TK2_using_1xZZPhase', 'approx_TK2_using_2xCX', 'approx_TK2_using_2xZZPhase', 'ladder_down', 'ladder_down_2', 'ladder_up'] +__all__ = ['BRIDGE', 'BRIDGE_using_CX_0', 'BRIDGE_using_CX_1', 'C3X_normal_decomp', 'C4X_normal_decomp', 'CCX', 'CCX_modulo_phase_shift', 'CCX_normal_decomp', 'CH_using_CX', 'CRx_using_CX', 'CRx_using_TK2', 'CRy_using_CX', 'CRy_using_TK2', 'CRz_using_CX', 'CRz_using_TK2', 'CSWAP_using_CX', 'CSX_using_CX', 'CSXdg_using_CX', 'CS_using_CX', 'CSdg_using_CX', 'CU1_using_CX', 'CU1_using_TK2', 'CU3_using_CX', 'CV_using_CX', 'CVdg_using_CX', 'CX', 'CX_S_CX_reduced', 'CX_S_V_XC_reduced', 'CX_VS_CX_reduced', 'CX_V_CX_reduced', 'CX_V_S_XC_reduced', 'CX_XC_reduced', 'CX_using_AAMS', 'CX_using_ECR', 'CX_using_TK2', 'CX_using_XXPhase_0', 'CX_using_XXPhase_1', 'CX_using_ZZMax', 'CX_using_ZZPhase', 'CX_using_flipped_CX', 'CY_using_CX', 'CZ_using_CX', 'ECR_using_CX', 'ESWAP_using_CX', 'ESWAP_using_TK2', 'FSim_using_CX', 'FSim_using_TK2', 'H_CZ_H', 'ISWAP_using_CX', 'ISWAP_using_TK2', 'NPhasedX_using_PhasedX', 'PhasedISWAP_using_CX', 'PhasedISWAP_using_TK2', 'Rx_using_GPI', 'Ry_using_GPI', 'Rz_using_GPI', 'SWAP_using_CX_0', 'SWAP_using_CX_1', 'TK1_to_PhasedXRz', 'TK1_to_RzH', 'TK1_to_RzRx', 'TK1_to_RzSX', 'TK1_to_TK1', 'TK1_using_GPI', 'TK2_using_3xCX', 'TK2_using_AAMS', 'TK2_using_CX', 'TK2_using_CX_and_swap', 'TK2_using_TK2_or_swap', 'TK2_using_ZZMax', 'TK2_using_ZZMax_and_swap', 'TK2_using_ZZPhase', 'TK2_using_ZZPhase_and_swap', 'TK2_using_normalised_TK2', 'X', 'X1_CX', 'XXPhase3_using_CX', 'XXPhase3_using_TK2', 'XXPhase_using_AAMS', 'XXPhase_using_CX', 'XXPhase_using_TK2', 'YYPhase_using_AAMS', 'YYPhase_using_CX', 'YYPhase_using_TK2', 'Z0_CX', 'ZZMax_using_CX', 'ZZPhase_using_AAMS', 'ZZPhase_using_CX', 'ZZPhase_using_TK2', 'approx_TK2_using_1xCX', 'approx_TK2_using_1xZZPhase', 'approx_TK2_using_2xCX', 'approx_TK2_using_2xZZPhase', 'ladder_down', 'ladder_down_2', 'ladder_up'] def BRIDGE() -> pytket._tket.circuit.Circuit: """ Just a BRIDGE[0,1,2] gate @@ -131,6 +131,10 @@ def CX_XC_reduced() -> pytket._tket.circuit.Circuit: """ CX-reduced form of CX/XC """ +def CX_using_AAMS() -> pytket._tket.circuit.Circuit: + """ + Equivalent to CX, using AAMS, GPI and GPI2 gates + """ def CX_using_ECR() -> pytket._tket.circuit.Circuit: """ Equivalent to CX, using only ECR, Rx and U3 gates @@ -211,6 +215,18 @@ def PhasedISWAP_using_TK2(arg0: sympy.Expr | float, arg1: sympy.Expr | float) -> """ Equivalent to PhasedISWAP, using a TK2 and Rz gates """ +def Rx_using_GPI(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to Rx, using GPI and GPI2 gates + """ +def Ry_using_GPI(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to Ry, using GPI and GPI2 gates + """ +def Rz_using_GPI(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to Rz, using GPI gates + """ def SWAP_using_CX_0() -> pytket._tket.circuit.Circuit: """ Equivalent to SWAP, using three CX, outer CX have control on qubit 0 @@ -239,12 +255,20 @@ def TK1_to_TK1(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.E """ A circuit of a single tk1 gate with given parameters """ +def TK1_using_GPI(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to TK1, using GPI and GPI2 gates + """ def TK2_using_3xCX(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ Given expressions α, β and γ, return circuit equivalent to TK2(α, β, γ) using 3 CX and single-qubit gates. Prefer using `_TK2_using_CX` unless you wish to explicitly use 3 CX or if α, β and γ are not normalised to the Weyl chamber. """ +def TK2_using_AAMS(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to TK2, using AAMS, GPI and GPI2 gates + """ def TK2_using_CX(arg0: sympy.Expr | float, arg1: sympy.Expr | float, arg2: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ Given expressions α, β and γ, return circuit equivalent to TK2(α, β, γ) using up to 3 CX and single-qubit gates. @@ -297,6 +321,10 @@ def XXPhase3_using_TK2(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit """ Equivalent to XXPhase3, using three TK2 gates """ +def XXPhase_using_AAMS(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to XXPhase, using AAMS gates + """ def XXPhase_using_CX(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ Equivalent to XXPhase, using CX and U3 gates @@ -305,6 +333,10 @@ def XXPhase_using_TK2(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ Equivalent to XXPhase, using a TK2 gate """ +def YYPhase_using_AAMS(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to YYPhase, using AAMS gates + """ def YYPhase_using_CX(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ Equivalent to YYPhase, using two CX gates and one Ry, one Sdg and one S gate. @@ -321,6 +353,10 @@ def ZZMax_using_CX() -> pytket._tket.circuit.Circuit: """ Equivalent to ZZMax, using CX, Rz and U3 gates """ +def ZZPhase_using_AAMS(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: + """ + Equivalent to ZZPhase, using AAMS, GPI and GPI2 gates + """ def ZZPhase_using_CX(arg0: sympy.Expr | float) -> pytket._tket.circuit.Circuit: """ Equivalent to ZZPhase, using CX and Rz gates diff --git a/pytket/pytket/_tket/transform.pyi b/pytket/pytket/_tket/transform.pyi index 5fd1e85040..0795f97cbf 100644 --- a/pytket/pytket/_tket/transform.pyi +++ b/pytket/pytket/_tket/transform.pyi @@ -247,6 +247,11 @@ class Transform: Replace all single-qubit unitary gates outside the set {Z, X, S, V} that are recognized as Clifford operations with an equivalent sequence of gates from that set. """ @staticmethod + def RebaseToIonQ() -> Transform: + """ + Rebase from any gate set into the gate set supported by IonQ (GPI, GPI2, AAMS). + """ + @staticmethod def RebaseToProjectQ() -> Transform: """ Rebase from any gate set into the gate set supported by ProjectQ (Rx, Ry, Rz, X, Y, Z, S, T, V, H, CX, CZ, CRz, SWAP). diff --git a/pytket/pytket/circuit_library/__init__.py b/pytket/pytket/circuit_library/__init__.py index 114cfe5020..1cd3275f1e 100644 --- a/pytket/pytket/circuit_library/__init__.py +++ b/pytket/pytket/circuit_library/__init__.py @@ -27,6 +27,7 @@ CX_using_ZZPhase, CX_using_XXPhase_0, CX_using_XXPhase_1, + CX_using_AAMS, CX_VS_CX_reduced, CX_V_CX_reduced, CX_S_CX_reduced, @@ -100,4 +101,12 @@ TK1_to_RzH, TK1_to_RzSX, TK1_to_TK1, + Rx_using_GPI, + Ry_using_GPI, + Rz_using_GPI, + XXPhase_using_AAMS, + YYPhase_using_AAMS, + ZZPhase_using_AAMS, + TK1_using_GPI, + TK2_using_AAMS, ) diff --git a/pytket/pytket/passes/auto_rebase.py b/pytket/pytket/passes/auto_rebase.py index 4998963198..5cc5db4e4d 100644 --- a/pytket/pytket/passes/auto_rebase.py +++ b/pytket/pytket/passes/auto_rebase.py @@ -31,6 +31,7 @@ class NoAutoRebase(Exception): OpType.XXPhase: _library.CX_using_XXPhase_0, OpType.ECR: _library.CX_using_ECR, OpType.CZ: _library.H_CZ_H, + OpType.AAMS: _library.CX_using_AAMS, } @@ -43,6 +44,7 @@ def _TK2_using_TK2(a: Param, b: Param, c: Param) -> Circuit: OpType.ZZPhase: _library.TK2_using_ZZPhase, OpType.CX: _library.TK2_using_CX, OpType.ZZMax: _library.TK2_using_ZZMax, + OpType.AAMS: _library.TK2_using_AAMS, } _TK2_CIRCS_WIRE_SWAP: Dict[OpType, Callable[[Param, Param, Param], "Circuit"]] = { @@ -101,6 +103,7 @@ def get_tk2_decomposition( frozenset({OpType.Rz, OpType.SX}): _TK1_to_X_SX_Rz, frozenset({OpType.Rz, OpType.SX}): _library.TK1_to_RzSX, frozenset({OpType.U3}): _TK1_to_U, + frozenset({OpType.GPI, OpType.GPI2}): _library.TK1_using_GPI, } diff --git a/pytket/pytket/utils/symbolic.py b/pytket/pytket/utils/symbolic.py index 26b839fadb..a0d0926344 100644 --- a/pytket/pytket/utils/symbolic.py +++ b/pytket/pytket/utils/symbolic.py @@ -252,6 +252,48 @@ def symb_fsim(params: ParamsType) -> ImmutableMatrix: ) +def symb_gpi(params: ParamsType) -> ImmutableMatrix: + t = sympy.exp(I * sympy.pi * params[0]) + + return ImmutableMatrix( # type: ignore + [ + [0, 1 / t], + [t, 0], + ] + ) + + +def symb_gpi2(params: ParamsType) -> ImmutableMatrix: + t = sympy.exp(I * sympy.pi * params[0]) + c = 1 / sympy.sqrt(2) # type: ignore + + return c * ImmutableMatrix( # type: ignore + [ + [1, -I / t], + [-I * t, 1], + ] + ) + + +def symb_aams(params: ParamsType) -> ImmutableMatrix: + alpha, beta, gamma = params + c = sympy.cos(sympy.pi / 2 * alpha) + s = sympy.sin(sympy.pi / 2 * alpha) + s1 = -I * sympy.exp(I * sympy.pi * (-beta - gamma)) * s + s2 = -I * sympy.exp(I * sympy.pi * (-beta + gamma)) * s + s3 = -I * sympy.exp(I * sympy.pi * (beta - gamma)) * s + s4 = -I * sympy.exp(I * sympy.pi * (beta + gamma)) * s + + return ImmutableMatrix( # type: ignore + [ + [c, 0, 0, s1], + [0, c, s2, 0], + [0, s3, c, 0], + [s4, 0, 0, c], + ] + ) + + # end symbolic matrix definitions @@ -282,6 +324,9 @@ class SymGateRegister: OpType.PhasedX: symb_phasedx, OpType.ESWAP: symb_eswap, OpType.FSim: symb_fsim, + OpType.GPI: symb_gpi, + OpType.GPI2: symb_gpi2, + OpType.AAMS: symb_aams, } @classmethod diff --git a/pytket/tests/transform_test.py b/pytket/tests/transform_test.py index 850b8fadd3..e6212c3f5a 100644 --- a/pytket/tests/transform_test.py +++ b/pytket/tests/transform_test.py @@ -1074,6 +1074,16 @@ def test_auto_rebase() -> None: _library.CX(), _library.TK1_to_TK1, ), + ( + {OpType.GPI, OpType.GPI2, OpType.AAMS}, + _library.CX_using_AAMS(), + _library.TK1_using_GPI, + ), + ( + {OpType.GPI, OpType.GPI2, OpType.AAMS}, + _library.TK2_using_AAMS, + _library.TK1_using_GPI, + ), ] circ = get_test_circuit() diff --git a/tket/conanfile.py b/tket/conanfile.py index 180dfffec2..d8a0c63a25 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.125" + version = "1.2.126" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" diff --git a/tket/include/tket/Circuit/CircPool.hpp b/tket/include/tket/Circuit/CircPool.hpp index 461e909f0f..33de57c09e 100644 --- a/tket/include/tket/Circuit/CircPool.hpp +++ b/tket/include/tket/Circuit/CircPool.hpp @@ -49,6 +49,9 @@ const Circuit &CX_using_XXPhase_0(); /** Equivalent to CX, using only XXPhase, Rx and Rz gates */ const Circuit &CX_using_XXPhase_1(); +/** Equivalent to CX, using only AAMS, GPI and GPI2 gates */ +const Circuit &CX_using_AAMS(); + /** * CX-reduced form of CX/V,S/CX * @@ -536,6 +539,30 @@ Circuit CnU_gray_code_decomp(unsigned n, const Gate_ptr &gate); Circuit CnSU2_linear_decomp( unsigned n, const Expr &alpha, const Expr &theta, const Expr &beta); +/** Equivalent to Rx, using GPI and GPI2 gates */ +Circuit Rx_using_GPI(const Expr &theta); + +/** Equivalent to Ry, using GPI and GPI2 gates */ +Circuit Ry_using_GPI(const Expr &theta); + +/** Equivalent to Rz, using GPI gates */ +Circuit Rz_using_GPI(const Expr &theta); + +/** Equivalent to XXPhase, using AAMS gates */ +Circuit XXPhase_using_AAMS(const Expr &theta); + +/** Equivalent to YYPhase, using AAMS gates */ +Circuit YYPhase_using_AAMS(const Expr &theta); + +/** Equivalent to ZZPhase, using AAMS, GPI and GPI2 gates */ +Circuit ZZPhase_using_AAMS(const Expr &theta); + +/** Equivalent to TK1, using GPI and GPI2 gates */ +Circuit TK1_using_GPI(const Expr &alpha, const Expr &beta, const Expr &gamma); + +/** Equivalent to TK2, using AAMS, GPI and GPI2 gates */ +Circuit TK2_using_AAMS(const Expr &alpha, const Expr &beta, const Expr &gamma); + } // namespace CircPool } // namespace tket diff --git a/tket/include/tket/Transformations/Rebase.hpp b/tket/include/tket/Transformations/Rebase.hpp index 3eea674873..9bc011bd41 100644 --- a/tket/include/tket/Transformations/Rebase.hpp +++ b/tket/include/tket/Transformations/Rebase.hpp @@ -77,6 +77,10 @@ Transform rebase_TK(); // Singleqs: Rz, PhasedX Transform rebase_UMD(); +// Multiqs: AAMS +// Singleqs: GPI, GPI2 +Transform rebase_ionq(); + } // namespace Transforms } // namespace tket diff --git a/tket/src/Circuit/CircPool.cpp b/tket/src/Circuit/CircPool.cpp index 1b9d84368f..d0a6a4e1c8 100644 --- a/tket/src/Circuit/CircPool.cpp +++ b/tket/src/Circuit/CircPool.cpp @@ -153,6 +153,20 @@ const Circuit &CX_using_XXPhase_1() { return *C; } +const Circuit &CX_using_AAMS() { + static std::unique_ptr C = std::make_unique([]() { + Circuit c(2); + c.add_op(OpType::GPI2, 0.5, {0}); + c.add_op(OpType::GPI2, 1, {0}); + c.add_op(OpType::GPI2, 1, {1}); + c.add_op(OpType::AAMS, {0.5, 0, 0}, {0, 1}); + c.add_op(OpType::GPI2, -0.5, {0}); + c.add_phase(-0.25); + return c; + }()); + return *C; +} + const Circuit &CX_VS_CX_reduced() { static std::unique_ptr C = std::make_unique([]() { Circuit c(2); @@ -1377,6 +1391,80 @@ Circuit tk1_to_PhasedXRz( return c; } +Circuit Rx_using_GPI(const Expr &theta) { + Circuit c(1); + c.add_op(OpType::GPI2, 0.5, {0}); + c.add_op(OpType::GPI, 0.5 * theta, {0}); + c.add_op(OpType::GPI, 0, {0}); + c.add_op(OpType::GPI2, -0.5, {0}); + return c; +} + +Circuit Ry_using_GPI(const Expr &theta) { + Circuit c(1); + c.add_op(OpType::GPI2, 1, {0}); + c.add_op(OpType::GPI, 0.5 * theta, {0}); + c.add_op(OpType::GPI, 0, {0}); + c.add_op(OpType::GPI2, 0, {0}); + return c; +} + +Circuit Rz_using_GPI(const Expr &theta) { + Circuit c(1); + c.add_op(OpType::GPI, -0.5 * theta, {0}); + c.add_op(OpType::GPI, 0, {0}); + return c; +} + +Circuit XXPhase_using_AAMS(const Expr &theta) { + Circuit c(2); + c.add_op(OpType::AAMS, {theta, 0, 0}, {0, 1}); + return c; +} + +Circuit YYPhase_using_AAMS(const Expr &theta) { + Circuit c(2); + c.add_op(OpType::AAMS, {theta, 0.5, 0.5}, {0, 1}); + return c; +} + +Circuit ZZPhase_using_AAMS(const Expr &theta) { + Circuit c(2); + c.add_op(OpType::GPI2, 0.5, {0}); + c.add_op(OpType::GPI2, 1, {0}); + c.add_op(OpType::GPI2, 1, {1}); + c.add_op(OpType::AAMS, {theta, 0, 0.5}, {0, 1}); + c.add_op(OpType::GPI2, 0, {1}); + c.add_op(OpType::GPI2, 0, {0}); + c.add_op(OpType::GPI2, -0.5, {0}); + return c; +} + +Circuit TK1_using_GPI(const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c(1); + c.add_op(OpType::GPI, 0, {0}); + c.add_op(OpType::GPI, 0.5 * gamma, {0}); + c.add_op(OpType::GPI2, 0.5, {0}); + c.add_op(OpType::GPI, 0.5 * beta, {0}); + c.add_op(OpType::GPI2, 0.5, {0}); + c.add_op(OpType::GPI, 0.5 * alpha, {0}); + return c; +} + +Circuit TK2_using_AAMS(const Expr &alpha, const Expr &beta, const Expr &gamma) { + Circuit c(2); + c.add_op(OpType::AAMS, {alpha, 0, 0}, {0, 1}); + c.add_op(OpType::AAMS, {beta, 0.5, 0.5}, {0, 1}); + c.add_op(OpType::GPI2, 0.5, {0}); + c.add_op(OpType::GPI2, 1, {0}); + c.add_op(OpType::GPI2, 1, {1}); + c.add_op(OpType::AAMS, {gamma, 0, 0.5}, {0, 1}); + c.add_op(OpType::GPI2, 0, {1}); + c.add_op(OpType::GPI2, 0, {0}); + c.add_op(OpType::GPI2, -0.5, {0}); + return c; +} + } // namespace CircPool } // namespace tket diff --git a/tket/src/Transformations/Rebase.cpp b/tket/src/Transformations/Rebase.cpp index a6f62cff0a..497dc7452f 100644 --- a/tket/src/Transformations/Rebase.cpp +++ b/tket/src/Transformations/Rebase.cpp @@ -287,6 +287,14 @@ Transform rebase_UMD() { CircPool::CX_using_XXPhase_0(), CircPool::tk1_to_PhasedXRz); } +// Multiqs: AAMS +// Singleqs: GPI, GPI2 +Transform rebase_ionq() { + return rebase_factory( + {OpType::GPI, OpType::GPI2, OpType::AAMS}, CircPool::CX_using_AAMS(), + CircPool::TK1_using_GPI); +} + } // namespace Transforms } // namespace tket diff --git a/tket/test/src/Circuit/test_CircPool.cpp b/tket/test/src/Circuit/test_CircPool.cpp index 70ac4e1af0..d469525bda 100644 --- a/tket/test/src/Circuit/test_CircPool.cpp +++ b/tket/test/src/Circuit/test_CircPool.cpp @@ -45,6 +45,11 @@ SCENARIO("Simple CircPool identities") { orig.add_op(OpType::CX, {0, 1}); res = CircPool::H_CZ_H(); } + GIVEN("CX_using_AAMS") { + orig = Circuit(2); + orig.add_op(OpType::CX, {0, 1}); + res = CircPool::CX_using_AAMS(); + } auto u_orig = tket_sim::get_unitary(orig); auto u_res = tket_sim::get_unitary(res); @@ -234,5 +239,210 @@ SCENARIO("Test remove_noops") { } } +SCENARIO( + "Rx_using_GPI, Ry_using_GPI, Rz_using_GPI, XXPhase_using_AAMS," + "YYPhase_using_AAMS, ZZPhase_using_AAMS") { + Expr e1; + Sym asym = SymEngine::symbol("a"); + Expr a(asym); + + GIVEN("Normalised concrete angles (1)") { e1 = .3; } + GIVEN("Normalised concrete angles (2)") { e1 = -0.32; } + GIVEN("Not normalised concrete angles (1)") { e1 = 1.4; } + GIVEN("Not normalised concrete angles (2)") { e1 = -5.7; } + GIVEN("Symbolic angles (1)") { e1 = a; } + + Circuit rx_orig(1); + rx_orig.add_op(OpType::Rx, {e1}, {0}); + Circuit ry_orig(1); + ry_orig.add_op(OpType::Ry, {e1}, {0}); + Circuit rz_orig(1); + rz_orig.add_op(OpType::Rz, {e1}, {0}); + Circuit xxphase_orig(2); + xxphase_orig.add_op(OpType::XXPhase, {e1}, {0, 1}); + Circuit yyphase_orig(2); + yyphase_orig.add_op(OpType::YYPhase, {e1}, {0, 1}); + Circuit zzphase_orig(2); + zzphase_orig.add_op(OpType::ZZPhase, {e1}, {0, 1}); + std::vector circuits_orig = { + rx_orig, ry_orig, rz_orig, xxphase_orig, yyphase_orig, zzphase_orig}; + + Circuit rx_res = CircPool::Rx_using_GPI(e1); + Circuit ry_res = CircPool::Ry_using_GPI(e1); + Circuit rz_res = CircPool::Rz_using_GPI(e1); + Circuit xxphase_res = CircPool::XXPhase_using_AAMS(e1); + Circuit yyphase_res = CircPool::YYPhase_using_AAMS(e1); + Circuit zzphase_res = CircPool::ZZPhase_using_AAMS(e1); + std::vector circuits_res = {rx_res, ry_res, rz_res, + xxphase_res, yyphase_res, zzphase_res}; + + // check unitary identity + auto symset = circuits_orig[0].free_symbols(); + std::vector symbols(symset.begin(), symset.end()); + if (symbols.empty()) { + for (unsigned k = 0; k < 6; k++) { + auto u_orig = tket_sim::get_unitary(circuits_orig[k]); + auto u_res = tket_sim::get_unitary(circuits_res[k]); + REQUIRE(u_res.isApprox(u_orig)); + } + } else { + std::vector rands{0.1231, 2.3124, 34.23, 2.23, 3.15, 1.2, 0.93}; + // substitute random values for symbolics and check equality + unsigned i = 0; + while (i + symbols.size() <= rands.size()) { + symbol_map_t symmap; + for (unsigned j = 0; j < symbols.size(); ++j) { + symmap[symbols[j]] = rands[i + j]; + } + for (unsigned k = 0; k < 6; ++k) { + Circuit orig_sub = circuits_orig[k]; + orig_sub.symbol_substitution(symmap); + auto u_orig = tket_sim::get_unitary(orig_sub); + Circuit res_sub = circuits_res[k]; + res_sub.symbol_substitution(symmap); + auto u_res = tket_sim::get_unitary(res_sub); + REQUIRE(u_res.isApprox(u_orig)); + } + ++i; + } + } +} + +SCENARIO("TK1_using_GPI, TK2_using_AAMS") { + Expr e1, e2, e3; + Sym asym = SymEngine::symbol("a"); + Expr a(asym); + Sym bsym = SymEngine::symbol("b"); + Expr b(bsym); + Sym csym = SymEngine::symbol("c"); + Expr c(csym); + + GIVEN("Normalised concrete angles (1)") { + e1 = .3; + e2 = .1; + e3 = .05; + } + GIVEN("Normalised concrete angles (2)") { + e1 = 0.32; + e2 = 0.31; + e3 = -0.3; + } + GIVEN("Not normalised concrete angles (1)") { + e1 = .3; + e2 = .4; + e3 = .45; + } + GIVEN("Not normalised concrete angles (2)") { + e1 = .3; + e2 = 1.4; + e3 = .489; + } + GIVEN("Not normalised concrete angles (3)") { + e1 = 2.3; + e2 = 3.4; + e3 = .489; + } + GIVEN("Not normalised concrete angles (4)") { + e1 = .3; + e2 = -.2; + e3 = .1; + } + GIVEN("Not normalised concrete angles (5)") { + e1 = -.3; + e2 = -.2; + e3 = .1; + } + GIVEN("Not normalised concrete angles (6)") { + e1 = .3; + e2 = .2; + e3 = -.3; + } + GIVEN("Not normalised concrete angles (7)") { + e1 = 0; + e2 = 0; + e3 = -1.2; + } + GIVEN("Not normalised concrete angles (8)") { + e1 = 0.1; + e2 = 0.3; + e3 = 0.2; + } + GIVEN("Symbolic angles (1)") { + e1 = a; + e2 = 3.4; + e3 = .489; + } + GIVEN("Symbolic angles (2)") { + e1 = a; + e2 = b; + e3 = 2.42; + } + GIVEN("Symbolic angles (3)") { + e1 = 2.3; + e2 = b; + e3 = 1.489; + } + GIVEN("Symbolic angles (4)") { + e1 = 2.3; + e2 = 123.08174; + e3 = c; + } + GIVEN("Symbolic angles (5)") { + e1 = a; + e2 = 123.08174; + e3 = c; + } + GIVEN("Symbolic angles (6)") { + e1 = 0.10012; + e2 = b; + e3 = c; + } + + Circuit tk1_orig(1); + tk1_orig.add_op(OpType::TK1, {e1, e2, e3}, {0}); + Circuit tk1_res = CircPool::TK1_using_GPI(e1, e2, e3); + + Circuit tk2_orig(2); + tk2_orig.add_op(OpType::TK2, {e1, e2, e3}, {0, 1}); + Circuit tk2_res = CircPool::TK2_using_AAMS(e1, e2, e3); + + // check unitary identity + auto symset = tk1_orig.free_symbols(); + std::vector symbols(symset.begin(), symset.end()); + if (symbols.empty()) { + auto u_tk1_orig = tket_sim::get_unitary(tk1_orig); + auto u_tk2_orig = tket_sim::get_unitary(tk2_orig); + auto u_tk1_res = tket_sim::get_unitary(tk1_res); + auto u_tk2_res = tket_sim::get_unitary(tk2_res); + REQUIRE(u_tk1_res.isApprox(u_tk1_orig)); + REQUIRE(u_tk2_res.isApprox(u_tk2_orig)); + } else { + std::vector rands{0.1231, 2.3124, 34.23, 2.23, 3.15, 1.2, 0.93}; + // substitute random values for symbolics and check equality + unsigned i = 0; + while (i + symbols.size() <= rands.size()) { + symbol_map_t symmap; + for (unsigned j = 0; j < symbols.size(); ++j) { + symmap[symbols[j]] = rands[i + j]; + } + Circuit tk1_orig_sub = tk1_orig; + Circuit tk2_orig_sub = tk2_orig; + tk1_orig_sub.symbol_substitution(symmap); + tk2_orig_sub.symbol_substitution(symmap); + auto u_tk1_orig = tket_sim::get_unitary(tk1_orig_sub); + auto u_tk2_orig = tket_sim::get_unitary(tk2_orig_sub); + Circuit tk1_res_sub = tk1_res; + Circuit tk2_res_sub = tk2_res; + tk1_res_sub.symbol_substitution(symmap); + tk2_res_sub.symbol_substitution(symmap); + auto u_tk1_res = tket_sim::get_unitary(tk1_res_sub); + auto u_tk2_res = tket_sim::get_unitary(tk2_res_sub); + REQUIRE(u_tk1_res.isApprox(u_tk1_orig)); + REQUIRE(u_tk2_res.isApprox(u_tk2_orig)); + ++i; + } + } +} + } // namespace test_CircPool } // namespace tket diff --git a/tket/test/src/Circuit/test_Symbolic.cpp b/tket/test/src/Circuit/test_Symbolic.cpp index 4e95122770..2389786d6e 100644 --- a/tket/test/src/Circuit/test_Symbolic.cpp +++ b/tket/test/src/Circuit/test_Symbolic.cpp @@ -207,5 +207,57 @@ SCENARIO("Symbolic squashing, correctness") { } } +SCENARIO("Symbolic GPI, GPI2, AAMS") { + Sym asym = SymEngine::symbol("a"); + Expr a(asym); + Sym bsym = SymEngine::symbol("b"); + Expr b(bsym); + Sym csym = SymEngine::symbol("c"); + Expr c(csym); + + Circuit gpi_orig(1); + gpi_orig.add_op(OpType::GPI, a, {0}); + Circuit gpi2_orig(1); + gpi2_orig.add_op(OpType::GPI2, a, {0}); + Circuit aams_orig(2); + aams_orig.add_op(OpType::AAMS, {a, b, c}, {0, 1}); + + std::vector rands{0.1231, 2.3124, 34.23, 2.23, 3.15, 1.2, 0.93}; + for (unsigned i = 0; i < rands.size(); ++i) { + Circuit gpi_orig_sub = gpi_orig; + Circuit gpi2_orig_sub = gpi2_orig; + double an = rands[i]; + symbol_map_t symmap; + symmap[asym] = an; + gpi_orig_sub.symbol_substitution(symmap); + gpi2_orig_sub.symbol_substitution(symmap); + auto u_gpi_orig = tket_sim::get_unitary(gpi_orig_sub); + auto u_gpi2_orig = tket_sim::get_unitary(gpi2_orig_sub); + Circuit gpi_res(1); + Circuit gpi2_res(1); + gpi_res.add_op(OpType::GPI, an, {0}); + gpi2_res.add_op(OpType::GPI2, an, {0}); + auto u_gpi_res = tket_sim::get_unitary(gpi_res); + auto u_gpi2_res = tket_sim::get_unitary(gpi2_res); + REQUIRE(u_gpi_res.isApprox(u_gpi_orig)); + REQUIRE(u_gpi2_res.isApprox(u_gpi2_orig)); + for (unsigned j = 0; j < rands.size(); ++j) { + double bn = rands[j]; + for (unsigned k = 0; k < rands.size(); ++k) { + double cn = rands[k]; + symmap[bsym] = bn; + symmap[csym] = cn; + Circuit aams_orig_sub = aams_orig; + aams_orig_sub.symbol_substitution(symmap); + auto u_aams_orig = tket_sim::get_unitary(aams_orig_sub); + Circuit aams_res(2); + aams_res.add_op(OpType::AAMS, {an, bn, cn}, {0, 1}); + auto u_aams_res = tket_sim::get_unitary(aams_res); + REQUIRE(u_aams_res.isApprox(u_aams_orig)); + } + } + } +} + } // namespace test_Symbolic } // namespace tket From ce208337b38b043204a405769f5e76f515a73a74 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Thu, 16 May 2024 10:03:19 +0100 Subject: [PATCH 26/33] Update docs and changelog. (#1398) --- pytket/docs/changelog.rst | 4 ++-- pytket/docs/conf.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 3140ed281d..11687d596b 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -1,8 +1,8 @@ Changelog ========= -Unreleased ----------- +1.28.0 (May 2024) +----------------- Features: diff --git a/pytket/docs/conf.py b/pytket/docs/conf.py index 9d4e85b76d..b29825325d 100644 --- a/pytket/docs/conf.py +++ b/pytket/docs/conf.py @@ -38,9 +38,9 @@ author = "Quantinuum" # The short X.Y version -version = "1.27" +version = "1.28" # The full version, including alpha/beta/rc tags -release = "1.27.0" +release = "1.28.0" # -- General configuration --------------------------------------------------- From de0cf979345607ff178dc1b8481a719be42c3fcd Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Thu, 16 May 2024 11:02:22 +0100 Subject: [PATCH 27/33] Fix name for consistency and to match reference in `publish_to_pypi`. (#1399) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8b70da70c5..36537ed530 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -217,7 +217,7 @@ jobs: - name: Run tests run: cd tket/pytket/tests && pytest --ignore=simulator/ - test_Linux_aarch64_wheels: + test_linux_aarch64_wheels: name: Test linux aarch64 wheels needs: build_Linux_aarch64_wheels runs-on: 'buildjet-4vcpu-ubuntu-2204-arm' From 7ecbb50c31b2ab04600287ee912b84e22a36d7ac Mon Sep 17 00:00:00 2001 From: yao-cqc <75305462+yao-cqc@users.noreply.github.com> Date: Fri, 17 May 2024 15:16:02 +0100 Subject: [PATCH 28/33] Allow barriers when dagger or transpose circuits (#1400) * Allow barriers in dagger() and transpose() * bump tket version * add changelog entry * Format test --- pytket/conanfile.py | 2 +- pytket/docs/changelog.rst | 8 ++++++++ pytket/tests/circuit_test.py | 9 +++++++++ tket/conanfile.py | 2 +- tket/src/Circuit/macro_manipulation.cpp | 3 +++ tket/test/src/Circuit/test_Circ.cpp | 26 +++++++++++++++++++++++++ 6 files changed, 48 insertions(+), 2 deletions(-) diff --git a/pytket/conanfile.py b/pytket/conanfile.py index 2dc87bfd8b..c0d7fc3686 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.2.126@tket/stable") + self.requires("tket/1.2.127@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 11687d596b..70f385eb4d 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -1,6 +1,14 @@ Changelog ========= +Unreleased +---------- + +Fixes: + +* Allow barriers when dagger or transpose a circuit. + + 1.28.0 (May 2024) ----------------- diff --git a/pytket/tests/circuit_test.py b/pytket/tests/circuit_test.py index 19eb6157b9..2c400b205e 100644 --- a/pytket/tests/circuit_test.py +++ b/pytket/tests/circuit_test.py @@ -141,6 +141,15 @@ def test_circuit_dagger() -> None: assert commands[1].op.get_matrix().all() == u.conj().transpose().all() +def test_circuit_dagger_transpose_with_barriers() -> None: + c = Circuit(2).S(0).add_barrier([0, 1]).CX(0, 1) + c_d = c.dagger() + assert c_d == Circuit(2).CX(0, 1).add_barrier([0, 1]).Sdg(0) + c = Circuit(2).Ry(0.3, 0).add_barrier([0, 1]).CX(0, 1) + c_t = c.transpose() + assert c_t == Circuit(2).CX(0, 1).add_barrier([0, 1]).Ry(-0.3, 0) + + # TKET-1365 bug def test_cnx_dagger() -> None: c = Circuit(2) diff --git a/tket/conanfile.py b/tket/conanfile.py index d8a0c63a25..8aaa2905f8 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.126" + version = "1.2.127" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" diff --git a/tket/src/Circuit/macro_manipulation.cpp b/tket/src/Circuit/macro_manipulation.cpp index 4aeb715900..8b77cab4d5 100644 --- a/tket/src/Circuit/macro_manipulation.cpp +++ b/tket/src/Circuit/macro_manipulation.cpp @@ -484,6 +484,9 @@ void Circuit::_handle_interior( } Vertex v = circ.add_vertex(op_type_ptr); vmap[*vi] = v; + } else if (desc.is_barrier()) { + Vertex v = circ.add_vertex(op); + vmap[*vi] = v; } else { throw CircuitInvalidity( "Cannot dagger or transpose op: " + op->get_name()); diff --git a/tket/test/src/Circuit/test_Circ.cpp b/tket/test/src/Circuit/test_Circ.cpp index c09122a9f2..5e96517130 100644 --- a/tket/test/src/Circuit/test_Circ.cpp +++ b/tket/test/src/Circuit/test_Circ.cpp @@ -1499,6 +1499,19 @@ SCENARIO("Test circuit.transpose() method") { REQUIRE(matrices_are_equal(ubox_t->get_matrix(), m.transpose())); REQUIRE(*cx_t_ptr == *get_op_ptr(OpType::CX)); } + GIVEN("Circuit with barriers") { + Circuit circ(2); + circ.add_op(OpType::Y, {0}); + circ.add_barrier({0, 1}, {}, "comment"); + circ.add_op(OpType::CX, {0, 1}); + Circuit correct_transposed(2); + correct_transposed.add_op(OpType::CX, {0, 1}); + correct_transposed.add_barrier({0, 1}, {}, "comment"); + correct_transposed.add_op(OpType::U3, {3, 0.5, 0.5}, {0}); + Circuit transposed = circ.transpose(); + REQUIRE(transposed == correct_transposed); + transposed.assert_valid(); + } } SCENARIO("Test circuit.dagger() method") { @@ -1535,6 +1548,19 @@ SCENARIO("Test circuit.dagger() method") { const Eigen::MatrixXcd udag = tket_sim::get_unitary(daggered); REQUIRE(u.adjoint().isApprox(udag, ERR_EPS)); } + GIVEN("Circuit with barriers") { + Circuit circ(2); + circ.add_op(OpType::Sdg, {0}); + circ.add_barrier({0, 1}, {}, "comment"); + circ.add_op(OpType::CX, {0, 1}); + Circuit correct_daggered(2); + correct_daggered.add_op(OpType::CX, {0, 1}); + correct_daggered.add_barrier({0, 1}, {}, "comment"); + correct_daggered.add_op(OpType::S, {0}); + Circuit daggered = circ.dagger(); + REQUIRE(daggered == correct_daggered); + daggered.assert_valid(); + } } SCENARIO("Test conditional_circuit method") { From 76e3f5767245e0a0fdaed4589e217109a10464f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 09:23:30 +0100 Subject: [PATCH 29/33] Bump cachix/install-nix-action from 26 to 27 (#1403) Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 26 to 27. - [Release notes](https://github.com/cachix/install-nix-action/releases) - [Commits](https://github.com/cachix/install-nix-action/compare/v26...V27) --- updated-dependencies: - dependency-name: cachix/install-nix-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/build-with-nix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-with-nix.yml b/.github/workflows/build-with-nix.yml index 7a83d0b784..9ebe7ad6b4 100644 --- a/.github/workflows/build-with-nix.yml +++ b/.github/workflows/build-with-nix.yml @@ -20,7 +20,7 @@ jobs: runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v4 - - uses: cachix/install-nix-action@v26 + - uses: cachix/install-nix-action@V27 - uses: cachix/cachix-action@v14 with: name: tket From 98979c0a89e788622f9ac3c3432e6fbf30e8d167 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 May 2024 09:28:48 +0100 Subject: [PATCH 30/33] Bump actions/add-to-project from 0.6.1 to 1.0.1 (#1404) Bumps [actions/add-to-project](https://github.com/actions/add-to-project) from 0.6.1 to 1.0.1. - [Release notes](https://github.com/actions/add-to-project/releases) - [Commits](https://github.com/actions/add-to-project/compare/v0.6.1...v1.0.1) --- updated-dependencies: - dependency-name: actions/add-to-project dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/issue-to-project.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue-to-project.yml b/.github/workflows/issue-to-project.yml index 5c8ba372c5..f55a9f0610 100644 --- a/.github/workflows/issue-to-project.yml +++ b/.github/workflows/issue-to-project.yml @@ -10,7 +10,7 @@ jobs: name: Add issue to project runs-on: ubuntu-latest steps: - - uses: actions/add-to-project@v0.6.1 + - uses: actions/add-to-project@v1.0.1 with: project-url: https://github.com/orgs/CQCL-DEV/projects/19 github-token: ${{ secrets.ADD_TO_PROJECT_PAT }} From ecc985550a7431fe118a114c770192f41a5805c7 Mon Sep 17 00:00:00 2001 From: Alec Edgington <54802828+cqc-alec@users.noreply.github.com> Date: Mon, 20 May 2024 10:55:32 +0100 Subject: [PATCH 31/33] Use `ubuntu-24.04` on CI (#1401) --- .github/workflows/build-with-nix.yml | 2 +- .github/workflows/build-without-conan.yml | 4 +- .github/workflows/build_and_test.yml | 47 ++++++++++++++--------- .github/workflows/build_libs.yml | 10 ++--- .github/workflows/coverage.yml | 16 +++++--- .github/workflows/docs.yml | 4 +- .github/workflows/lint.yml | 2 +- .github/workflows/packages.yml | 9 ++--- .github/workflows/release.yml | 14 +++---- .github/workflows/test-args | 20 ---------- .github/workflows/test_libs.yml | 20 ++++++---- .github/workflows/test_libs_all.yml | 6 +-- .github/workflows/valgrind.yml | 22 ++++++++--- README.md | 4 +- conan-profiles/ubuntu-24.04 | 8 ++++ pytket/conanfile.py | 2 +- tket/conanfile.py | 2 +- 17 files changed, 104 insertions(+), 88 deletions(-) delete mode 100755 .github/workflows/test-args create mode 100644 conan-profiles/ubuntu-24.04 diff --git a/.github/workflows/build-with-nix.yml b/.github/workflows/build-with-nix.yml index 9ebe7ad6b4..7695852d03 100644 --- a/.github/workflows/build-with-nix.yml +++ b/.github/workflows/build-with-nix.yml @@ -16,7 +16,7 @@ jobs: build_and_test: strategy: matrix: - os: ['ubuntu-22.04', 'macos-14'] + os: ['ubuntu-24.04', 'macos-14'] runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/build-without-conan.yml b/.github/workflows/build-without-conan.yml index 311d153b41..26739ed4b5 100644 --- a/.github/workflows/build-without-conan.yml +++ b/.github/workflows/build-without-conan.yml @@ -9,7 +9,7 @@ concurrency: cancel-in-progress: true jobs: build_and_test: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 env: INSTALL_DIR: "/home/runner/local" TMP_DIR: "/home/runner/tmp" @@ -22,7 +22,7 @@ jobs: - name: ccache uses: hendrikmuhs/ccache-action@v1.2 with: - key: no-conan-build-ubuntu-22.04 + key: no-conan-build-ubuntu-24.04 - name: install ninja run: | sudo apt update diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index eca21c7f2a..0dec90b3d8 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -19,7 +19,7 @@ concurrency: jobs: check_changes: name: Check tket library version - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: tket_or_workflow_changed: ${{ steps.filter.outputs.tket_or_workflow }} tket_changed: ${{ steps.filter.outputs.tket }} @@ -29,7 +29,8 @@ jobs: tket_package_exists: ${{ steps.tket_package_exists.outputs.tket_package_exists }} steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - name: Select Python 3.12 + uses: actions/setup-python@v5 with: python-version: '3.12' - uses: dorny/paths-filter@v3.0.2 @@ -69,7 +70,7 @@ jobs: name: Check documentation build needs: check_changes if: needs.check_changes.outputs.tket_or_workflow_changed == 'true' || needs.check_changes.outputs.doxyfile_or_workflow_changed == 'true' - runs-on: 'ubuntu-22.04' + runs-on: 'ubuntu-24.04' steps: - uses: actions/checkout@v4 - name: Check doxygen @@ -97,18 +98,17 @@ jobs: if: needs.check_changes.outputs.tket_or_workflow_changed == 'true' strategy: matrix: - os: ['ubuntu-22.04', 'macos-12', 'macos-14'] + os: ['ubuntu-24.04', 'macos-12', 'macos-14'] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: apt update - if: matrix.os == 'ubuntu-22.04' + if: matrix.os == 'ubuntu-24.04' run: sudo apt update - - name: Select Python 3.11 - # otherwise turtlebrowser/get-conan@v1.2 fails on macos-12 + - name: Select Python 3.12 uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install conan uses: turtlebrowser/get-conan@v1.2 - name: Set up conan @@ -120,7 +120,7 @@ jobs: cp ${PROFILE_PATH} ${DEFAULT_PROFILE_PATH} conan remote add tket-libs https://quantinuumsw.jfrog.io/artifactory/api/conan/tket1-libs --index 0 - name: Install runtime test requirements - if: matrix.os == 'ubuntu-22.04' && github.event_name == 'schedule' + if: matrix.os == 'ubuntu-24.04' && github.event_name == 'schedule' run: | sudo apt install texlive texlive-latex-extra latexmk mkdir -p ~/texmf/tex/latex @@ -155,6 +155,10 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 + - name: Select Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: '3.12' - name: Install conan uses: turtlebrowser/get-conan@v1.2 - name: Set up conan @@ -223,23 +227,27 @@ jobs: name: Build and test pytket (ubuntu) needs: check_changes if: needs.check_changes.outputs.tket_or_workflow_changed == 'true' || needs.check_changes.outputs.pytket_or_workflow_changed == 'true' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + - name: Select Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: '3.12' - name: Install conan uses: turtlebrowser/get-conan@v1.2 - name: Set up conan run: | conan profile detect DEFAULT_PROFILE_PATH=`conan profile path default` - PROFILE_PATH=./conan-profiles/ubuntu-22.04 + PROFILE_PATH=./conan-profiles/ubuntu-24.04 diff ${DEFAULT_PROFILE_PATH} ${PROFILE_PATH} || true cp ${PROFILE_PATH} ${DEFAULT_PROFILE_PATH} conan remote add tket-libs https://quantinuumsw.jfrog.io/artifactory/api/conan/tket1-libs --index 0 - name: ccache uses: hendrikmuhs/ccache-action@v1.2 with: - key: tket-dynamic-ubuntu-22.04 + key: tket-dynamic-ubuntu-24.04 - name: further ccache config run: | ccache --set-config base_dir=${HOME} @@ -334,11 +342,10 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - name: Select Python 3.11 - # otherwise turtlebrowser/get-conan@v1.2 fails on macos-12 + - name: Select Python 3.12 uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install conan uses: turtlebrowser/get-conan@v1.2 - name: Set up conan @@ -421,6 +428,10 @@ jobs: runs-on: windows-2022 steps: - uses: actions/checkout@v4 + - name: Select Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: '3.12' - name: Install conan uses: turtlebrowser/get-conan@v1.2 - name: Set up conan @@ -535,7 +546,7 @@ jobs: needs: build_test_pytket_ubuntu concurrency: gh_pages if: github.event_name == 'push' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: @@ -563,7 +574,7 @@ jobs: name: Check pytket line and branch coverage needs: build_test_pytket_ubuntu if: github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Download artefact @@ -591,7 +602,7 @@ jobs: - publish_pytket_coverage - check_pytket_coverage if: always() - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - shell: python name: Check job results diff --git a/.github/workflows/build_libs.yml b/.github/workflows/build_libs.yml index eb0ac8cb91..af717d470b 100644 --- a/.github/workflows/build_libs.yml +++ b/.github/workflows/build_libs.yml @@ -10,7 +10,7 @@ on: jobs: changes: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: tklog: ${{ steps.filter.outputs.tklog }} tkassert: ${{ steps.filter.outputs.tkassert }} @@ -46,7 +46,7 @@ jobs: if: ${{ needs.changes.outputs.libs != '[]' && needs.changes.outputs.libs != '' }} strategy: matrix: - os: ['ubuntu-22.04', 'macos-12', 'windows-2022', 'macos-14'] + os: ['ubuntu-24.04', 'macos-12', 'windows-2022', 'macos-14'] lib: ${{ fromJson(needs.changes.outputs.libs) }} build_type: ['Release', 'Debug'] runs-on: ${{ matrix.os }} @@ -63,10 +63,10 @@ jobs: $normalized_file = [IO.File]::ReadAllText($f) -replace "`r`n", "`n" [IO.File]::WriteAllText($f, $normalized_file) } - - name: Set up Python 3.11 + - name: Select Python 3.12 uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install conan uses: turtlebrowser/get-conan@v1.2 - name: Set up conan @@ -97,7 +97,7 @@ jobs: name: build library (manylinux) needs: changes if: ${{ needs.changes.outputs.libs != '[]' && needs.changes.outputs.libs != '' }} - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: lib: ${{ fromJson(needs.changes.outputs.libs) }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index be2ebec17a..edf65fdaf6 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -19,7 +19,7 @@ concurrency: jobs: changes: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: tket: ${{ steps.filter.outputs.tket }} steps: @@ -37,16 +37,20 @@ jobs: name: Generate coverage report needs: changes if: needs.changes.outputs.tket == 'true' || github.event_name == 'schedule' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 + - name: Select Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: '3.12' - name: Install conan uses: turtlebrowser/get-conan@v1.2 - name: Set up conan run: | conan profile detect DEFAULT_PROFILE_PATH=`conan profile path default` - PROFILE_PATH=./conan-profiles/ubuntu-22.04 + PROFILE_PATH=./conan-profiles/ubuntu-24.04 diff ${DEFAULT_PROFILE_PATH} ${PROFILE_PATH} || true cp ${PROFILE_PATH} ${DEFAULT_PROFILE_PATH} conan remote add tket-libs https://quantinuumsw.jfrog.io/artifactory/api/conan/tket1-libs --index 0 @@ -91,7 +95,7 @@ jobs: name: Check coverage needs: generate_coverage if: ((github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch') && needs.changes.outputs.tket == 'true') || github.event_name == 'schedule' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Download artefact @@ -115,7 +119,7 @@ jobs: needs: generate_coverage concurrency: gh_pages if: github.event_name == 'push' && needs.changes.outputs.tket == 'true' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: @@ -144,7 +148,7 @@ jobs: needs: check_coverage concurrency: gh_pages if: github.event_name == 'schedule' - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 6edc995828..81d502deb6 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -9,7 +9,7 @@ on: jobs: build_docs: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Install Doxygen @@ -24,7 +24,7 @@ jobs: publish_docs: needs: build_docs concurrency: gh_pages - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ae68a0fd58..db16cda435 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,7 +7,7 @@ on: jobs: lint: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index d1b91ea09e..9a5980aee1 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -17,17 +17,16 @@ jobs: name: Build strategy: matrix: - os: ['ubuntu-22.04', 'macos-12', 'windows-2022', 'macos-14'] + os: ['ubuntu-24.04', 'macos-12', 'windows-2022', 'macos-14'] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - name: Select Python 3.11 - # otherwise turtlebrowser/get-conan@v1.2 fails on macos-12 + - name: Select Python 3.12 uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install conan uses: turtlebrowser/get-conan@v1.2 @@ -51,7 +50,7 @@ jobs: build_manylinux_x86_64: name: Build on manylinux (x86_64) - runs-on: 'ubuntu-22.04' + runs-on: 'ubuntu-24.04' steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 36537ed530..8762bc989f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,7 +11,7 @@ on: jobs: build_Linux_wheels: name: Build manylinux - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: python3-version: ['10', '11', '12'] @@ -159,6 +159,10 @@ jobs: with: fetch-depth: '0' - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* + - name: Select Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} - name: Install conan uses: turtlebrowser/get-conan@v1.2 - name: Set up conan @@ -172,10 +176,6 @@ jobs: conan remote add tket-libs https://quantinuumsw.jfrog.io/artifactory/api/conan/tket1-libs --index 0 - name: Build tket run: conan create tket --user tket --channel stable --build=missing -o boost/*:header_only=True -o tklog/*:shared=True -o tket/*:shared=True -tf "" - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - name: Build wheel run: | conan create recipes/pybind11 @@ -191,7 +191,7 @@ jobs: test_linux_wheels: name: Test linux wheels needs: build_Linux_wheels - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: matrix: python3-version: ['10', '11', '12'] @@ -348,7 +348,7 @@ jobs: name: Publish to pypi if: github.event_name == 'release' needs: [test_linux_wheels, test_linux_aarch64_wheels, test_macos_x86_wheels, test_macos_arm64_wheels, test_Windows_wheels] - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - name: Download all wheels uses: actions/download-artifact@v4 diff --git a/.github/workflows/test-args b/.github/workflows/test-args deleted file mode 100755 index 36f072ac27..0000000000 --- a/.github/workflows/test-args +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# ./test-args - -set -evx - -OS=$1 -ET=$2 - -if [[ $OS = "Linux" ]]; then - if [[ $ET = "schedule" ]]; then - echo "[long],~[long]" - fi -else - if [[ $ET = "schedule" ]]; then - echo "[long]~[latex],~[long]~[latex]" - else - echo "~[latex]" - fi -fi diff --git a/.github/workflows/test_libs.yml b/.github/workflows/test_libs.yml index 2091bafaf8..d8caee4c07 100644 --- a/.github/workflows/test_libs.yml +++ b/.github/workflows/test_libs.yml @@ -12,7 +12,7 @@ env: ALL_LIBS: '["tklog", "tkassert", "tkrng", "tktokenswap", "tkwsm"]' jobs: changes: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: tklog: ${{ steps.filter.outputs.tklog }} tkassert: ${{ steps.filter.outputs.tkassert }} @@ -40,7 +40,7 @@ jobs: set_libs_matrix: name: Set the libs strategy matrix needs: changes - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: libs: ${{ steps.set-matrix.outputs.matrix }} steps: @@ -63,15 +63,15 @@ jobs: strategy: fail-fast: false matrix: - os: ['ubuntu-22.04', 'macos-12', 'windows-2022', 'macos-14'] + os: ['ubuntu-24.04', 'macos-12', 'windows-2022', 'macos-14'] lib: ${{ fromJson(needs.set_libs_matrix.outputs.libs) }} runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - name: Set up Python 3.11 + - name: Select Python 3.12 uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install conan uses: turtlebrowser/get-conan@v1.2 - name: Set up conan @@ -100,16 +100,20 @@ jobs: strategy: matrix: lib: ${{ fromJson(needs.set_libs_matrix.outputs.libs) }} - runs-on: 'ubuntu-22.04' + runs-on: 'ubuntu-24.04' steps: - uses: actions/checkout@v4 + - name: Select Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: '3.12' - name: Install conan uses: turtlebrowser/get-conan@v1.2 - name: Set up conan run: | conan profile detect DEFAULT_PROFILE_PATH=`conan profile path default` - PROFILE_PATH=./conan-profiles/ubuntu-22.04 + PROFILE_PATH=./conan-profiles/ubuntu-24.04 diff ${DEFAULT_PROFILE_PATH} ${PROFILE_PATH} || true cp ${PROFILE_PATH} ${DEFAULT_PROFILE_PATH} conan remote add tket-libs https://quantinuumsw.jfrog.io/artifactory/api/conan/tket1-libs --index 0 @@ -151,7 +155,7 @@ jobs: matrix: lib: ${{ fromJson(needs.set_libs_matrix.outputs.libs) }} concurrency: gh_pages - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/test_libs_all.yml b/.github/workflows/test_libs_all.yml index 3421d87374..a66e28f5b6 100644 --- a/.github/workflows/test_libs_all.yml +++ b/.github/workflows/test_libs_all.yml @@ -10,15 +10,15 @@ jobs: name: test library strategy: matrix: - os: ['ubuntu-22.04', 'macos-12', 'windows-2022', 'macos-14'] + os: ['ubuntu-24.04', 'macos-12', 'windows-2022', 'macos-14'] lib: ['tklog', 'tkassert', 'tkrng', 'tktokenswap', 'tkwsm'] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - name: Set up Python 3.11 + - name: Select Python 3.12 uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' - name: Install conan uses: turtlebrowser/get-conan@v1.2 - name: Set up conan diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml index eaa78cca44..68f20741a9 100644 --- a/.github/workflows/valgrind.yml +++ b/.github/workflows/valgrind.yml @@ -14,7 +14,7 @@ concurrency: cancel-in-progress: true jobs: changes: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 outputs: tket: ${{ steps.filter.outputs.tket }} steps: @@ -28,20 +28,24 @@ jobs: - 'tket/**' - '.github/workflows/valgrind.yml' check: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 needs: changes if: needs.changes.outputs.tket == 'true' steps: - uses: actions/checkout@v4 - name: apt update run: sudo apt update + - name: Select Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: '3.12' - name: Install conan uses: turtlebrowser/get-conan@v1.2 - name: Set up conan run: | conan profile detect DEFAULT_PROFILE_PATH=`conan profile path default` - PROFILE_PATH=./conan-profiles/ubuntu-22.04 + PROFILE_PATH=./conan-profiles/ubuntu-24.04 diff ${DEFAULT_PROFILE_PATH} ${PROFILE_PATH} || true cp ${PROFILE_PATH} ${DEFAULT_PROFILE_PATH} conan remote add tket-libs https://quantinuumsw.jfrog.io/artifactory/api/conan/tket1-libs --index 0 @@ -58,13 +62,19 @@ jobs: ccache -p - name: build tket run: conan create tket --user=tket --channel=stable -o boost/*:header_only=True -o with_test=True --format json > tket.json - - name: Install runtime test requirements + - name: Run tests under valgrind (long) if: github.event_name == 'schedule' run: | sudo apt install texlive texlive-latex-extra latexmk mkdir -p ~/texmf/tex/latex wget http://mirrors.ctan.org/graphics/pgf/contrib/quantikz/tikzlibraryquantikz.code.tex -P ~/texmf/tex/latex - - name: Run tests under valgrind + PKGPATH=`./rootpath tket.json tket` + # realloc of size 0 used (intentionally?) in eigen + cd ${PKGPATH}/bin && valgrind --error-exitcode=1 --show-realloc-size-zero=no ./test-tket [long],~[long] + - name: Run tests under valgrind (basic) + if: github.event_name != 'schedule' run: | PKGPATH=`./rootpath tket.json tket` - cd ${PKGPATH}/bin && valgrind --error-exitcode=1 ./test-tket `./github/workflows/test-args ${{ matrix.os }} ${{ github.event_name }}` + # realloc of size 0 used (intentionally?) in eigen + cd ${PKGPATH}/bin && valgrind --error-exitcode=1 --show-realloc-size-zero=no ./test-tket + diff --git a/README.md b/README.md index d00fb29554..07f63fc3e8 100644 --- a/README.md +++ b/README.md @@ -65,8 +65,8 @@ The codebase is split into two main projects: The following compiler toolchains are used to build tket on the CI and are therefore known to work: -* Linux: gcc-11 -* MacOS: apple-clang 14 +* Linux: gcc-13 +* MacOS: apple-clang 15 * Windows: MSVC 19 It is recommended that you use these versions to build locally, as code may diff --git a/conan-profiles/ubuntu-24.04 b/conan-profiles/ubuntu-24.04 new file mode 100644 index 0000000000..8976420b03 --- /dev/null +++ b/conan-profiles/ubuntu-24.04 @@ -0,0 +1,8 @@ +[settings] +arch=x86_64 +build_type=Release +compiler=gcc +compiler.cppstd=gnu17 +compiler.libcxx=libstdc++11 +compiler.version=13 +os=Linux diff --git a/pytket/conanfile.py b/pytket/conanfile.py index c0d7fc3686..dde43e56cd 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.2.127@tket/stable") + self.requires("tket/1.3.0@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") diff --git a/tket/conanfile.py b/tket/conanfile.py index 8aaa2905f8..cc056cc54d 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.127" + version = "1.3.0" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" From 7a3cf207e78795ed9dd0844e607de31a6d9034ae Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Wed, 22 May 2024 15:15:50 +0100 Subject: [PATCH 32/33] Add OpType.CnRx and OpType.CnRz (#1405) * Add CnRx and CnRz * Update circuit_test.py * re clang format * regen stubs * Update changelog.rst * bump version * Unbump changelog... oops * Update ControlledGates.cpp * split controlled phase gate decomp * Update circuit_test.py * Update OpType.hpp --- pytket/binders/circuit/main.cpp | 8 ++ pytket/binders/passes.cpp | 2 +- pytket/conanfile.py | 2 +- pytket/docs/changelog.rst | 4 + pytket/pytket/_tket/circuit.pyi | 60 +++++++------- pytket/pytket/_tket/passes.pyi | 2 +- pytket/tests/circuit_test.py | 16 ++++ tket/conanfile.py | 2 +- tket/include/tket/Circuit/CircPool.hpp | 4 + .../Gate/GateUnitaryMatrixImplementations.hpp | 4 + tket/include/tket/OpType/OpType.hpp | 14 ++++ .../tket/Transformations/Decomposition.hpp | 2 +- .../tket/Transformations/Replacement.hpp | 3 +- tket/src/Circuit/CircUtils.cpp | 23 +++++- tket/src/Circuit/ControlledGates.cpp | 40 +++++++++ tket/src/Circuit/basic_circ_manip.cpp | 4 + tket/src/Circuit/latex_drawing.cpp | 12 +++ tket/src/Gate/Gate.cpp | 12 ++- tket/src/Gate/GateUnitaryMatrixComposites.cpp | 11 +++ .../Gate/GateUnitaryMatrixVariableQubits.cpp | 8 ++ tket/src/Gate/GateUnitarySparseMatrix.cpp | 6 ++ tket/src/OpType/OpTypeFunctions.cpp | 81 ++++++++++--------- tket/src/OpType/OpTypeInfo.cpp | 2 + tket/src/Transformations/Decomposition.cpp | 3 +- tket/src/Transformations/Replacement.cpp | 12 +++ tket/test/src/Circuit/test_Boxes.cpp | 17 ++-- tket/test/src/Circuit/test_Circ.cpp | 4 + tket/test/src/Ops/test_Ops.cpp | 12 +++ tket/test/src/Passes/test_SynthesiseTK.cpp | 4 + .../src/Simulation/test_CircuitSimulator.cpp | 2 + tket/test/src/test_ControlDecomp.cpp | 4 + tket/test/src/test_Synthesis.cpp | 10 ++- tket/test/src/test_json.cpp | 2 + 33 files changed, 310 insertions(+), 82 deletions(-) diff --git a/pytket/binders/circuit/main.cpp b/pytket/binders/circuit/main.cpp index 2687745a63..2214622f9e 100644 --- a/pytket/binders/circuit/main.cpp +++ b/pytket/binders/circuit/main.cpp @@ -439,10 +439,18 @@ PYBIND11_MODULE(circuit, m) { ":math:`(\\alpha, \\beta) \\mapsto \\mathrm{PhasedX}(\\alpha, \\beta)" "^{\\otimes n}` (n-qubit gate composed of identical PhasedX in " "parallel.") + .value( + "CnRx", OpType::CnRx, + ":math:`(\\alpha)` := n-controlled " + ":math:`\\mathrm{Rx}(\\alpha)` gate.") .value( "CnRy", OpType::CnRy, ":math:`(\\alpha)` := n-controlled " ":math:`\\mathrm{Ry}(\\alpha)` gate.") + .value( + "CnRz", OpType::CnRz, + ":math:`(\\alpha)` := n-controlled " + ":math:`\\mathrm{Rz}(\\alpha)` gate.") .value("CnX", OpType::CnX, "n-controlled X gate.") .value("CnY", OpType::CnY, "n-controlled Y gate.") .value("CnZ", OpType::CnZ, "n-controlled Z gate.") diff --git a/pytket/binders/passes.cpp b/pytket/binders/passes.cpp index bd621e0cae..8bed732bc1 100644 --- a/pytket/binders/passes.cpp +++ b/pytket/binders/passes.cpp @@ -453,7 +453,7 @@ PYBIND11_MODULE(passes, m) { m.def( "DecomposeArbitrarilyControlledGates", &DecomposeArbitrarilyControlledGates, - "Decomposes CCX, CnX, CnY, CnZ, and CnRy gates into " + "Decomposes CCX, CnX, CnY, CnZ, CnRy, CnRz and CnRx gates into " "CX and single-qubit gates."); m.def( "DecomposeBoxes", &DecomposeBoxes, diff --git a/pytket/conanfile.py b/pytket/conanfile.py index dde43e56cd..04ceca9ea9 100644 --- a/pytket/conanfile.py +++ b/pytket/conanfile.py @@ -32,7 +32,7 @@ def package(self): cmake.install() def requirements(self): - self.requires("tket/1.3.0@tket/stable") + self.requires("tket/1.3.1@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.4@tket/stable") diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 70f385eb4d..f8a09210c6 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -4,6 +4,10 @@ Changelog Unreleased ---------- +Features: + +* Add ``OpType.CnRx`` and ``OpType.CnRz``. + Fixes: * Allow barriers when dagger or transpose a circuit. diff --git a/pytket/pytket/_tket/circuit.pyi b/pytket/pytket/_tket/circuit.pyi index 163ac46444..eb0e7f8de3 100644 --- a/pytket/pytket/_tket/circuit.pyi +++ b/pytket/pytket/_tket/circuit.pyi @@ -3250,8 +3250,12 @@ class OpType: NPhasedX : :math:`(\\alpha, \\beta) \\mapsto \\mathrm{PhasedX}(\\alpha, \\beta)^{\\otimes n}` (n-qubit gate composed of identical PhasedX in parallel. + CnRx : :math:`(\\alpha)` := n-controlled :math:`\\mathrm{Rx}(\\alpha)` gate. + CnRy : :math:`(\\alpha)` := n-controlled :math:`\\mathrm{Ry}(\\alpha)` gate. + CnRz : :math:`(\\alpha)` := n-controlled :math:`\\mathrm{Rz}(\\alpha)` gate. + CnX : n-controlled X gate. CnY : n-controlled Y gate. @@ -3319,22 +3323,24 @@ class OpType: CX: typing.ClassVar[OpType] # value = CY: typing.ClassVar[OpType] # value = CZ: typing.ClassVar[OpType] # value = - CircBox: typing.ClassVar[OpType] # value = - ClassicalExpBox: typing.ClassVar[OpType] # value = + CircBox: typing.ClassVar[OpType] # value = + ClassicalExpBox: typing.ClassVar[OpType] # value = ClassicalTransform: typing.ClassVar[OpType] # value = + CnRx: typing.ClassVar[OpType] # value = CnRy: typing.ClassVar[OpType] # value = - CnX: typing.ClassVar[OpType] # value = - CnY: typing.ClassVar[OpType] # value = - CnZ: typing.ClassVar[OpType] # value = - Conditional: typing.ClassVar[OpType] # value = - ConjugationBox: typing.ClassVar[OpType] # value = + CnRz: typing.ClassVar[OpType] # value = + CnX: typing.ClassVar[OpType] # value = + CnY: typing.ClassVar[OpType] # value = + CnZ: typing.ClassVar[OpType] # value = + Conditional: typing.ClassVar[OpType] # value = + ConjugationBox: typing.ClassVar[OpType] # value = CopyBits: typing.ClassVar[OpType] # value = - CustomGate: typing.ClassVar[OpType] # value = - DiagonalBox: typing.ClassVar[OpType] # value = - DummyBox: typing.ClassVar[OpType] # value = + CustomGate: typing.ClassVar[OpType] # value = + DiagonalBox: typing.ClassVar[OpType] # value = + DummyBox: typing.ClassVar[OpType] # value = ECR: typing.ClassVar[OpType] # value = ESWAP: typing.ClassVar[OpType] # value = - ExpBox: typing.ClassVar[OpType] # value = + ExpBox: typing.ClassVar[OpType] # value = ExplicitModifier: typing.ClassVar[OpType] # value = ExplicitPredicate: typing.ClassVar[OpType] # value = FSim: typing.ClassVar[OpType] # value = @@ -3347,19 +3353,19 @@ class OpType: Label: typing.ClassVar[OpType] # value = Measure: typing.ClassVar[OpType] # value = MultiBit: typing.ClassVar[OpType] # value = - MultiplexedRotationBox: typing.ClassVar[OpType] # value = - MultiplexedTensoredU2Box: typing.ClassVar[OpType] # value = - MultiplexedU2Box: typing.ClassVar[OpType] # value = - MultiplexorBox: typing.ClassVar[OpType] # value = + MultiplexedRotationBox: typing.ClassVar[OpType] # value = + MultiplexedTensoredU2Box: typing.ClassVar[OpType] # value = + MultiplexedU2Box: typing.ClassVar[OpType] # value = + MultiplexorBox: typing.ClassVar[OpType] # value = NPhasedX: typing.ClassVar[OpType] # value = - PauliExpBox: typing.ClassVar[OpType] # value = - PauliExpCommutingSetBox: typing.ClassVar[OpType] # value = - PauliExpPairBox: typing.ClassVar[OpType] # value = + PauliExpBox: typing.ClassVar[OpType] # value = + PauliExpCommutingSetBox: typing.ClassVar[OpType] # value = + PauliExpPairBox: typing.ClassVar[OpType] # value = Phase: typing.ClassVar[OpType] # value = - PhasePolyBox: typing.ClassVar[OpType] # value = + PhasePolyBox: typing.ClassVar[OpType] # value = PhasedISWAP: typing.ClassVar[OpType] # value = PhasedX: typing.ClassVar[OpType] # value = - QControlBox: typing.ClassVar[OpType] # value = + QControlBox: typing.ClassVar[OpType] # value = RangePredicate: typing.ClassVar[OpType] # value = Reset: typing.ClassVar[OpType] # value = Rx: typing.ClassVar[OpType] # value = @@ -3371,21 +3377,21 @@ class OpType: SXdg: typing.ClassVar[OpType] # value = Sdg: typing.ClassVar[OpType] # value = SetBits: typing.ClassVar[OpType] # value = - StatePreparationBox: typing.ClassVar[OpType] # value = + StatePreparationBox: typing.ClassVar[OpType] # value = Stop: typing.ClassVar[OpType] # value = Sycamore: typing.ClassVar[OpType] # value = T: typing.ClassVar[OpType] # value = TK1: typing.ClassVar[OpType] # value = TK2: typing.ClassVar[OpType] # value = Tdg: typing.ClassVar[OpType] # value = - TermSequenceBox: typing.ClassVar[OpType] # value = - ToffoliBox: typing.ClassVar[OpType] # value = + TermSequenceBox: typing.ClassVar[OpType] # value = + ToffoliBox: typing.ClassVar[OpType] # value = U1: typing.ClassVar[OpType] # value = U2: typing.ClassVar[OpType] # value = U3: typing.ClassVar[OpType] # value = - Unitary1qBox: typing.ClassVar[OpType] # value = - Unitary2qBox: typing.ClassVar[OpType] # value = - Unitary3qBox: typing.ClassVar[OpType] # value = + Unitary1qBox: typing.ClassVar[OpType] # value = + Unitary2qBox: typing.ClassVar[OpType] # value = + Unitary3qBox: typing.ClassVar[OpType] # value = V: typing.ClassVar[OpType] # value = Vdg: typing.ClassVar[OpType] # value = WASM: typing.ClassVar[OpType] # value = @@ -3397,7 +3403,7 @@ class OpType: Z: typing.ClassVar[OpType] # value = ZZMax: typing.ClassVar[OpType] # value = ZZPhase: typing.ClassVar[OpType] # value = - __members__: typing.ClassVar[dict[str, OpType]] # value = {'Phase': , 'Z': , 'X': , 'Y': , 'S': , 'Sdg': , 'T': , 'Tdg': , 'V': , 'Vdg': , 'SX': , 'SXdg': , 'H': , 'Rx': , 'Ry': , 'Rz': , 'U1': , 'U2': , 'U3': , 'GPI': , 'GPI2': , 'AAMS': , 'TK1': , 'TK2': , 'CX': , 'CY': , 'CZ': , 'CH': , 'CV': , 'CVdg': , 'CSX': , 'CSXdg': , 'CS': , 'CSdg': , 'CRz': , 'CRx': , 'CRy': , 'CU1': , 'CU3': , 'CCX': , 'ECR': , 'SWAP': , 'CSWAP': , 'noop': , 'Barrier': , 'Label': , 'Branch': , 'Goto': , 'Stop': , 'BRIDGE': , 'Measure': , 'Reset': , 'CircBox': , 'PhasePolyBox': , 'Unitary1qBox': , 'Unitary2qBox': , 'Unitary3qBox': , 'ExpBox': , 'PauliExpBox': , 'PauliExpPairBox': , 'PauliExpCommutingSetBox': , 'TermSequenceBox': , 'QControlBox': , 'ToffoliBox': , 'ConjugationBox': , 'DummyBox': , 'CustomGate': , 'Conditional': , 'ISWAP': , 'PhasedISWAP': , 'XXPhase': , 'YYPhase': , 'ZZPhase': , 'XXPhase3': , 'PhasedX': , 'NPhasedX': , 'CnRy': , 'CnX': , 'CnY': , 'CnZ': , 'ZZMax': , 'ESWAP': , 'FSim': , 'Sycamore': , 'ISWAPMax': , 'ClassicalTransform': , 'WASM': , 'SetBits': , 'CopyBits': , 'RangePredicate': , 'ExplicitPredicate': , 'ExplicitModifier': , 'MultiBit': , 'ClassicalExpBox': , 'MultiplexorBox': , 'MultiplexedRotationBox': , 'MultiplexedU2Box': , 'MultiplexedTensoredU2Box': , 'StatePreparationBox': , 'DiagonalBox': } + __members__: typing.ClassVar[dict[str, OpType]] # value = {'Phase': , 'Z': , 'X': , 'Y': , 'S': , 'Sdg': , 'T': , 'Tdg': , 'V': , 'Vdg': , 'SX': , 'SXdg': , 'H': , 'Rx': , 'Ry': , 'Rz': , 'U1': , 'U2': , 'U3': , 'GPI': , 'GPI2': , 'AAMS': , 'TK1': , 'TK2': , 'CX': , 'CY': , 'CZ': , 'CH': , 'CV': , 'CVdg': , 'CSX': , 'CSXdg': , 'CS': , 'CSdg': , 'CRz': , 'CRx': , 'CRy': , 'CU1': , 'CU3': , 'CCX': , 'ECR': , 'SWAP': , 'CSWAP': , 'noop': , 'Barrier': , 'Label': , 'Branch': , 'Goto': , 'Stop': , 'BRIDGE': , 'Measure': , 'Reset': , 'CircBox': , 'PhasePolyBox': , 'Unitary1qBox': , 'Unitary2qBox': , 'Unitary3qBox': , 'ExpBox': , 'PauliExpBox': , 'PauliExpPairBox': , 'PauliExpCommutingSetBox': , 'TermSequenceBox': , 'QControlBox': , 'ToffoliBox': , 'ConjugationBox': , 'DummyBox': , 'CustomGate': , 'Conditional': , 'ISWAP': , 'PhasedISWAP': , 'XXPhase': , 'YYPhase': , 'ZZPhase': , 'XXPhase3': , 'PhasedX': , 'NPhasedX': , 'CnRx': , 'CnRy': , 'CnRz': , 'CnX': , 'CnY': , 'CnZ': , 'ZZMax': , 'ESWAP': , 'FSim': , 'Sycamore': , 'ISWAPMax': , 'ClassicalTransform': , 'WASM': , 'SetBits': , 'CopyBits': , 'RangePredicate': , 'ExplicitPredicate': , 'ExplicitModifier': , 'MultiBit': , 'ClassicalExpBox': , 'MultiplexorBox': , 'MultiplexedRotationBox': , 'MultiplexedU2Box': , 'MultiplexedTensoredU2Box': , 'StatePreparationBox': , 'DiagonalBox': } noop: typing.ClassVar[OpType] # value = @staticmethod def from_name(arg0: str) -> OpType: diff --git a/pytket/pytket/_tket/passes.pyi b/pytket/pytket/_tket/passes.pyi index a73bbee83c..e945877d50 100644 --- a/pytket/pytket/_tket/passes.pyi +++ b/pytket/pytket/_tket/passes.pyi @@ -291,7 +291,7 @@ def CustomRoutingPass(arc: pytket._tket.architecture.Architecture, config: typin """ def DecomposeArbitrarilyControlledGates() -> BasePass: """ - Decomposes CCX, CnX, CnY, CnZ, and CnRy gates into CX and single-qubit gates. + Decomposes CCX, CnX, CnY, CnZ, CnRy, CnRz and CnRx gates into CX and single-qubit gates. """ def DecomposeBoxes(excluded_types: set[pytket._tket.circuit.OpType] = set(), excluded_opgroups: set[str] = set()) -> BasePass: """ diff --git a/pytket/tests/circuit_test.py b/pytket/tests/circuit_test.py index 2c400b205e..614e34e083 100644 --- a/pytket/tests/circuit_test.py +++ b/pytket/tests/circuit_test.py @@ -1536,6 +1536,21 @@ def test_pickle_bit() -> None: assert b == pickle.loads(pickle.dumps(b)) +def test_cnrx_cnrz() -> None: + c1rx = Circuit(2) + c1rx.add_gate(OpType.CnRx, 0.3, [0, 1]) + crx = Circuit(2) + crx.add_gate(OpType.CRx, 0.3, [0, 1]) + + c1rz = Circuit(2) + c1rz.add_gate(OpType.CnRz, 0.3, [0, 1]) + crz = Circuit(2) + crz.add_gate(OpType.CRz, 0.3, [0, 1]) + + assert np.allclose(c1rz.get_unitary(), crz.get_unitary()) + assert np.allclose(c1rx.get_unitary(), crx.get_unitary()) + + if __name__ == "__main__": test_circuit_gen() test_symbolic_ops() @@ -1551,3 +1566,4 @@ def test_pickle_bit() -> None: test_multi_controlled_gates() test_counting_n_qubit_gates() test_pauliexp_pair_box_serialisation() + test_cnrx_cnrz() diff --git a/tket/conanfile.py b/tket/conanfile.py index cc056cc54d..c53a41f49a 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.3.0" + version = "1.3.1" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" diff --git a/tket/include/tket/Circuit/CircPool.hpp b/tket/include/tket/Circuit/CircPool.hpp index 33de57c09e..f84346bf20 100644 --- a/tket/include/tket/Circuit/CircPool.hpp +++ b/tket/include/tket/Circuit/CircPool.hpp @@ -507,6 +507,10 @@ Circuit CnX_gray_decomp(unsigned n); Circuit CnRy_normal_decomp(const Op_ptr op, unsigned arity); +Circuit CnRx_normal_decomp(const Op_ptr op, unsigned arity); + +Circuit CnRz_normal_decomp(const Op_ptr op, unsigned arity); + /** * @brief Given a 2x2 numerical unitary matrix U and the number of control * qubits n return the decomposed CnU gate diff --git a/tket/include/tket/Gate/GateUnitaryMatrixImplementations.hpp b/tket/include/tket/Gate/GateUnitaryMatrixImplementations.hpp index e04e14998d..248599cf1e 100644 --- a/tket/include/tket/Gate/GateUnitaryMatrixImplementations.hpp +++ b/tket/include/tket/Gate/GateUnitaryMatrixImplementations.hpp @@ -117,6 +117,10 @@ struct GateUnitaryMatrixImplementations { // to have a sparse version. static Eigen::MatrixXcd CnRy(unsigned int number_of_qubits, double alpha); + static Eigen::MatrixXcd CnRx(unsigned int number_of_qubits, double alpha); + + static Eigen::MatrixXcd CnRz(unsigned int number_of_qubits, double alpha); + static Eigen::MatrixXcd CnX(unsigned int number_of_qubits); static Eigen::MatrixXcd CnZ(unsigned int number_of_qubits); diff --git a/tket/include/tket/OpType/OpType.hpp b/tket/include/tket/OpType/OpType.hpp index 6be6ed96d5..e1b6c8fa5a 100644 --- a/tket/include/tket/OpType/OpType.hpp +++ b/tket/include/tket/OpType/OpType.hpp @@ -608,6 +608,20 @@ enum class OpType { */ CnRy, + /** + * Multiply-controlled \ref OpType::Rx + * + * The phase parameter is defined modulo \f$ 4 \f$. + */ + CnRx, + + /** + * Multiply-controlled \ref OpType::Rz + * + * The phase parameter is defined modulo \f$ 4 \f$. + */ + CnRz, + /** * Multiply-controlled \ref OpType::X */ diff --git a/tket/include/tket/Transformations/Decomposition.hpp b/tket/include/tket/Transformations/Decomposition.hpp index 91af43f824..ba44f8c172 100644 --- a/tket/include/tket/Transformations/Decomposition.hpp +++ b/tket/include/tket/Transformations/Decomposition.hpp @@ -241,7 +241,7 @@ Transform decomp_CCX(); Transform decomp_controlled_Rys(); // does not use ancillae -// Expects: CCX, CnX, CnY, CnZ, CnRy and any other gates +// Expects: CCX, CnX, CnY, CnZ, CnRy, CnRx, CnRz, and any other gates // returns CX and single-qubit gate + any previous gates Transform decomp_arbitrary_controlled_gates(); diff --git a/tket/include/tket/Transformations/Replacement.hpp b/tket/include/tket/Transformations/Replacement.hpp index 3018387424..f41a4ade07 100644 --- a/tket/include/tket/Transformations/Replacement.hpp +++ b/tket/include/tket/Transformations/Replacement.hpp @@ -54,7 +54,8 @@ Circuit CX_circ_from_multiq(const Op_ptr op); Circuit CX_ZX_circ_from_op(const Op_ptr op); /** - * Replace CnRy, CnX, CnZ, CnY with 2-qubit gates and single qubit gates + * Replace CnRy, CnRx, CnRz, CnX, CnZ, CnY with 2-qubit gates and single qubit + * gates * * @param op operation * @param two_q_type whether rebase 2-q gates to CX or TK2 diff --git a/tket/src/Circuit/CircUtils.cpp b/tket/src/Circuit/CircUtils.cpp index fe23674bc5..c97cb45f26 100644 --- a/tket/src/Circuit/CircUtils.cpp +++ b/tket/src/Circuit/CircUtils.cpp @@ -622,6 +622,8 @@ Circuit with_CX(Gate_ptr op) { #define CNZTYPE(n) (((n) == 2) ? OpType::CZ : OpType::CnZ) #define CNYTYPE(n) (((n) == 2) ? OpType::CY : OpType::CnY) #define CNRYTYPE(n) (((n) == 2) ? OpType::CRy : OpType::CnRy) +#define CNRXTYPE(n) (((n) == 2) ? OpType::CRx : OpType::CnRx) +#define CNRZTYPE(n) (((n) == 2) ? OpType::CRz : OpType::CnRz) /** * Construct a circuit representing CnU1. */ @@ -692,12 +694,13 @@ static Circuit with_controls_symbolic(const Circuit &c, unsigned n_controls) { } static const OpTypeSet multiq_gate_set = { - OpType::CX, OpType::CCX, OpType::CnX, OpType::CRy, OpType::CnRy, - OpType::CZ, OpType::CnZ, OpType::CY, OpType::CnY}; + OpType::CX, OpType::CCX, OpType::CnX, OpType::CRy, + OpType::CnRy, OpType::CZ, OpType::CnZ, OpType::CY, + OpType::CnY, OpType::CnRx, OpType::CnRz}; unsigned c_n_qubits = c1.n_qubits(); - // 1. Rebase to {CX, CCX, CnX, CnRy} and single-qubit gates + // 1. Rebase to {CX, CCX, CnX, CnRy, CnRx, CnRz} and single-qubit gates VertexList bin; BGL_FORALL_VERTICES(v, c1.dag, DAG) { Op_ptr op = c1.get_Op_ptr_from_Vertex(v); @@ -778,6 +781,16 @@ static Circuit with_controls_symbolic(const Circuit &c, unsigned n_controls) { case OpType::CnRy: c2.add_op(CNRYTYPE(n_new_args), params, new_args); break; + case OpType::Rx: + case OpType::CRx: + case OpType::CnRx: + c2.add_op(CNRXTYPE(n_new_args), params, new_args); + break; + case OpType::Rz: + case OpType::CRz: + case OpType::CnRz: + c2.add_op(CNRZTYPE(n_new_args), params, new_args); + break; case OpType::Z: case OpType::CZ: case OpType::CnZ: @@ -843,6 +856,7 @@ static Eigen::Matrix2cd get_target_op_matrix(const Op_ptr &op) { return Gate(OpType::V, {}, 1).get_unitary(); case OpType::CVdg: return Gate(OpType::Vdg, {}, 1).get_unitary(); + case OpType::CnRx: case OpType::CRx: return Gate(OpType::Rx, op->get_params(), 1).get_unitary(); case OpType::CnRy: @@ -851,6 +865,7 @@ static Eigen::Matrix2cd get_target_op_matrix(const Op_ptr &op) { case OpType::CY: case OpType::CnY: return Gate(OpType::Y, {}, 1).get_unitary(); + case OpType::CnRz: case OpType::CRz: return Gate(OpType::Rz, op->get_params(), 1).get_unitary(); case OpType::CZ: @@ -1248,6 +1263,8 @@ Circuit with_controls(const Circuit &c, unsigned n_controls) { #undef CNZTYPE #undef CNYTYPE #undef CNRYTYPE +#undef CNRXTYPE +#undef CNRZTYPE std::tuple, Circuit> normalise_TK2_angles( Expr a, Expr b, Expr c) { diff --git a/tket/src/Circuit/ControlledGates.cpp b/tket/src/Circuit/ControlledGates.cpp index f06790914f..f1f648d7a4 100644 --- a/tket/src/Circuit/ControlledGates.cpp +++ b/tket/src/Circuit/ControlledGates.cpp @@ -804,6 +804,46 @@ Circuit CnRy_normal_decomp(const Op_ptr op, unsigned arity) { return rep; } +Circuit CnRz_normal_decomp(const Op_ptr op, unsigned arity) { + if (op->get_type() != OpType::CnRz) { + throw CircuitInvalidity("Operation not CnRz"); + } + OpDesc desc = op->get_desc(); + Expr angle = op->get_params()[0]; + Circuit cnry_circuit = + CnRy_normal_decomp(get_op_ptr(OpType::CnRy, angle), arity); + TKET_ASSERT(cnry_circuit.n_qubits() == arity); + // The target to the CnRy gate will be to the qubit indexed as arity-1 + // Therefore we add basis change Clifford gates to this qubit + Circuit rep(arity); + + rep.add_op(OpType::H, {arity - 1}); + rep.add_op(OpType::S, {arity - 1}); + rep.append(cnry_circuit); + rep.add_op(OpType::Sdg, {arity - 1}); + rep.add_op(OpType::H, {arity - 1}); + return rep; +} + +Circuit CnRx_normal_decomp(const Op_ptr op, unsigned arity) { + if (op->get_type() != OpType::CnRx) { + throw CircuitInvalidity("Operation not CnR"); + } + OpDesc desc = op->get_desc(); + Expr angle = op->get_params()[0]; + Circuit cnry_circuit = + CnRy_normal_decomp(get_op_ptr(OpType::CnRy, angle), arity); + TKET_ASSERT(cnry_circuit.n_qubits() == arity); + // The target to the CnRy gate will be to the qubit indexed as arity-1 + // Therefore we add basis change Clifford gates to this qubit + Circuit rep(arity); + + rep.add_op(OpType::S, {arity - 1}); + rep.append(cnry_circuit); + rep.add_op(OpType::Sdg, {arity - 1}); + return rep; +} + // decompose CnX gate using lemma 7.1 // `n` = no. of controls Circuit CnX_gray_decomp(unsigned n) { diff --git a/tket/src/Circuit/basic_circ_manip.cpp b/tket/src/Circuit/basic_circ_manip.cpp index d00d39f929..49749eb4a1 100644 --- a/tket/src/Circuit/basic_circ_manip.cpp +++ b/tket/src/Circuit/basic_circ_manip.cpp @@ -108,6 +108,10 @@ Vertex Circuit::add_op( } if (optype == OpType::CnRy && args.size() == 1) { return add_op(get_op_ptr(OpType::Ry, gate->get_params()), arg_ids); + } else if (optype == OpType::CnRx && args.size() == 1) { + return add_op(get_op_ptr(OpType::Rx, gate->get_params()), arg_ids); + } else if (optype == OpType::CnRz && args.size() == 1) { + return add_op(get_op_ptr(OpType::Rz, gate->get_params()), arg_ids); } else if (optype == OpType::CnX && args.size() == 1) { return add_op(get_op_ptr(OpType::X), arg_ids); } else if (optype == OpType::CnZ && args.size() == 1) { diff --git a/tket/src/Circuit/latex_drawing.cpp b/tket/src/Circuit/latex_drawing.cpp index 56fab702bc..2949db930f 100644 --- a/tket/src/Circuit/latex_drawing.cpp +++ b/tket/src/Circuit/latex_drawing.cpp @@ -37,6 +37,8 @@ void add_latex_for_command(LatexContext& context, const Command& command) { unit_vector_t args = command.get_args(); const Op_ptr op = command.get_op_ptr(); switch (op->get_type()) { + case OpType::CnRz: + case OpType::CnRx: case OpType::CnRy: case OpType::CnX: case OpType::CnY: @@ -54,6 +56,16 @@ void add_latex_for_command(LatexContext& context, const Command& command) { << "\\gate{\\text{" << get_op_ptr(OpType::Ry, op->get_params())->get_name(true) << "}} & "; + } else if (op->get_type() == OpType::CnRx) { + lines.at(target_index).buffer + << "\\gate{\\text{" + << get_op_ptr(OpType::Rx, op->get_params())->get_name(true) + << "}} & "; + } else if (op->get_type() == OpType::CnRz) { + lines.at(target_index).buffer + << "\\gate{\\text{" + << get_op_ptr(OpType::Rz, op->get_params())->get_name(true) + << "}} & "; } else if (op->get_type() == OpType::CnX) { lines.at(target_index).buffer << "\\targ{} & "; } else if (op->get_type() == OpType::CnY) { diff --git a/tket/src/Gate/Gate.cpp b/tket/src/Gate/Gate.cpp index 6dc93da255..5c6f24958d 100644 --- a/tket/src/Gate/Gate.cpp +++ b/tket/src/Gate/Gate.cpp @@ -110,6 +110,8 @@ Op_ptr Gate::dagger() const { case OpType::Rx: case OpType::PhaseGadget: case OpType::CnRy: + case OpType::CnRx: + case OpType::CnRz: case OpType::XXPhase: case OpType::YYPhase: case OpType::ZZPhase: @@ -215,6 +217,8 @@ Op_ptr Gate::transpose() const { case OpType::SXdg: case OpType::CRz: case OpType::CRx: + case OpType::CnRz: + case OpType::CnRx: case OpType::CU1: case OpType::U1: case OpType::Rz: @@ -438,7 +442,9 @@ std::optional Gate::is_identity() const { case OpType::CRy: case OpType::PhaseGadget: case OpType::ISWAP: - case OpType::CnRy: { + case OpType::CnRy: + case OpType::CnRx: + case OpType::CnRz: { return equiv_0(params[0], 4) ? 0. : notid; } case OpType::FSim: { @@ -575,6 +581,8 @@ bool Gate::has_symmetry(unsigned port1, unsigned port2) const { // n (+1) qubit gates case OpType::CnX: case OpType::CnY: + case OpType::CnRx: + case OpType::CnRz: case OpType::CnRy: { // symmetry on first n ports not on n+1 auto last_port = n_q - 1; @@ -863,6 +871,7 @@ std::optional Gate::commuting_basis(unsigned i) const { } case OpType::CZ: case OpType::CRz: + case OpType::CnRz: case OpType::CS: case OpType::CSdg: case OpType::CU1: @@ -901,6 +910,7 @@ std::optional Gate::commuting_basis(unsigned i) const { case OpType::CSX: case OpType::CSXdg: case OpType::CRx: + case OpType::CnRx: case OpType::CX: case OpType::CCX: case OpType::CnX: { diff --git a/tket/src/Gate/GateUnitaryMatrixComposites.cpp b/tket/src/Gate/GateUnitaryMatrixComposites.cpp index 1ab23a34de..e88736ce15 100644 --- a/tket/src/Gate/GateUnitaryMatrixComposites.cpp +++ b/tket/src/Gate/GateUnitaryMatrixComposites.cpp @@ -105,6 +105,17 @@ Eigen::MatrixXcd GateUnitaryMatrixImplementations::CnRy( Ry(alpha), number_of_qubits); } +Eigen::MatrixXcd GateUnitaryMatrixImplementations::CnRx( + unsigned int number_of_qubits, double alpha) { + return GateUnitaryMatrixUtils::get_multi_controlled_gate_dense_unitary( + Rx(alpha), number_of_qubits); +} +Eigen::MatrixXcd GateUnitaryMatrixImplementations::CnRz( + unsigned int number_of_qubits, double alpha) { + return GateUnitaryMatrixUtils::get_multi_controlled_gate_dense_unitary( + Rz(alpha), number_of_qubits); +} + Eigen::MatrixXcd GateUnitaryMatrixImplementations::CnX( unsigned int number_of_qubits) { return GateUnitaryMatrixUtils::get_multi_controlled_gate_dense_unitary( diff --git a/tket/src/Gate/GateUnitaryMatrixVariableQubits.cpp b/tket/src/Gate/GateUnitaryMatrixVariableQubits.cpp index d8902e5e3b..dea7ec852a 100644 --- a/tket/src/Gate/GateUnitaryMatrixVariableQubits.cpp +++ b/tket/src/Gate/GateUnitaryMatrixVariableQubits.cpp @@ -26,6 +26,8 @@ GateUnitaryMatrixVariableQubits::GateUnitaryMatrixVariableQubits( : op_type(op_type_), known_type(true), number_of_parameters(0) { switch (op_type) { case OpType::CnRy: + case OpType::CnRx: + case OpType::CnRz: // Fall through. case OpType::PhaseGadget: number_of_parameters = 1; @@ -71,6 +73,12 @@ Eigen::MatrixXcd GateUnitaryMatrixVariableQubits::get_dense_unitary( if (op_type == OpType::CnRy) { return GateUnitaryMatrixImplementations::CnRy( number_of_qubits, parameters[0]); + } else if (op_type == OpType::CnRx) { + return GateUnitaryMatrixImplementations::CnRx( + number_of_qubits, parameters[0]); + } else if (op_type == OpType::CnRz) { + return GateUnitaryMatrixImplementations::CnRz( + number_of_qubits, parameters[0]); } else { TKET_ASSERT(op_type == OpType::PhaseGadget); return GateUnitaryMatrixImplementations::PhaseGadget( diff --git a/tket/src/Gate/GateUnitarySparseMatrix.cpp b/tket/src/Gate/GateUnitarySparseMatrix.cpp index cf25f6bed2..549d28d51e 100644 --- a/tket/src/Gate/GateUnitarySparseMatrix.cpp +++ b/tket/src/Gate/GateUnitarySparseMatrix.cpp @@ -47,6 +47,12 @@ static OpType get_primitive_type(OpType type_without_controls) { case OpType::CnRy: return OpType::Ry; + case OpType::CnRx: + return OpType::Rx; + + case OpType::CnRz: + return OpType::Rz; + default: return OpType::noop; } diff --git a/tket/src/OpType/OpTypeFunctions.cpp b/tket/src/OpType/OpTypeFunctions.cpp index adb5cae654..5a564c3171 100644 --- a/tket/src/OpType/OpTypeFunctions.cpp +++ b/tket/src/OpType/OpTypeFunctions.cpp @@ -26,23 +26,29 @@ bool find_in_set(const OpType& val, const OpTypeSet& set) { const OpTypeSet& all_gate_types() { static const OpTypeSet optypes{ - OpType::Z, OpType::X, OpType::Y, OpType::S, - OpType::Sdg, OpType::T, OpType::Tdg, OpType::V, - OpType::Vdg, OpType::SX, OpType::SXdg, OpType::H, - OpType::Rx, OpType::Ry, OpType::Rz, OpType::U3, - OpType::U2, OpType::U1, OpType::TK1, OpType::CX, - OpType::CY, OpType::CZ, OpType::CH, OpType::CV, - OpType::CVdg, OpType::CSX, OpType::CSXdg, OpType::CS, - OpType::CSdg, OpType::CRz, OpType::CRx, OpType::CRy, - OpType::CU1, OpType::CU3, OpType::PhaseGadget, OpType::CCX, - OpType::SWAP, OpType::CSWAP, OpType::noop, OpType::Measure, - OpType::Reset, OpType::ECR, OpType::ISWAP, OpType::PhasedX, - OpType::ZZMax, OpType::XXPhase, OpType::YYPhase, OpType::ZZPhase, - OpType::CnRy, OpType::CnX, OpType::CnZ, OpType::CnY, - OpType::BRIDGE, OpType::Collapse, OpType::ESWAP, OpType::FSim, - OpType::Sycamore, OpType::ISWAPMax, OpType::PhasedISWAP, OpType::XXPhase3, - OpType::NPhasedX, OpType::TK2, OpType::Phase, OpType::GPI, - OpType::GPI2, OpType::AAMS}; + OpType::Z, OpType::X, OpType::Y, + OpType::S, OpType::Sdg, OpType::T, + OpType::Tdg, OpType::V, OpType::Vdg, + OpType::SX, OpType::SXdg, OpType::H, + OpType::Rx, OpType::Ry, OpType::Rz, + OpType::U3, OpType::U2, OpType::U1, + OpType::TK1, OpType::CX, OpType::CY, + OpType::CZ, OpType::CH, OpType::CV, + OpType::CVdg, OpType::CSX, OpType::CSXdg, + OpType::CS, OpType::CSdg, OpType::CRz, + OpType::CRx, OpType::CRy, OpType::CU1, + OpType::CU3, OpType::PhaseGadget, OpType::CCX, + OpType::SWAP, OpType::CSWAP, OpType::noop, + OpType::Measure, OpType::Reset, OpType::ECR, + OpType::ISWAP, OpType::PhasedX, OpType::ZZMax, + OpType::XXPhase, OpType::YYPhase, OpType::ZZPhase, + OpType::CnRx, OpType::CnRz, OpType::CnRy, + OpType::CnX, OpType::CnZ, OpType::CnY, + OpType::BRIDGE, OpType::Collapse, OpType::ESWAP, + OpType::FSim, OpType::Sycamore, OpType::ISWAPMax, + OpType::PhasedISWAP, OpType::XXPhase3, OpType::NPhasedX, + OpType::TK2, OpType::Phase, OpType::GPI, + OpType::GPI2, OpType::AAMS}; static std::unique_ptr gates = std::make_unique(optypes); return *gates; @@ -50,19 +56,20 @@ const OpTypeSet& all_gate_types() { const OpTypeSet& all_multi_qubit_types() { static const OpTypeSet optypes{ - OpType::CX, OpType::CY, OpType::CZ, - OpType::CH, OpType::CV, OpType::CVdg, - OpType::CSX, OpType::CSXdg, OpType::CS, - OpType::CSdg, OpType::CRz, OpType::CRx, - OpType::CRy, OpType::CU1, OpType::CU3, - OpType::PhaseGadget, OpType::CCX, OpType::SWAP, - OpType::CSWAP, OpType::ECR, OpType::ISWAP, - OpType::ZZMax, OpType::XXPhase, OpType::YYPhase, - OpType::ZZPhase, OpType::CnRy, OpType::CnX, - OpType::CnZ, OpType::CnY, OpType::BRIDGE, - OpType::ESWAP, OpType::FSim, OpType::Sycamore, - OpType::ISWAPMax, OpType::PhasedISWAP, OpType::XXPhase3, - OpType::NPhasedX, OpType::TK2, OpType::AAMS}; + OpType::CX, OpType::CY, OpType::CZ, + OpType::CH, OpType::CV, OpType::CVdg, + OpType::CSX, OpType::CSXdg, OpType::CS, + OpType::CSdg, OpType::CRz, OpType::CRx, + OpType::CRy, OpType::CU1, OpType::CU3, + OpType::PhaseGadget, OpType::CCX, OpType::SWAP, + OpType::CSWAP, OpType::ECR, OpType::ISWAP, + OpType::ZZMax, OpType::XXPhase, OpType::YYPhase, + OpType::ZZPhase, OpType::CnRx, OpType::CnRz, + OpType::CnRy, OpType::CnX, OpType::CnZ, + OpType::CnY, OpType::BRIDGE, OpType::ESWAP, + OpType::FSim, OpType::Sycamore, OpType::ISWAPMax, + OpType::PhasedISWAP, OpType::XXPhase3, OpType::NPhasedX, + OpType::TK2, OpType::AAMS}; static std::unique_ptr gates = std::make_unique(optypes); return *gates; @@ -120,8 +127,9 @@ const OpTypeSet& all_controlled_gate_types() { static const OpTypeSet optypes{ OpType::CX, OpType::CCX, OpType::CnX, OpType::CnZ, OpType::CnY, OpType::CSX, OpType::CSXdg, OpType::CS, OpType::CSdg, OpType::CV, - OpType::CVdg, OpType::CRx, OpType::CnRy, OpType::CRy, OpType::CY, - OpType::CRz, OpType::CZ, OpType::CH, OpType::CU1, OpType::CU3}; + OpType::CVdg, OpType::CRx, OpType::CnRx, OpType::CnRz, OpType::CnRy, + OpType::CRy, OpType::CY, OpType::CRz, OpType::CZ, OpType::CH, + OpType::CU1, OpType::CU3}; static std::unique_ptr gates = std::make_unique(optypes); return *gates; @@ -214,10 +222,11 @@ bool is_flowop_type(OpType optype) { bool is_rotation_type(OpType optype) { static const OpTypeSet rotation_gates = { - OpType::Rx, OpType::Ry, OpType::Rz, OpType::U1, - OpType::CnRy, OpType::CRz, OpType::CRx, OpType::CRy, - OpType::CU1, OpType::XXPhase, OpType::YYPhase, OpType::ZZPhase, - OpType::ESWAP, OpType::ISWAP, OpType::XXPhase3}; + OpType::Rx, OpType::Ry, OpType::Rz, OpType::U1, + OpType::CnRy, OpType::CnRx, OpType::CnRz, OpType::CRz, + OpType::CRx, OpType::CRy, OpType::CU1, OpType::XXPhase, + OpType::YYPhase, OpType::ZZPhase, OpType::ESWAP, OpType::ISWAP, + OpType::XXPhase3}; return find_in_set(optype, rotation_gates); } diff --git a/tket/src/OpType/OpTypeInfo.cpp b/tket/src/OpType/OpTypeInfo.cpp index a2605edd20..19e292bb70 100644 --- a/tket/src/OpType/OpTypeInfo.cpp +++ b/tket/src/OpType/OpTypeInfo.cpp @@ -123,6 +123,8 @@ const std::map& optypeinfo() { {OpType::XXPhase3, {"XXPhase3", "$R_{X_0X_1}R_{X_0X_2}R_{X_1X_2}$", {4}, tripleq}}, {OpType::CnRy, {"CnRy", "CnRy", {4}, std::nullopt}}, + {OpType::CnRx, {"CnRx", "CnRx", {4}, std::nullopt}}, + {OpType::CnRz, {"CnRz", "CnRz", {4}, std::nullopt}}, {OpType::CnX, {"CnX", "CnX", {}, std::nullopt}}, {OpType::CnZ, {"CnZ", "CnZ", {}, std::nullopt}}, {OpType::CnY, {"CnY", "CnY", {}, std::nullopt}}, diff --git a/tket/src/Transformations/Decomposition.cpp b/tket/src/Transformations/Decomposition.cpp index 22a25376d1..9e431ffb24 100644 --- a/tket/src/Transformations/Decomposition.cpp +++ b/tket/src/Transformations/Decomposition.cpp @@ -1805,7 +1805,8 @@ Transform decomp_controlled_Rys() { Transform decomp_arbitrary_controlled_gates() { static const std::set cn_gate_set = { - OpType::CCX, OpType::CnX, OpType::CnRy, OpType::CnZ, OpType::CnY}; + OpType::CCX, OpType::CnX, OpType::CnRy, OpType::CnZ, + OpType::CnY, OpType::CnRx, OpType::CnRz}; std::set all_gates; std::copy( all_gate_types().begin(), all_gate_types().end(), diff --git a/tket/src/Transformations/Replacement.cpp b/tket/src/Transformations/Replacement.cpp index ceda73f655..8c44674d7f 100644 --- a/tket/src/Transformations/Replacement.cpp +++ b/tket/src/Transformations/Replacement.cpp @@ -37,6 +37,12 @@ Circuit multi_controlled_to_2q( case OpType::CnRy: c = CircPool::CnRy_normal_decomp(op, n_qubits); break; + case OpType::CnRx: + c = CircPool::CnRx_normal_decomp(op, n_qubits); + break; + case OpType::CnRz: + c = CircPool::CnRz_normal_decomp(op, n_qubits); + break; case OpType::CnX: case OpType::CnZ: case OpType::CnY: @@ -87,6 +93,8 @@ Circuit TK2_circ_from_multiq(const Op_ptr op) { "Can only build replacement circuits for basic gates", desc.type()); switch (desc.type()) { case OpType::CnRy: + case OpType::CnRx: + case OpType::CnRz: case OpType::CnX: case OpType::CnZ: case OpType::CnY: @@ -110,6 +118,8 @@ Circuit CX_circ_from_multiq(const Op_ptr op) { "Can only build replacement circuits for basic gates", desc.type()); switch (desc.type()) { case OpType::CnRy: + case OpType::CnRx: + case OpType::CnRz: case OpType::CnX: case OpType::CnZ: case OpType::CnY: @@ -288,6 +298,8 @@ Circuit CX_ZX_circ_from_op(const Op_ptr op) { case OpType::ZZPhase: case OpType::YYPhase: case OpType::CnRy: + case OpType::CnRx: + case OpType::CnRz: case OpType::CnX: case OpType::ESWAP: case OpType::FSim: diff --git a/tket/test/src/Circuit/test_Boxes.cpp b/tket/test/src/Circuit/test_Boxes.cpp index bfaed1f186..d60824782e 100644 --- a/tket/test/src/Circuit/test_Boxes.cpp +++ b/tket/test/src/Circuit/test_Boxes.cpp @@ -933,17 +933,22 @@ SCENARIO("QControlBox", "[boxes]") { REQUIRE(*c == d); } - GIVEN("controlled phase_gadget") { + GIVEN("controlled phase_gadget, numerical") { Expr a; - WHEN("numerical") { a = 0.3; } - WHEN("symbolic") { - Sym s = SymEngine::symbol("a"); - a = Expr(s); - } + a = 0.3; QControlBox qbox(get_op_ptr(OpType::PhaseGadget, {a}, 2)); std::shared_ptr c = qbox.to_circuit(); REQUIRE(c->count_gates(OpType::CX) == 4); } + GIVEN("controlled phase_gadget, symbolic") { + Expr a; + Sym s = SymEngine::symbol("a"); + a = Expr(s); + QControlBox qbox(get_op_ptr(OpType::PhaseGadget, {a}, 2)); + std::shared_ptr c = qbox.to_circuit(); + REQUIRE(c->count_gates(OpType::CX) == 2); + REQUIRE(c->count_gates(OpType::CRz) == 1); + } GIVEN("controlled PauliExpBox") { // https://github.com/CQCL/tket/issues/1109 PauliExpBox pbox( diff --git a/tket/test/src/Circuit/test_Circ.cpp b/tket/test/src/Circuit/test_Circ.cpp index 5e96517130..d1724bdd13 100644 --- a/tket/test/src/Circuit/test_Circ.cpp +++ b/tket/test/src/Circuit/test_Circ.cpp @@ -2712,6 +2712,10 @@ SCENARIO("Confirm that LaTeX output compiles", "[latex][.long]") { c.add_conditional_gate(OpType::CnZ, {}, {0, 1, 2, 4, 3}, {}, 0); c.add_conditional_gate( OpType::CnRy, {-0.57}, {0, 3, 2, 4, 1}, {}, 0); + c.add_conditional_gate( + OpType::CnRx, {-0.57}, {0, 3, 2, 4, 1}, {}, 0); + c.add_conditional_gate( + OpType::CnRz, {-0.57}, {0, 3, 2, 4, 1}, {}, 0); c.add_conditional_gate(OpType::CH, {}, {1, 0}, {}, 0); c.add_conditional_gate(OpType::CY, {}, {2, 3}, {}, 0); c.add_conditional_gate(OpType::CRz, {1.42}, {0, 2}, {}, 0); diff --git a/tket/test/src/Ops/test_Ops.cpp b/tket/test/src/Ops/test_Ops.cpp index 6078d78e76..b07654bbd5 100644 --- a/tket/test/src/Ops/test_Ops.cpp +++ b/tket/test/src/Ops/test_Ops.cpp @@ -134,6 +134,16 @@ SCENARIO("Check op retrieval overloads are working correctly.", "[ops]") { CHECK(cnry->get_name() == "CnRy(0.5)"); CHECK(cnry->get_params().size() == 1); REQUIRE(cnry->transpose()->get_params() == rhs); + const Op_ptr cnrx = (get_op_ptr(OpType::CnRx, 0.5)); + + std::vector rhs_same = {Expr(0.5)}; + CHECK(cnrx->get_name() == "CnRx(0.5)"); + CHECK(cnrx->get_params().size() == 1); + REQUIRE(cnrx->transpose()->get_params() == rhs_same); + const Op_ptr cnrz = (get_op_ptr(OpType::CnRz, 0.5)); + CHECK(cnrz->get_name() == "CnRz(0.5)"); + CHECK(cnrz->get_params().size() == 1); + REQUIRE(cnrz->transpose()->get_params() == rhs_same); const Op_ptr xxphase = (get_op_ptr(OpType::XXPhase, 0.5)); CHECK(xxphase->get_name() == "XXPhase(0.5)"); REQUIRE(*xxphase->transpose() == *xxphase); @@ -307,6 +317,8 @@ SCENARIO("Examples for is_singleq_unitary") { } GIVEN("Variable-qubit gates") { REQUIRE(!(get_op_ptr(OpType::CnRy, 0.2))->get_desc().is_singleq_unitary()); + REQUIRE(!(get_op_ptr(OpType::CnRx, 0.2))->get_desc().is_singleq_unitary()); + REQUIRE(!(get_op_ptr(OpType::CnRz, 0.2))->get_desc().is_singleq_unitary()); REQUIRE(!(get_op_ptr(OpType::PhaseGadget, Expr(0.4))) ->get_desc() .is_singleq_unitary()); diff --git a/tket/test/src/Passes/test_SynthesiseTK.cpp b/tket/test/src/Passes/test_SynthesiseTK.cpp index eb49eb03ca..b41c2a1a7d 100644 --- a/tket/test/src/Passes/test_SynthesiseTK.cpp +++ b/tket/test/src/Passes/test_SynthesiseTK.cpp @@ -90,6 +90,8 @@ SCENARIO("SynthesiseTK correctness") { c.add_op(OpType::CCX, {0, 1, 2}); c.add_op(OpType::CnX, {0, 1, 2, 3}); c.add_op(OpType::CnRy, 0.25, {0, 1, 2, 3}); + c.add_op(OpType::CnRx, 0.25, {0, 1, 2, 3}); + c.add_op(OpType::CnRz, 0.25, {0, 1, 2, 3}); c.add_op(OpType::CSWAP, {1, 2, 3}); c.add_op(OpType::BRIDGE, {1, 3, 0}); check_synthesise_tk(c); @@ -119,6 +121,8 @@ SCENARIO("SynthesiseTK correctness") { c.add_op(OpType::PhasedX, {a, a + 0.2}, {0}); c.add_op(OpType::NPhasedX, {a + 0.3, a}, {0, 1, 2}); c.add_op(OpType::CnRy, a - 0.3, {1, 2, 0}); + c.add_op(OpType::CnRx, a - 0.3, {1, 2, 0}); + c.add_op(OpType::CnRz, a - 0.3, {1, 2, 0}); c.add_op(OpType::ESWAP, a, {1, 2}); c.add_op(OpType::FSim, {a + 0.1, a + 0.2}, {2, 0}); check_synthesise_tk(c); diff --git a/tket/test/src/Simulation/test_CircuitSimulator.cpp b/tket/test/src/Simulation/test_CircuitSimulator.cpp index f65bc069d7..a0355b0bf8 100644 --- a/tket/test/src/Simulation/test_CircuitSimulator.cpp +++ b/tket/test/src/Simulation/test_CircuitSimulator.cpp @@ -243,6 +243,8 @@ SCENARIO("Directly simulate circuits with >= 3 qubit gates") { circ.add_op(OpType::BRIDGE, {0, 1, 2}); circ.add_op(OpType::CSWAP, {0, 1, 2}); circ.add_op(OpType::CnRy, 0.1234, {0, 1, 2, 3}); + circ.add_op(OpType::CnRx, 0.1234, {0, 1, 2, 3}); + circ.add_op(OpType::CnRz, 0.1234, {0, 1, 2, 3}); circ.add_op(OpType::CnX, {0, 1, 2, 3}); circ.add_op(OpType::PhaseGadget, 0.1, {0, 1, 2, 3}); const auto u = tket_sim::get_unitary(circ); diff --git a/tket/test/src/test_ControlDecomp.cpp b/tket/test/src/test_ControlDecomp.cpp index 3c61b5cdaf..9200554cd4 100644 --- a/tket/test/src/test_ControlDecomp.cpp +++ b/tket/test/src/test_ControlDecomp.cpp @@ -780,6 +780,8 @@ SCENARIO("Test decomp_arbitrary_controlled_gates") { Circuit circ(3); circ.add_op(OpType::CnRy, 0.33, {0, 1, 2}); + circ.add_op(OpType::CnRx, 0.33, {0, 1, 2}); + circ.add_op(OpType::CnRz, 0.33, {0, 1, 2}); circ.add_op(OpType::CnY, {0, 1, 2}); circ.add_op(OpType::CnZ, {1, 0, 2}); circ.add_op(OpType::CnX, {0, 2, 1}); @@ -787,6 +789,8 @@ SCENARIO("Test decomp_arbitrary_controlled_gates") { auto u = tket_sim::get_unitary(circ); REQUIRE(Transforms::decomp_arbitrary_controlled_gates().apply(circ)); REQUIRE(circ.count_gates(OpType::CnRy) == 0); + REQUIRE(circ.count_gates(OpType::CnRx) == 0); + REQUIRE(circ.count_gates(OpType::CnRz) == 0); REQUIRE(circ.count_gates(OpType::CnY) == 0); REQUIRE(circ.count_gates(OpType::CnZ) == 0); REQUIRE(circ.count_gates(OpType::CnX) == 0); diff --git a/tket/test/src/test_Synthesis.cpp b/tket/test/src/test_Synthesis.cpp index 124d92c85a..6c2138b326 100644 --- a/tket/test/src/test_Synthesis.cpp +++ b/tket/test/src/test_Synthesis.cpp @@ -1646,16 +1646,20 @@ SCENARIO("Test barrier blocks transforms successfully") { GIVEN("Controlled gates with barrier") { Circuit circ(8); circ.add_op(OpType::CnRy, 0.4, {0, 1, 2, 3, 4, 5, 6, 7}); + circ.add_op(OpType::CnRx, 0.4, {0, 1, 2, 3, 4, 5, 6, 7}); + circ.add_op(OpType::CnRz, 0.4, {0, 1, 2, 3, 4, 5, 6, 7}); circ.add_op(OpType::CX, {6, 7}); circ.add_barrier({0, 1, 2, 3}); circ.add_op(OpType::CX, {6, 7}); + circ.add_op(OpType::CnRz, -0.4, {0, 1, 2, 3, 4, 5, 6, 7}); + circ.add_op(OpType::CnRx, -0.4, {0, 1, 2, 3, 4, 5, 6, 7}); circ.add_op(OpType::CnRy, -0.4, {0, 1, 2, 3, 4, 5, 6, 7}); REQUIRE(verify_n_qubits_for_ops(circ)); - REQUIRE(circ.n_gates() == 5); + REQUIRE(circ.n_gates() == 9); REQUIRE(Transforms::remove_redundancies().apply(circ)); REQUIRE(verify_n_qubits_for_ops(circ)); REQUIRE(circ.depth_by_type(OpType::Barrier) == 1); - REQUIRE(circ.n_gates() == 3); // both CXs removed + REQUIRE(circ.n_gates() == 7); // both CXs removed Circuit rep(4); const Op_ptr bar = std::make_shared(op_signature_t(4, EdgeType::Quantum)); @@ -2201,6 +2205,8 @@ SCENARIO("Synthesis with conditional gates") { c.add_measure(1, 1); c.add_conditional_gate(OpType::U1, {0.25}, {1}, {0}, 1); c.add_conditional_gate(OpType::CnRy, {0.25}, {0, 1, 2}, {0, 1}, 0); + c.add_conditional_gate(OpType::CnRx, {0.25}, {0, 1, 2}, {0, 1}, 0); + c.add_conditional_gate(OpType::CnRz, {0.25}, {0, 1, 2}, {0, 1}, 0); c.add_measure(2, 2); check_conditions(SynthesiseOQC(), c); check_conditions(SynthesiseTK(), c); diff --git a/tket/test/src/test_json.cpp b/tket/test/src/test_json.cpp index 177ab0f177..238c1449ae 100644 --- a/tket/test/src/test_json.cpp +++ b/tket/test/src/test_json.cpp @@ -132,6 +132,8 @@ SCENARIO("Test Command serialization") { const Qubit a = Qubit("a", 1, 2); c.add_qubit(a); c.add_op(OpType::CnRy, 0.1, {q[0], a, q[1]}); + c.add_op(OpType::CnRx, 0.1, {q[0], a, q[1]}); + c.add_op(OpType::CnRz, 0.1, {q[0], a, q[1]}); c.add_barrier({q[0], a}); check_cases(c.get_commands()); From 48eaccc4554ea1052bc897c423ab3f8114663f80 Mon Sep 17 00:00:00 2001 From: Silas Dilkes <36165522+sjdilkes@users.noreply.github.com> Date: Thu, 23 May 2024 14:00:21 +0100 Subject: [PATCH 33/33] Update `place_fully_connected` (#1409) * update place_fully_connected * Update placement.cpp * Update placement_test.py --- pytket/binders/placement.cpp | 9 +++++---- pytket/tests/placement_test.py | 9 +++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/pytket/binders/placement.cpp b/pytket/binders/placement.cpp index f89e4543fb..a3107dcb52 100644 --- a/pytket/binders/placement.cpp +++ b/pytket/binders/placement.cpp @@ -39,10 +39,11 @@ void place_fully_connected( "Circuit has more qubits than the FullyConnected graph has nodes"); } std::map qmap; - unsigned index = 0; - for (const Qubit &q : circ.all_qubits()) { - qmap[q] = Node("fcNode", index); - index++; + std::vector qubits = circ.all_qubits(); + std::vector nodes = fully_connected.get_all_nodes_vec(); + TKET_ASSERT(nodes.size() >= qubits.size()); + for (unsigned i = 0; i < qubits.size(); i++) { + qmap[qubits[i]] = nodes[i]; } place_with_map(circ, qmap); } diff --git a/pytket/tests/placement_test.py b/pytket/tests/placement_test.py index 035087b44d..346d1861aa 100644 --- a/pytket/tests/placement_test.py +++ b/pytket/tests/placement_test.py @@ -191,6 +191,15 @@ def test_place_fully_connected() -> None: assert qbs[3].reg_name == "fcNode" assert qbs[4].reg_name == "fcNode" + fc5 = FullyConnected(5, "fcNodetest") + place_fully_connected(c, fc5) + qbs = c.qubits + assert qbs[0].reg_name == "fcNodetest" + assert qbs[1].reg_name == "fcNodetest" + assert qbs[2].reg_name == "fcNodetest" + assert qbs[3].reg_name == "fcNodetest" + assert qbs[4].reg_name == "fcNodetest" + def test_big_placement() -> None: # TKET-1275