From e215e2f906af75ec14e170d82bc9eac1caf1f690 Mon Sep 17 00:00:00 2001 From: Yao Tang Date: Thu, 24 Aug 2023 20:03:00 +0100 Subject: [PATCH 01/10] Add ConjugationBox optype --- tket/include/tket/OpType/OpType.hpp | 5 +++++ tket/src/OpType/OpTypeFunctions.cpp | 1 + tket/src/OpType/OpTypeInfo.cpp | 2 ++ 3 files changed, 8 insertions(+) diff --git a/tket/include/tket/OpType/OpType.hpp b/tket/include/tket/OpType/OpType.hpp index cd756a564d..2998a685d3 100644 --- a/tket/include/tket/OpType/OpType.hpp +++ b/tket/include/tket/OpType/OpType.hpp @@ -660,6 +660,11 @@ enum class OpType { */ DiagonalBox, + /** + * See \ref ConjugationBox + */ + ConjugationBox, + /** * See \ref ClassicalExpBox */ diff --git a/tket/src/OpType/OpTypeFunctions.cpp b/tket/src/OpType/OpTypeFunctions.cpp index 0d52c9c76d..092f2cbcad 100644 --- a/tket/src/OpType/OpTypeFunctions.cpp +++ b/tket/src/OpType/OpTypeFunctions.cpp @@ -181,6 +181,7 @@ bool is_box_type(OpType optype) { OpType::MultiplexedTensoredU2Box, OpType::StatePreparationBox, OpType::DiagonalBox, + OpType::ConjugationBox, OpType::ClassicalExpBox, OpType::ProjectorAssertionBox, OpType::StabiliserAssertionBox, diff --git a/tket/src/OpType/OpTypeInfo.cpp b/tket/src/OpType/OpTypeInfo.cpp index 8241df85a7..18bc15a53d 100644 --- a/tket/src/OpType/OpTypeInfo.cpp +++ b/tket/src/OpType/OpTypeInfo.cpp @@ -147,6 +147,8 @@ const std::map& optypeinfo() { {OpType::StatePreparationBox, {"StatePreparationBox", "StatePreparationBox", {}, std::nullopt}}, {OpType::DiagonalBox, {"DiagonalBox", "DiagonalBox", {}, std::nullopt}}, + {OpType::ConjugationBox, + {"ConjugationBox", "ConjugationBox", {}, std::nullopt}}, {OpType::Conditional, {"Conditional", "If", {}, std::nullopt}}, {OpType::ProjectorAssertionBox, {"ProjectorAssertionBox", "ProjectorAssertionBox", {}, std::nullopt}}, From 758b3ef9a700c4aad5ad328e7259fac1e666ad57 Mon Sep 17 00:00:00 2001 From: Yao Tang Date: Thu, 24 Aug 2023 20:09:19 +0100 Subject: [PATCH 02/10] Implement ConjugationBox --- tket/CMakeLists.txt | 2 + tket/include/tket/Circuit/ConjugationBox.hpp | 82 +++++++++++ tket/src/Circuit/ConjugationBox.cpp | 135 +++++++++++++++++++ 3 files changed, 219 insertions(+) create mode 100644 tket/include/tket/Circuit/ConjugationBox.hpp create mode 100644 tket/src/Circuit/ConjugationBox.cpp diff --git a/tket/CMakeLists.txt b/tket/CMakeLists.txt index 1649df5ccb..0a4bc7e830 100644 --- a/tket/CMakeLists.txt +++ b/tket/CMakeLists.txt @@ -160,6 +160,7 @@ target_sources(tket src/Circuit/DiagonalBox.cpp src/Circuit/ToffoliBox.cpp src/Circuit/PauliExpBoxes.cpp + src/Circuit/ConjugationBox.cpp src/Circuit/Simulation/BitOperations.cpp src/Circuit/Simulation/CircuitSimulator.cpp src/Circuit/Simulation/DecomposeCircuit.cpp @@ -321,6 +322,7 @@ target_sources(tket include/tket/Circuit/ThreeQubitConversion.hpp include/tket/Circuit/ToffoliBox.hpp include/tket/Circuit/PauliExpBoxes.hpp + include/tket/Circuit/ConjugationBox.hpp include/tket/Circuit/Simulation/CircuitSimulator.hpp include/tket/Circuit/Simulation/PauliExpBoxUnitaryCalculator.hpp include/tket/Architecture/Architecture.hpp diff --git a/tket/include/tket/Circuit/ConjugationBox.hpp b/tket/include/tket/Circuit/ConjugationBox.hpp new file mode 100644 index 0000000000..38cb99c4eb --- /dev/null +++ b/tket/include/tket/Circuit/ConjugationBox.hpp @@ -0,0 +1,82 @@ +// Copyright 2019-2023 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 "Boxes.hpp" +#include "Circuit.hpp" +#include "tket/Utils/Json.hpp" + +namespace tket { +/** + * Box to express computations that follow the compute-action-uncompute pattern + */ +class ConjugationBox : public Box { + public: + /** + * @brief Construct a new ConjugationBox object from operations that perform + * compute, action, and uncompute. All three operations need to have the same + * signature. + * + * @param compute the compute operation + * @param action the action operation + * @param uncompute optional uncompute operation, default to compute.dagger(). + * If provided, the user needs to make sure that the unitary matrices of + * uncompute.dagger() and compute must be equal. + */ + explicit ConjugationBox( + const Op_ptr &compute, const Op_ptr &action, + const std::optional uncompute = std::nullopt); + + /** + * Copy constructor + */ + ConjugationBox(const ConjugationBox &other); + ~ConjugationBox() override {} + + Op_ptr symbol_substitution( + const SymEngine::map_basic_basic &) const override { + return Op_ptr(); + } + + SymSet free_symbols() const override { return {}; } + + /** + * Equality check between two ConjugationBox instances + */ + bool is_equal(const Op &op_other) const override; + + Op_ptr dagger() const override; + Op_ptr transpose() const override; + + Op_ptr get_compute() const { return compute_; } + Op_ptr get_action() const { return action_; } + std::optional get_uncompute() const { return uncompute_; } + + static Op_ptr from_json(const nlohmann::json &j); + + static nlohmann::json to_json(const Op_ptr &op); + + protected: + void generate_circuit() const override; + + ConjugationBox() + : Box(OpType::ConjugationBox), compute_(), action_(), uncompute_() {} + + private: + const Op_ptr compute_; + const Op_ptr action_; + const std::optional uncompute_; +}; +} // namespace tket diff --git a/tket/src/Circuit/ConjugationBox.cpp b/tket/src/Circuit/ConjugationBox.cpp new file mode 100644 index 0000000000..293daeefe2 --- /dev/null +++ b/tket/src/Circuit/ConjugationBox.cpp @@ -0,0 +1,135 @@ +// Copyright 2019-2023 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/Circuit/ConjugationBox.hpp" + +#include "tket/Circuit/Circuit.hpp" +#include "tket/Ops/OpJsonFactory.hpp" +#include "tket/Utils/HelperFunctions.hpp" +#include "tket/Utils/Json.hpp" + +namespace tket { + +ConjugationBox::ConjugationBox( + const Op_ptr &compute, const Op_ptr &action, + const std::optional uncompute) + : Box(OpType::ConjugationBox), + compute_(compute), + action_(action), + uncompute_(uncompute) { + op_signature_t compute_sig = compute_->get_signature(); + op_signature_t action_sig = action_->get_signature(); + unsigned compute_size_ = compute_sig.size(); + unsigned action_size_ = action_sig.size(); + unsigned compute_n_qubits_ = + std::count(compute_sig.begin(), compute_sig.end(), EdgeType::Quantum); + unsigned action_n_qubits_ = + std::count(action_sig.begin(), action_sig.end(), EdgeType::Quantum); + unsigned uncompute_size_ = 0, uncompute_n_qubits_ = 0; + if (uncompute_ != std::nullopt) { + op_signature_t uncompute_sig = uncompute_.value()->get_signature(); + uncompute_size_ = uncompute_sig.size(); + uncompute_n_qubits_ = std::count( + uncompute_sig.begin(), uncompute_sig.end(), EdgeType::Quantum); + } + if (compute_size_ != compute_n_qubits_ || action_size_ != action_n_qubits_ || + uncompute_size_ != uncompute_n_qubits_) { + throw std::invalid_argument( + "ConjugationBox only supports quantum operations"); + } + if (compute_size_ != action_size_ || + (uncompute_ != std::nullopt && uncompute_size_ != compute_size_)) { + throw std::invalid_argument( + "Operations provided to ConjugationBox need to have the same number of " + "qubits"); + } + signature_ = op_signature_t(compute_size_, EdgeType::Quantum); +} + +ConjugationBox::ConjugationBox(const ConjugationBox &other) + : Box(other), + compute_(other.compute_), + action_(other.action_), + uncompute_(other.uncompute_) {} + +Op_ptr ConjugationBox::dagger() const { + return std::make_shared( + compute_, action_->dagger(), uncompute_); +} + +Op_ptr ConjugationBox::transpose() const { + return std::make_shared( + (uncompute_ == std::nullopt) ? compute_->dagger()->transpose() + : uncompute_.value()->transpose(), + action_->transpose(), compute_->transpose()); +} + +void ConjugationBox::generate_circuit() const { + Circuit circ(signature_.size()); + std::vector args(circ.n_qubits()); + std::iota(args.begin(), args.end(), 0); + circ.add_op(compute_, args); + circ.add_op(action_, args); + if (uncompute_ != std::nullopt) { + circ.add_op(uncompute_.value(), args); + } else { + circ.add_op(compute_->dagger(), args); + } + circ_ = std::make_shared(circ); +} + +bool ConjugationBox::is_equal(const Op &op_other) const { + const ConjugationBox &other = dynamic_cast(op_other); + if (id_ == other.get_id()) return true; + // if only one of them has uncompute_, compare the uncompute_ with + // the other's compute_.dagger(). + return *compute_ == *other.compute_ && *action_ == *other.action_ && + ((uncompute_ == std::nullopt && other.uncompute_ == std::nullopt) || + (uncompute_ != std::nullopt && other.uncompute_ != std::nullopt && + *uncompute_.value() == *other.uncompute_.value()) || + (uncompute_ == std::nullopt && other.uncompute_ != std::nullopt && + *compute_->dagger() == *other.uncompute_.value()) || + (uncompute_ != std::nullopt && other.uncompute_ == std::nullopt && + *uncompute_.value() == *other.compute_->dagger())); +} + +nlohmann::json ConjugationBox::to_json(const Op_ptr &op) { + const auto &box = static_cast(*op); + nlohmann::json j = core_box_json(box); + j["compute"] = box.get_compute(); + j["action"] = box.get_action(); + // set j["uncompute"] to null + j["uncompute"] = nlohmann::json(); + std::optional uncompute = box.get_uncompute(); + if (uncompute != std::nullopt) { + j["uncompute"] = uncompute.value(); + } + return j; +} + +Op_ptr ConjugationBox::from_json(const nlohmann::json &j) { + std::optional uncompute = std::nullopt; + if (j.contains("uncompute") && !j.at("uncompute").is_null()) { + uncompute = j.at("uncompute").get(); + } + ConjugationBox box = ConjugationBox( + j.at("compute").get(), j.at("action").get(), uncompute); + return set_box_id( + box, + boost::lexical_cast(j.at("id").get())); +} + +REGISTER_OPFACTORY(ConjugationBox, ConjugationBox) + +} // namespace tket From 83560da690648734eaa70a1910ee110ea59a0fd0 Mon Sep 17 00:00:00 2001 From: Yao Tang Date: Thu, 24 Aug 2023 20:43:45 +0100 Subject: [PATCH 03/10] Add tests --- tket/test/CMakeLists.txt | 1 + tket/test/src/Circuit/test_Boxes.cpp | 33 ++++ tket/test/src/Circuit/test_ConjugationBox.cpp | 165 ++++++++++++++++++ tket/test/src/test_json.cpp | 28 +++ 4 files changed, 227 insertions(+) create mode 100644 tket/test/src/Circuit/test_ConjugationBox.cpp diff --git a/tket/test/CMakeLists.txt b/tket/test/CMakeLists.txt index e9ebc2854e..93ec203b6f 100644 --- a/tket/test/CMakeLists.txt +++ b/tket/test/CMakeLists.txt @@ -118,6 +118,7 @@ add_executable(test-tket src/Circuit/test_StatePreparation.cpp src/Circuit/test_DiagonalBox.cpp src/Circuit/test_ToffoliBox.cpp + src/Circuit/test_ConjugationBox.cpp src/test_UnitaryTableau.cpp src/test_ChoiMixTableau.cpp src/test_PhasePolynomials.cpp diff --git a/tket/test/src/Circuit/test_Boxes.cpp b/tket/test/src/Circuit/test_Boxes.cpp index df19f2fc52..ec6b58679e 100644 --- a/tket/test/src/Circuit/test_Boxes.cpp +++ b/tket/test/src/Circuit/test_Boxes.cpp @@ -21,6 +21,7 @@ #include "tket/Circuit/Boxes.hpp" #include "tket/Circuit/CircUtils.hpp" #include "tket/Circuit/Circuit.hpp" +#include "tket/Circuit/ConjugationBox.hpp" #include "tket/Circuit/DiagonalBox.hpp" #include "tket/Circuit/Multiplexor.hpp" #include "tket/Circuit/PauliExpBoxes.hpp" @@ -1250,6 +1251,38 @@ SCENARIO("Checking equality", "[boxes]") { {{{Pauli::Y}, 1.0}, {{Pauli::I}, 1.2}, {{Pauli::I}, -0.5}})); } } + GIVEN("ConjugationBox") { + Circuit compute(2); + compute.add_op(OpType::CRx, 0.5, {1, 0}); + Op_ptr compute_op = std::make_shared(CircBox(compute)); + Circuit action(2); + action.add_op(OpType::H, {0}); + Op_ptr action_op = std::make_shared(CircBox(action)); + ConjugationBox box(compute_op, action_op); + WHEN("all arguments are equal") { REQUIRE(box == box); } + WHEN("different ids but equivalent ops") { + REQUIRE(box == ConjugationBox(compute_op, action_op)); + } + WHEN("different uncompute") { + Circuit uncompute(2); + uncompute.add_op(OpType::CZ, {0, 1}); + Op_ptr uncompute_op = std::make_shared(CircBox(uncompute)); + REQUIRE(box != ConjugationBox(compute_op, action_op, uncompute_op)); + } + WHEN("equivalent uncompute") { + REQUIRE( + box == ConjugationBox(compute_op, action_op, compute_op->dagger())); + REQUIRE( + ConjugationBox(compute_op, action_op, compute_op->dagger()) == + ConjugationBox(compute_op, action_op)); + } + WHEN("different args") { + Circuit compute_2(2); + compute_2.add_op(OpType::CZ, {0, 1}); + Op_ptr compute_2_op = std::make_shared(CircBox(compute_2)); + REQUIRE(box != ConjugationBox(compute_2_op, action_op)); + } + } } SCENARIO("Checking box names", "[boxes]") { diff --git a/tket/test/src/Circuit/test_ConjugationBox.cpp b/tket/test/src/Circuit/test_ConjugationBox.cpp new file mode 100644 index 0000000000..d7e3bd0585 --- /dev/null +++ b/tket/test/src/Circuit/test_ConjugationBox.cpp @@ -0,0 +1,165 @@ +// Copyright 2019-2023 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 +#include + +#include "../testutil.hpp" +#include "tket/Circuit/Boxes.hpp" +#include "tket/Circuit/CircUtils.hpp" +#include "tket/Circuit/Circuit.hpp" +#include "tket/Circuit/ConjugationBox.hpp" +#include "tket/Circuit/Simulation/CircuitSimulator.hpp" +#include "tket/Gate/Rotation.hpp" + +namespace tket { +namespace test_ConjugationBox { + +SCENARIO("Test ConjugationBox") { + GIVEN("Constructor with default uncompute") { + Circuit compute(2); + compute.add_op(OpType::CRx, 0.5, {1, 0}); + Op_ptr compute_op = std::make_shared(CircBox(compute)); + Circuit action(2); + action.add_op(OpType::H, {0}); + Op_ptr action_op = std::make_shared(CircBox(action)); + ConjugationBox box(compute_op, action_op); + std::shared_ptr c = box.to_circuit(); + Circuit d(2); + d.add_op(compute_op, {0, 1}); + d.add_op(action_op, {0, 1}); + d.add_op(compute_op->dagger(), {0, 1}); + REQUIRE(*c == d); + } + GIVEN("Constructor with explicit uncompute op") { + Circuit compute(2); + compute.add_op(OpType::CX, {0, 1}); + compute.add_op(OpType::CX, {1, 0}); + compute.add_op(OpType::CX, {0, 1}); + Op_ptr compute_op = std::make_shared(CircBox(compute)); + Circuit action(2); + action.add_op(OpType::H, {0}); + Op_ptr action_op = std::make_shared(CircBox(action)); + Circuit uncompute(2); + uncompute.add_op(OpType::CX, {1, 0}); + uncompute.add_op(OpType::CX, {0, 1}); + uncompute.add_op(OpType::CX, {1, 0}); + Op_ptr uncompute_op = std::make_shared(CircBox(uncompute)); + ConjugationBox box(compute_op, action_op, uncompute_op); + std::shared_ptr c = box.to_circuit(); + Circuit d(2); + d.add_op(compute_op, {0, 1}); + d.add_op(action_op, {0, 1}); + d.add_op(uncompute_op, {0, 1}); + REQUIRE(*c == d); + } + GIVEN("Test dagger") { + Circuit compute(2); + compute.add_op(OpType::CRx, 0.5, {1, 0}); + Op_ptr compute_op = std::make_shared(CircBox(compute)); + Circuit action(2); + action.add_op(OpType::Rz, 0.5, {0}); + Op_ptr action_op = std::make_shared(CircBox(action)); + WHEN("with default uncompute") { + ConjugationBox box(compute_op, action_op); + ConjugationBox correct_box_dagger(compute_op, action_op->dagger()); + const ConjugationBox box_dagger = + static_cast(*box.dagger()); + REQUIRE(box_dagger == correct_box_dagger); + } + WHEN("with explicit uncompute") { + ConjugationBox box(compute_op, action_op, compute_op->dagger()); + ConjugationBox correct_box_dagger( + compute_op, action_op->dagger(), compute_op->dagger()); + const ConjugationBox box_dagger = + static_cast(*box.dagger()); + REQUIRE(box_dagger == correct_box_dagger); + } + } + GIVEN("Test transpose") { + Circuit compute(1); + compute.add_op(OpType::TK1, {0.1, 0.2, 0.3}, {0}); + Op_ptr compute_op = std::make_shared(CircBox(compute)); + Circuit action(1); + action.add_op(OpType::TK1, {1.1, 1.2, 1.3}, {0}); + Op_ptr action_op = std::make_shared(CircBox(action)); + WHEN("with default uncompute") { + ConjugationBox box(compute_op, action_op); + ConjugationBox correct_box_transpose( + compute_op->dagger()->transpose(), action_op->transpose(), + compute_op->transpose()); + const ConjugationBox box_transpose = + static_cast(*box.transpose()); + REQUIRE(box_transpose == correct_box_transpose); + } + WHEN("with explicit uncompute") { + ConjugationBox box(compute_op, action_op, compute_op->dagger()); + ConjugationBox correct_box_transpose( + compute_op->dagger()->transpose(), action_op->transpose(), + compute_op->transpose()); + const ConjugationBox box_transpose = + static_cast(*box.transpose()); + REQUIRE(box_transpose == correct_box_transpose); + } + } +} +SCENARIO("Test ConjugationBox Exceptions") { + GIVEN("Ops with classical wires") { + Circuit compute(2, 1); + Op_ptr compute_op = std::make_shared(CircBox(compute)); + Circuit action(2); + Op_ptr action_op = std::make_shared(CircBox(action)); + REQUIRE_THROWS_MATCHES( + ConjugationBox(compute_op, action_op), std::invalid_argument, + MessageContains("only supports quantum operations")); + } + GIVEN("Uncompute with classical wires") { + Circuit compute(2); + Op_ptr compute_op = std::make_shared(CircBox(compute)); + Circuit action(2); + Op_ptr action_op = std::make_shared(CircBox(action)); + Circuit uncompute(2, 1); + Op_ptr uncompute_op = std::make_shared(CircBox(uncompute)); + REQUIRE_THROWS_MATCHES( + ConjugationBox(compute_op, action_op, uncompute_op), + std::invalid_argument, + MessageContains("only supports quantum operations")); + } + GIVEN("Unmatched size") { + Circuit compute(3); + Op_ptr compute_op = std::make_shared(CircBox(compute)); + Circuit action(2); + Op_ptr action_op = std::make_shared(CircBox(action)); + REQUIRE_THROWS_MATCHES( + ConjugationBox(compute_op, action_op), std::invalid_argument, + MessageContains("have the same number of qubits")); + } + GIVEN("Unmatched size caused by uncompute") { + Circuit compute(2); + Op_ptr compute_op = std::make_shared(CircBox(compute)); + Circuit action(2); + Op_ptr action_op = std::make_shared(CircBox(action)); + Circuit uncompute(3); + Op_ptr uncompute_op = std::make_shared(CircBox(uncompute)); + REQUIRE_THROWS_MATCHES( + ConjugationBox(compute_op, action_op, uncompute_op), + std::invalid_argument, + MessageContains("have the same number of qubits")); + } +} +} // namespace test_ConjugationBox +} // namespace tket diff --git a/tket/test/src/test_json.cpp b/tket/test/src/test_json.cpp index 60796e60b1..d52eeeb665 100644 --- a/tket/test/src/test_json.cpp +++ b/tket/test/src/test_json.cpp @@ -23,6 +23,7 @@ #include "tket/Circuit/CircUtils.hpp" #include "tket/Circuit/Circuit.hpp" #include "tket/Circuit/Command.hpp" +#include "tket/Circuit/ConjugationBox.hpp" #include "tket/Circuit/DiagonalBox.hpp" #include "tket/Circuit/Multiplexor.hpp" #include "tket/Circuit/PauliExpBoxes.hpp" @@ -605,6 +606,33 @@ SCENARIO("Test Circuit serialization") { REQUIRE(pp_b == ppbox); } + GIVEN("ConjugationBox") { + Circuit compute(2); + compute.add_op(OpType::CRx, 0.5, {1, 0}); + Op_ptr compute_op = std::make_shared(CircBox(compute)); + Circuit action(2); + action.add_op(OpType::H, {0}); + Op_ptr action_op = std::make_shared(CircBox(action)); + ConjugationBox box(compute_op, action_op); + nlohmann::json j_box = std::make_shared(box); + // check the uncompute field is null + REQUIRE( + (j_box.at("box").contains("uncompute") && + j_box.at("box").at("uncompute").is_null())); + Op_ptr box_ptr = j_box.get(); + const auto& new_box = static_cast(*box_ptr); + REQUIRE(new_box == box); + // uncompute is not null + ConjugationBox box2(compute_op, action_op, compute_op->dagger()); + nlohmann::json j_box2 = std::make_shared(box2); + REQUIRE( + (j_box2.at("box").contains("uncompute") && + !j_box2.at("box").at("uncompute").is_null())); + Op_ptr box_ptr2 = j_box2.get(); + const auto& new_box2 = static_cast(*box_ptr2); + REQUIRE(new_box2 == box2); + } + GIVEN("Circuits with named operations") { Circuit circ(2); circ.add_op(OpType::CX, {0, 1}); From 21b6ea2dd74f7d71f125147864938e68c860cc87 Mon Sep 17 00:00:00 2001 From: Yao Tang Date: Thu, 24 Aug 2023 20:44:33 +0100 Subject: [PATCH 04/10] Add binder and python test --- pytket/binders/circuit/Circuit/add_op.cpp | 25 ++++++++++++++++++ pytket/binders/circuit/boxes.cpp | 31 +++++++++++++++++++++++ pytket/tests/circuit_test.py | 14 ++++++++++ 3 files changed, 70 insertions(+) diff --git a/pytket/binders/circuit/Circuit/add_op.cpp b/pytket/binders/circuit/Circuit/add_op.cpp index 0ce0420af8..863c116c09 100644 --- a/pytket/binders/circuit/Circuit/add_op.cpp +++ b/pytket/binders/circuit/Circuit/add_op.cpp @@ -26,6 +26,7 @@ #include "tket/Circuit/Boxes.hpp" #include "tket/Circuit/Circuit.hpp" #include "tket/Circuit/ClassicalExpBox.hpp" +#include "tket/Circuit/ConjugationBox.hpp" #include "tket/Circuit/DiagonalBox.hpp" #include "tket/Circuit/Multiplexor.hpp" #include "tket/Circuit/PauliExpBoxes.hpp" @@ -807,6 +808,30 @@ void init_circuit_add_op(py::class_> &c) { ":param args: Indices of the qubits to append the box to" "\n:return: the new :py:class:`Circuit`", py::arg("box"), py::arg("args")) + .def( + "add_conjugation_box", + [](Circuit *circ, const ConjugationBox &box, + const unit_vector_t &args, const py::kwargs &kwargs) { + return add_box_method( + circ, std::make_shared(box), args, kwargs); + }, + "Append a :py:class:`ConjugationBox` to the circuit.\n\n" + ":param box: The box to append\n" + ":param args: The qubits to append the box to" + "\n:return: the new :py:class:`Circuit`", + py::arg("box"), py::arg("args")) + .def( + "add_conjugation_box", + [](Circuit *circ, const ConjugationBox &box, + const std::vector &args, const py::kwargs &kwargs) { + return add_box_method( + circ, std::make_shared(box), args, kwargs); + }, + "Append a :py:class:`ConjugationBox` to the circuit.\n\n" + ":param box: The box to append\n" + ":param args: Indices of the qubits to append the box to" + "\n:return: the new :py:class:`Circuit`", + py::arg("box"), py::arg("args")) .def( "H", [](Circuit *circ, unsigned qb, const py::kwargs &kwargs) { diff --git a/pytket/binders/circuit/boxes.cpp b/pytket/binders/circuit/boxes.cpp index 92b3ab3804..2cbe4f02fa 100644 --- a/pytket/binders/circuit/boxes.cpp +++ b/pytket/binders/circuit/boxes.cpp @@ -21,6 +21,7 @@ #include "binder_json.hpp" #include "binder_utils.hpp" #include "tket/Circuit/Circuit.hpp" +#include "tket/Circuit/ConjugationBox.hpp" #include "tket/Circuit/DiagonalBox.hpp" #include "tket/Circuit/Multiplexor.hpp" #include "tket/Circuit/PauliExpBoxes.hpp" @@ -691,5 +692,35 @@ void init_boxes(py::module &m) { .def( "is_upper_triangle", &DiagonalBox::is_upper_triangle, ":return: the upper_triangle flag"); + py::class_, Op>( + m, "ConjugationBox", + "A box to express computations that follow the compute-action-uncompute " + "pattern.") + .def( + py::init< + const Op_ptr &, const Op_ptr &, const std::optional>(), + "from operations that perform compute, action, and uncompute. All " + "three operations need to be quantum and have the same size.\n\n" + ":param compute: the compute operation\n" + ":param action: the action operation\n" + ":param uncompute: optional uncompute operation, default to " + "compute.dagger(). If provided, the user needs to make sure that the " + "unitary matrices of uncompute.dagger() and compute must be the " + "equal.", + py::arg("compute"), py::arg("action"), + py::arg("uncompute") = std::nullopt) + .def( + "get_circuit", [](ConjugationBox &box) { return *box.to_circuit(); }, + ":return: the :py:class:`Circuit` described by the box") + .def( + "get_compute", &ConjugationBox::get_compute, + ":return: the compute operation") + .def( + "get_action", &ConjugationBox::get_action, + ":return: the action operation") + .def( + "get_uncompute", &ConjugationBox::get_uncompute, + ":return: the uncompute operation. Returns None if the default " + "compute.dagger() is used"); } } // namespace tket diff --git a/pytket/tests/circuit_test.py b/pytket/tests/circuit_test.py index 16a94a9ecf..740c5727cd 100644 --- a/pytket/tests/circuit_test.py +++ b/pytket/tests/circuit_test.py @@ -33,6 +33,7 @@ MultiplexedTensoredU2Box, StatePreparationBox, DiagonalBox, + ConjugationBox, ExpBox, PauliExpBox, PauliExpPairBox, @@ -565,6 +566,19 @@ def test_boxes() -> None: d.add_multiplexed_tensored_u2(multiplexor, [3, 2, 1, 0]) assert np.allclose(unitary, comparison) assert d.n_gates == 23 + # ConjugationBox + compute = CircBox(Circuit(3).CX(0, 1).CX(1, 2)) + action = CircBox(Circuit(3).H(2)) + conj_box1 = ConjugationBox(compute, action) + assert conj_box1.get_compute() == compute + assert conj_box1.get_action() == action + assert conj_box1.get_uncompute() is None + uncompute = CircBox(Circuit(3).CX(1, 2).CX(0, 1)) + conj_box2 = ConjugationBox(compute, action, uncompute) + assert conj_box2.get_uncompute() == uncompute + d.add_conjugation_box(conj_box1, [0, 1, 2]) + d.add_conjugation_box(conj_box2, [Qubit(0), Qubit(1), Qubit(2)]) + assert d.n_gates == 25 assert json_validate(d) From dd73c3fe290fc0a485791ffabc5ba1bf5f79f461 Mon Sep 17 00:00:00 2001 From: Yao Tang Date: Thu, 24 Aug 2023 21:05:56 +0100 Subject: [PATCH 05/10] Add to jsonschema --- schemas/circuit_v1.json | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/schemas/circuit_v1.json b/schemas/circuit_v1.json index 3105f30e07..8564fc583a 100644 --- a/schemas/circuit_v1.json +++ b/schemas/circuit_v1.json @@ -585,6 +585,21 @@ "$ref": "#/definitions/matrix", "description": "Diagonal matrix represented by DiagonalBox." }, + "compute": { + "$ref": "#/definitions/operation", + "description": "Compute operation in ConjugationBox." + }, + "action": { + "$ref": "#/definitions/operation", + "description": "Action operation in ConjugationBox." + }, + "uncompute": { + "anyOf": [ + {"$ref": "#/definitions/operation"}, + {"type": "null"} + ], + "description": "Uncompute operation in ConjugationBox." + }, "upper_triangle": { "type": "boolean", "description": "Indicate whether to implement multiplexors in the DiagonalBox decomposition as an upper triangle." @@ -844,6 +859,24 @@ ] } }, + { + "if": { + "properties": { + "type": { + "const": "ConjugationBox" + } + } + }, + "then": { + "required": [ + "compute", + "action" + ], + "optional": [ + "uncompute" + ] + } + }, { "if": { "properties": { From 861dca30766cf147c52608fd7956f2d31d3e74e8 Mon Sep 17 00:00:00 2001 From: Yao Tang Date: Thu, 24 Aug 2023 21:07:11 +0100 Subject: [PATCH 06/10] 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 2ea0c10167..6505b08146 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.36@tket/stable") + self.requires("tket/1.2.37@tket/stable") self.requires("tklog/0.3.3@tket/stable") self.requires("tkrng/0.3.3@tket/stable") self.requires("tkassert/0.3.3@tket/stable") diff --git a/tket/conanfile.py b/tket/conanfile.py index a68024a074..1a304ebd92 100644 --- a/tket/conanfile.py +++ b/tket/conanfile.py @@ -23,7 +23,7 @@ class TketConan(ConanFile): name = "tket" - version = "1.2.36" + version = "1.2.37" package_type = "library" license = "Apache 2" homepage = "https://github.com/CQCL/tket" From de757c76e97b9b5fd0f59d4949eea91d85cab71a Mon Sep 17 00:00:00 2001 From: Yao Tang Date: Thu, 24 Aug 2023 21:10:04 +0100 Subject: [PATCH 07/10] Add entry to changelog --- pytket/docs/changelog.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 8396774e0e..a823707398 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -4,6 +4,11 @@ Changelog Unreleased ---------- +Major new features: + +* Add ``ConjugationBox`` to express circuits that follow +the compute-action-uncompute pattern. + Minor new features: * Implement equality checking for all boxes. From 745db5412aa0d9d5f22e6abb3b763b312a9eacf0 Mon Sep 17 00:00:00 2001 From: Yao Tang Date: Fri, 25 Aug 2023 00:23:01 +0100 Subject: [PATCH 08/10] Fix changelog format --- pytket/docs/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index a823707398..0104345d58 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -7,7 +7,7 @@ Unreleased Major new features: * Add ``ConjugationBox`` to express circuits that follow -the compute-action-uncompute pattern. + the compute-action-uncompute pattern. Minor new features: From 453e9718710dbbaa5a7353a5089d55d00a10e354 Mon Sep 17 00:00:00 2001 From: Yao Tang Date: Fri, 25 Aug 2023 09:55:59 +0100 Subject: [PATCH 09/10] Update docstrings --- pytket/binders/circuit/boxes.cpp | 5 ++--- tket/include/tket/Circuit/ConjugationBox.hpp | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pytket/binders/circuit/boxes.cpp b/pytket/binders/circuit/boxes.cpp index 2cbe4f02fa..5c559db712 100644 --- a/pytket/binders/circuit/boxes.cpp +++ b/pytket/binders/circuit/boxes.cpp @@ -704,9 +704,8 @@ void init_boxes(py::module &m) { ":param compute: the compute operation\n" ":param action: the action operation\n" ":param uncompute: optional uncompute operation, default to " - "compute.dagger(). If provided, the user needs to make sure that the " - "unitary matrices of uncompute.dagger() and compute must be the " - "equal.", + "compute.dagger(). If provided, the user needs to make sure that " + "uncompute.dagger() and compute have the same unitary.", py::arg("compute"), py::arg("action"), py::arg("uncompute") = std::nullopt) .def( diff --git a/tket/include/tket/Circuit/ConjugationBox.hpp b/tket/include/tket/Circuit/ConjugationBox.hpp index 38cb99c4eb..62fac23ad5 100644 --- a/tket/include/tket/Circuit/ConjugationBox.hpp +++ b/tket/include/tket/Circuit/ConjugationBox.hpp @@ -32,8 +32,8 @@ class ConjugationBox : public Box { * @param compute the compute operation * @param action the action operation * @param uncompute optional uncompute operation, default to compute.dagger(). - * If provided, the user needs to make sure that the unitary matrices of - * uncompute.dagger() and compute must be equal. + * If provided, the user needs to make sure that uncompute.dagger() and + * compute have the same unitary. */ explicit ConjugationBox( const Op_ptr &compute, const Op_ptr &action, From cb15d9d2b843b3564e54d14ee56704728828bcab Mon Sep 17 00:00:00 2001 From: Yao Tang Date: Tue, 29 Aug 2023 09:37:34 +0100 Subject: [PATCH 10/10] Update docstring --- pytket/binders/circuit/boxes.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pytket/binders/circuit/boxes.cpp b/pytket/binders/circuit/boxes.cpp index 5c559db712..5d86eeb4fa 100644 --- a/pytket/binders/circuit/boxes.cpp +++ b/pytket/binders/circuit/boxes.cpp @@ -699,8 +699,9 @@ void init_boxes(py::module &m) { .def( py::init< const Op_ptr &, const Op_ptr &, const std::optional>(), - "from operations that perform compute, action, and uncompute. All " - "three operations need to be quantum and have the same size.\n\n" + "Construct from operations that perform compute, action, and " + "uncompute. All three operations need to be quantum and have the " + "same size.\n\n" ":param compute: the compute operation\n" ":param action: the action operation\n" ":param uncompute: optional uncompute operation, default to "