From 86fb61e6f2922050a1ac438d1a8103ea6942a239 Mon Sep 17 00:00:00 2001 From: yao-cqc <75305462+yao-cqc@users.noreply.github.com> Date: Mon, 14 Feb 2022 10:12:29 +0000 Subject: [PATCH] Feature/decompose boxes in routing (#197) * Reject boxes in Architecture::valid_operation * Add `next_q_cut` method to a quantum cut Only consider quantum edges * Use `next_q_cut` in `advance_frontier_boundary` * Add BoxDecompositionRoutingMethod * Add tests * Reformat * Reject boxes in LexiRouteMethod::check_method * Update tests * Add JSON serialisation * Handle unused arguments * Refactor Circuit::decompose_boxes * fix naming Co-authored-by: sjdilkes --- tket/src/Architecture/Architecture.cpp | 7 +- tket/src/Architecture/CMakeLists.txt | 3 + .../include/Architecture/Architecture.hpp | 5 +- tket/src/Circuit/include/Circuit/Circuit.hpp | 12 ++ tket/src/Circuit/macro_circ_info.cpp | 38 +++++ tket/src/Circuit/macro_manipulation.cpp | 39 +++-- tket/src/Mapping/BoxDecomposition.cpp | 71 +++++++++ tket/src/Mapping/CMakeLists.txt | 1 + tket/src/Mapping/LexiRoute.cpp | 13 +- tket/src/Mapping/MappingFrontier.cpp | 16 +- tket/src/Mapping/MultiGateReorder.cpp | 3 +- tket/src/Mapping/RoutingMethodJson.cpp | 3 + .../include/Mapping/BoxDecomposition.hpp | 63 ++++++++ .../include/Mapping/MultiGateReorder.hpp | 4 +- .../include/Mapping/RoutingMethodJson.hpp | 1 + tket/tests/Circuit/test_Circ.cpp | 29 ++++ tket/tests/test_BoxDecompRoutingMethod.cpp | 137 ++++++++++++++++++ tket/tests/test_MappingFrontier.cpp | 30 ++++ tket/tests/test_MultiGateReorder.cpp | 18 ++- tket/tests/test_json.cpp | 5 +- tket/tests/tkettestsfiles.cmake | 1 + 21 files changed, 452 insertions(+), 47 deletions(-) create mode 100644 tket/src/Mapping/BoxDecomposition.cpp create mode 100644 tket/src/Mapping/include/Mapping/BoxDecomposition.hpp create mode 100644 tket/tests/test_BoxDecompRoutingMethod.cpp diff --git a/tket/src/Architecture/Architecture.cpp b/tket/src/Architecture/Architecture.cpp index 479f734f08..3c88bb5081 100644 --- a/tket/src/Architecture/Architecture.cpp +++ b/tket/src/Architecture/Architecture.cpp @@ -27,7 +27,12 @@ namespace tket { // basic implementation that works off same prior assumptions // TODO: Update this for more mature systems of multi-qubit gates bool Architecture::valid_operation( - /*const OpType& optype, */ const std::vector& uids) const { + const Op_ptr& op, const std::vector& uids) const { + if (op->get_desc().is_box() || + (op->get_type() == OpType::Conditional && + static_cast(*op).get_op()->get_desc().is_box())) + return false; + if (uids.size() == 1) { // TODO: for simple case here this should probably not pass if // node_exists[uids[0]] == FALSE, but should be fine for now? diff --git a/tket/src/Architecture/CMakeLists.txt b/tket/src/Architecture/CMakeLists.txt index 30f3b33135..c49a5a02ad 100644 --- a/tket/src/Architecture/CMakeLists.txt +++ b/tket/src/Architecture/CMakeLists.txt @@ -23,6 +23,9 @@ add_library(tket-${COMP} ArchitectureGraphClasses.cpp) list(APPEND DEPS_${COMP} + Circuit + OpType + Ops Graphs Utils) diff --git a/tket/src/Architecture/include/Architecture/Architecture.hpp b/tket/src/Architecture/include/Architecture/Architecture.hpp index b7c3d975d0..4d76614133 100644 --- a/tket/src/Architecture/include/Architecture/Architecture.hpp +++ b/tket/src/Architecture/include/Architecture/Architecture.hpp @@ -21,8 +21,10 @@ #include #include +#include "Circuit/Conditional.hpp" #include "Graphs/CompleteGraph.hpp" #include "Graphs/DirectedGraph.hpp" +#include "Ops/OpPtr.hpp" #include "Utils/BiMapHeaders.hpp" #include "Utils/EigenConfig.hpp" #include "Utils/Json.hpp" @@ -102,8 +104,7 @@ class Architecture : public ArchitectureBase> { */ node_set_t get_articulation_points(const Architecture &subarc) const; - bool valid_operation( - /*const OpType& optype, */ const std::vector &uids) const; + bool valid_operation(const Op_ptr &op, const std::vector &uids) const; /** * Sub-architecture generated by a subset of nodes. diff --git a/tket/src/Circuit/include/Circuit/Circuit.hpp b/tket/src/Circuit/include/Circuit/Circuit.hpp index 3d24de524a..dbd41d81c8 100644 --- a/tket/src/Circuit/include/Circuit/Circuit.hpp +++ b/tket/src/Circuit/include/Circuit/Circuit.hpp @@ -1059,6 +1059,11 @@ class Circuit { std::shared_ptr b_frontier, const std::function &skip_func) const; + // given current slice of quantum frontier, returns the next slice. + // ignore classical and boolean edges + CutFrontier next_q_cut( + std::shared_ptr u_frontier) const; + /** * Depth of circuit. * @@ -1375,6 +1380,13 @@ class Circuit { */ Circuit conditional_circuit(const bit_vector_t &bits, unsigned value) const; + /** + * Replaces one \ref vertex by applying \ref Box::to_circuit + * + * @return whether the vertex holds a box or a conditional box + */ + bool substitute_box_vertex(Vertex &vert, VertexDeletion vertex_deletion); + /** * Replaces each \ref Box operation by applying \ref Box::to_circuit * diff --git a/tket/src/Circuit/macro_circ_info.cpp b/tket/src/Circuit/macro_circ_info.cpp index e453e79cca..76b35732ec 100644 --- a/tket/src/Circuit/macro_circ_info.cpp +++ b/tket/src/Circuit/macro_circ_info.cpp @@ -517,6 +517,44 @@ CutFrontier Circuit::next_cut( get_next_b_frontier(*this, b_frontier, u_frontier, next_slice_lookup)}; } +CutFrontier Circuit::next_q_cut( + std::shared_ptr u_frontier) const { + auto next_slice = std::make_shared(); + VertexSet next_slice_lookup; + VertexSet bad_vertices; + EdgeSet edge_lookup; + for (const std::pair& pair : u_frontier->get()) { + edge_lookup.insert(pair.second); + } + + // find the next slice first + for (const std::pair& pair : u_frontier->get()) { + Vertex try_v = target(pair.second); + if (detect_final_Op(try_v)) continue; + if (next_slice_lookup.find(try_v) != next_slice_lookup.end()) + continue; // already going to be in next slice + bool good_vertex = bad_vertices.find(try_v) == bad_vertices.end(); + if (!good_vertex) continue; + EdgeVec ins = get_in_edges(try_v); + for (const Edge& in : ins) { + if (edge_lookup.find(in) == edge_lookup.end() && + get_edgetype(in) == EdgeType::Quantum) { + good_vertex = false; + bad_vertices.insert(try_v); + break; + } + } + if (good_vertex) { + next_slice_lookup.insert(try_v); + next_slice->push_back(try_v); + } + } + + return { + next_slice, get_next_u_frontier(*this, u_frontier, next_slice_lookup), + std::make_shared()}; +} + SliceVec Circuit::get_reverse_slices() const { vertex_map_t mapping; vertex_map_t rev_mapping; diff --git a/tket/src/Circuit/macro_manipulation.cpp b/tket/src/Circuit/macro_manipulation.cpp index 3f6c587cbd..c587b38fa2 100644 --- a/tket/src/Circuit/macro_manipulation.cpp +++ b/tket/src/Circuit/macro_manipulation.cpp @@ -629,27 +629,34 @@ Circuit Circuit::conditional_circuit( return cond_circ; } +bool Circuit::substitute_box_vertex( + Vertex& vert, VertexDeletion vertex_deletion) { + Op_ptr op = get_Op_ptr_from_Vertex(vert); + bool conditional = op->get_type() == OpType::Conditional; + if (conditional) { + const Conditional& cond = static_cast(*op); + op = cond.get_op(); + } + if (!op->get_desc().is_box()) return false; + const Box& b = static_cast(*op); + Circuit replacement = *b.to_circuit(); + if (conditional) { + substitute_conditional( + replacement, vert, vertex_deletion, OpGroupTransfer::Merge); + } else { + substitute(replacement, vert, vertex_deletion, OpGroupTransfer::Merge); + } + return true; +} + bool Circuit::decompose_boxes() { bool success = false; VertexList bin; BGL_FORALL_VERTICES(v, dag, DAG) { - Op_ptr op = get_Op_ptr_from_Vertex(v); - bool conditional = op->get_type() == OpType::Conditional; - if (conditional) { - const Conditional& cond = static_cast(*op); - op = cond.get_op(); - } - if (!op->get_desc().is_box()) continue; - const Box& b = static_cast(*op); - Circuit replacement = *b.to_circuit(); - if (conditional) { - substitute_conditional( - replacement, v, VertexDeletion::No, OpGroupTransfer::Merge); - } else { - substitute(replacement, v, VertexDeletion::No, OpGroupTransfer::Merge); + if (substitute_box_vertex(v, VertexDeletion::No)) { + bin.push_back(v); + success = true; } - bin.push_back(v); - success = true; } remove_vertices(bin, GraphRewiring::No, VertexDeletion::Yes); return success; diff --git a/tket/src/Mapping/BoxDecomposition.cpp b/tket/src/Mapping/BoxDecomposition.cpp new file mode 100644 index 0000000000..cd52143ece --- /dev/null +++ b/tket/src/Mapping/BoxDecomposition.cpp @@ -0,0 +1,71 @@ +#include "Mapping/BoxDecomposition.hpp" + +#include "Mapping/MappingFrontier.hpp" + +namespace tket { + +BoxDecomposition::BoxDecomposition( + const ArchitecturePtr &_architecture, + std::shared_ptr &_mapping_frontier) + : architecture_(_architecture), mapping_frontier_(_mapping_frontier) {} + +void BoxDecomposition::solve() { + // Box type vertices are later removed from DAG + VertexList bin; + + std::shared_ptr frontier_edges = + frontier_convert_vertport_to_edge( + this->mapping_frontier_->circuit_, + this->mapping_frontier_->quantum_boundary); + CutFrontier next_cut = + this->mapping_frontier_->circuit_.next_q_cut(frontier_edges); + for (Vertex &vert : *next_cut.slice) { + if (this->mapping_frontier_->circuit_.substitute_box_vertex( + vert, Circuit::VertexDeletion::No)) + bin.push_back(vert); + } + + // Delete vertices + this->mapping_frontier_->circuit_.remove_vertices( + bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes); +} + +BoxDecompositionRoutingMethod::BoxDecompositionRoutingMethod(){}; + +bool BoxDecompositionRoutingMethod::check_method( + const std::shared_ptr &mapping_frontier, + const ArchitecturePtr & /*architecture*/) const { + std::shared_ptr frontier_edges = + frontier_convert_vertport_to_edge( + mapping_frontier->circuit_, mapping_frontier->quantum_boundary); + CutFrontier next_cut = mapping_frontier->circuit_.next_q_cut(frontier_edges); + for (const Vertex &vert : *next_cut.slice) { + Op_ptr op = mapping_frontier->circuit_.get_Op_ptr_from_Vertex(vert); + if (op->get_desc().is_box() || + (op->get_type() == OpType::Conditional && + static_cast(*op).get_op()->get_desc().is_box())) + return true; + } + return false; +} + +unit_map_t BoxDecompositionRoutingMethod::routing_method( + std::shared_ptr &mapping_frontier, + const ArchitecturePtr &architecture) const { + BoxDecomposition bd(architecture, mapping_frontier); + bd.solve(); + return {}; +} + +nlohmann::json BoxDecompositionRoutingMethod::serialize() const { + nlohmann::json j; + j["name"] = "BoxDecompositionRoutingMethod"; + return j; +} + +BoxDecompositionRoutingMethod BoxDecompositionRoutingMethod::deserialize( + const nlohmann::json & /*j*/) { + return BoxDecompositionRoutingMethod(); +} + +} // namespace tket \ No newline at end of file diff --git a/tket/src/Mapping/CMakeLists.txt b/tket/src/Mapping/CMakeLists.txt index 534adc2ea8..17c2b4306d 100644 --- a/tket/src/Mapping/CMakeLists.txt +++ b/tket/src/Mapping/CMakeLists.txt @@ -24,6 +24,7 @@ add_library(tket-${COMP} MappingFrontier.cpp MappingManager.cpp MultiGateReorder.cpp + BoxDecomposition.cpp RoutingMethodCircuit.cpp RoutingMethodJson.cpp Verification.cpp) diff --git a/tket/src/Mapping/LexiRoute.cpp b/tket/src/Mapping/LexiRoute.cpp index ed3b8480cd..c1e3095263 100644 --- a/tket/src/Mapping/LexiRoute.cpp +++ b/tket/src/Mapping/LexiRoute.cpp @@ -510,8 +510,19 @@ LexiRouteRoutingMethod::LexiRouteRoutingMethod(unsigned _max_depth) : max_depth_(_max_depth){}; bool LexiRouteRoutingMethod::check_method( - const std::shared_ptr& /*mapping_frontier*/, + const std::shared_ptr& mapping_frontier, const ArchitecturePtr& /*architecture*/) const { + std::shared_ptr frontier_edges = + frontier_convert_vertport_to_edge( + mapping_frontier->circuit_, mapping_frontier->quantum_boundary); + CutFrontier next_cut = mapping_frontier->circuit_.next_q_cut(frontier_edges); + for (const Vertex& vert : *next_cut.slice) { + Op_ptr op = mapping_frontier->circuit_.get_Op_ptr_from_Vertex(vert); + if (op->get_desc().is_box() || + (op->get_type() == OpType::Conditional && + static_cast(*op).get_op()->get_desc().is_box())) + return false; + } return true; } diff --git a/tket/src/Mapping/MappingFrontier.cpp b/tket/src/Mapping/MappingFrontier.cpp index 86fd62bc8c..e435dc4261 100644 --- a/tket/src/Mapping/MappingFrontier.cpp +++ b/tket/src/Mapping/MappingFrontier.cpp @@ -218,19 +218,8 @@ void MappingFrontier::advance_frontier_boundary( std::shared_ptr frontier_edges = frontier_convert_vertport_to_edge( this->circuit_, this->quantum_boundary); - // Add all classical edges that share the same target - unsigned dummy_bit_index = 0; - for (const std::pair& pair : frontier_edges->get()) { - Vertex vert = this->circuit_.target(pair.second); - for (const Edge& e : - this->circuit_.get_in_edges_of_type(vert, EdgeType::Classical)) { - frontier_edges->insert({Bit(dummy_bit_index), e}); - dummy_bit_index++; - } - } - CutFrontier next_cut = this->circuit_.next_cut( - frontier_edges, std::make_shared()); + CutFrontier next_cut = this->circuit_.next_q_cut(frontier_edges); // For each vertex in a slice, if its physically permitted, update // quantum_boundary with quantum out edges from vertex (i.e. @@ -252,8 +241,7 @@ void MappingFrontier::advance_frontier_boundary( nodes.push_back(Node(uid)); } if (architecture->valid_operation( - /* this->circuit_.get_OpType_from_Vertex(vert), */ - nodes) || + this->circuit_.get_Op_ptr_from_Vertex(vert), nodes) || this->circuit_.get_OpType_from_Vertex(vert) == OpType::Barrier) { // if no valid operation, boundary not updated and while loop terminates boundary_updated = true; diff --git a/tket/src/Mapping/MultiGateReorder.cpp b/tket/src/Mapping/MultiGateReorder.cpp index c05ea06122..59f052c2bc 100644 --- a/tket/src/Mapping/MultiGateReorder.cpp +++ b/tket/src/Mapping/MultiGateReorder.cpp @@ -67,8 +67,9 @@ bool is_physically_permitted( for (port_t port = 0; port < frontier->circuit_.n_ports(vert); ++port) { nodes.push_back(Node(get_unitid_from_vertex_port(frontier, {vert, port}))); } + Op_ptr op = frontier->circuit_.get_Op_ptr_from_Vertex(vert); - return arc_ptr->valid_operation(nodes); + return arc_ptr->valid_operation(op, nodes); } // This method will try to commute a vertex to the quantum frontier diff --git a/tket/src/Mapping/RoutingMethodJson.cpp b/tket/src/Mapping/RoutingMethodJson.cpp index 1f9479c89f..ba17a22c4c 100644 --- a/tket/src/Mapping/RoutingMethodJson.cpp +++ b/tket/src/Mapping/RoutingMethodJson.cpp @@ -39,6 +39,9 @@ void from_json(const nlohmann::json& j, std::vector& rmp_v) { } else if (name == "MultiGateReorderRoutingMethod") { rmp_v.push_back(std::make_shared( MultiGateReorderRoutingMethod::deserialize(c))); + } else if (name == "BoxDecompositionRoutingMethod") { + rmp_v.push_back(std::make_shared( + BoxDecompositionRoutingMethod::deserialize(c))); } else { std::logic_error( "Deserialization for given RoutingMethod not supported."); diff --git a/tket/src/Mapping/include/Mapping/BoxDecomposition.hpp b/tket/src/Mapping/include/Mapping/BoxDecomposition.hpp new file mode 100644 index 0000000000..8b1cd45fa4 --- /dev/null +++ b/tket/src/Mapping/include/Mapping/BoxDecomposition.hpp @@ -0,0 +1,63 @@ +#ifndef _TKET_BoxDecomposition_H_ +#define _TKET_BoxDecomposition_H_ + +#include "Mapping/MappingFrontier.hpp" +#include "Mapping/RoutingMethod.hpp" + +namespace tket { + +class BoxDecomposition { + public: + /** + * Class Constructor + * @param _architecture Architecture object added operations must respect + * @param _mapping_frontier Contains Circuit object to be modified + */ + BoxDecomposition( + const ArchitecturePtr& _architecture, + std::shared_ptr& _mapping_frontier); + + /** + * Decompose any boxes in the next slice after the frontier + */ + void solve(); + + private: + // Architecture all new physical operations must respect + ArchitecturePtr architecture_; + std::shared_ptr mapping_frontier_; +}; + +class BoxDecompositionRoutingMethod : public RoutingMethod { + public: + /** + * Decompose any boxes on the frontier + */ + BoxDecompositionRoutingMethod(); + + /** + * @return true if method can route subcircuit, false if not + */ + bool check_method( + const std::shared_ptr& mapping_frontier, + const ArchitecturePtr& /*architecture*/) const override; + + /** + * @param mapping_frontier Contains boundary of routed/unrouted circuit for + * modifying + * @param architecture Architecture providing physical constraints + * @return Logical to Physical mapping at boundary due to modification. + * + */ + unit_map_t routing_method( + std::shared_ptr& mapping_frontier, + const ArchitecturePtr& architecture) const override; + + nlohmann::json serialize() const override; + + static BoxDecompositionRoutingMethod deserialize(const nlohmann::json& /*j*/); +}; + +} // namespace tket + +#endif \ No newline at end of file diff --git a/tket/src/Mapping/include/Mapping/MultiGateReorder.hpp b/tket/src/Mapping/include/Mapping/MultiGateReorder.hpp index cb7a51c300..317cf9b6d7 100644 --- a/tket/src/Mapping/include/Mapping/MultiGateReorder.hpp +++ b/tket/src/Mapping/include/Mapping/MultiGateReorder.hpp @@ -59,8 +59,8 @@ class MultiGateReorderRoutingMethod : public RoutingMethod { * @return true if method can route subcircuit, false if not */ bool check_method( - const std::shared_ptr& /*mapping_frontier*/, - const ArchitecturePtr& /*architecture*/) const override; + const std::shared_ptr& mapping_frontier, + const ArchitecturePtr& architecture) const override; /** * @param mapping_frontier Contains boundary of routed/unrouted circuit for diff --git a/tket/src/Mapping/include/Mapping/RoutingMethodJson.hpp b/tket/src/Mapping/include/Mapping/RoutingMethodJson.hpp index 9cbdb22e90..0a12ed92ff 100644 --- a/tket/src/Mapping/include/Mapping/RoutingMethodJson.hpp +++ b/tket/src/Mapping/include/Mapping/RoutingMethodJson.hpp @@ -14,6 +14,7 @@ #pragma once +#include "Mapping/BoxDecomposition.hpp" #include "Mapping/LexiRoute.hpp" #include "Mapping/MultiGateReorder.hpp" #include "Mapping/RoutingMethod.hpp" diff --git a/tket/tests/Circuit/test_Circ.cpp b/tket/tests/Circuit/test_Circ.cpp index 7d0f43f48b..d60b463a98 100644 --- a/tket/tests/Circuit/test_Circ.cpp +++ b/tket/tests/Circuit/test_Circ.cpp @@ -1288,6 +1288,35 @@ SCENARIO("Test next slice") { } } +SCENARIO("Test next quantum slice") { + GIVEN("A simple circuit") { + Circuit circ(3, 1); + Vertex v1 = circ.add_op(OpType::X, {0}); + Vertex v2 = + circ.add_conditional_gate(OpType::Rx, {0.6}, {1}, {0}, 1); + Vertex v3 = + circ.add_conditional_gate(OpType::Ry, {0.6}, {2}, {0}, 1); + Vertex v4 = circ.add_op(OpType::S, {2}); + Vertex v5 = circ.add_op(OpType::T, {1}); + + auto frontier = std::make_shared(); + for (const Qubit& q : circ.all_qubits()) { + Vertex in = circ.get_in(q); + frontier->insert({q, circ.get_nth_out_edge(in, 0)}); + } + CutFrontier slice_front = circ.next_q_cut(frontier); + Slice sl = *slice_front.slice; + WHEN("The frontier is calculated from inputs") { + THEN("The first slice is recovered accurately.") { + REQUIRE(sl.size() == 3); + REQUIRE(sl[0] == v1); + REQUIRE(sl[1] == v2); + REQUIRE(sl[2] == v3); + } + } + } +} + SCENARIO("Test circuit.transpose() method") { GIVEN("Simple circuit") { Circuit circ(2); diff --git a/tket/tests/test_BoxDecompRoutingMethod.cpp b/tket/tests/test_BoxDecompRoutingMethod.cpp new file mode 100644 index 0000000000..d2ec20da5d --- /dev/null +++ b/tket/tests/test_BoxDecompRoutingMethod.cpp @@ -0,0 +1,137 @@ +#include + +#include "Mapping/BoxDecomposition.hpp" +#include "Mapping/LexiRoute.hpp" +#include "Mapping/MappingManager.hpp" +#include "Predicates/Predicates.hpp" +#include "Simulation/CircuitSimulator.hpp" +#include "Simulation/ComparisonFunctions.hpp" + +namespace tket { +SCENARIO("Decompose boxes") { + std::vector nodes = { + Node("test_node", 0), Node("test_node", 1), Node("test_node", 2), + Node("node_test", 3)}; + + // n0 -- n1 -- n2 -- n3 + Architecture architecture( + {{nodes[0], nodes[1]}, {nodes[1], nodes[2]}, {nodes[2], nodes[3]}}); + ArchitecturePtr shared_arc = std::make_shared(architecture); + + Eigen::Matrix4cd m; + m << 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0; + Unitary2qBox ubox(m); + + GIVEN("A box") { + Circuit circ(4); + std::vector qubits = circ.all_qubits(); + + circ.add_box(ubox, {0, 2}); + std::map rename_map = { + {qubits[0], nodes[0]}, + {qubits[1], nodes[1]}, + {qubits[2], nodes[2]}, + {qubits[3], nodes[3]}}; + circ.rename_units(rename_map); + Circuit circ_copy(circ); + std::shared_ptr mf = + std::make_shared(circ); + BoxDecomposition bd(shared_arc, mf); + bd.solve(); + const auto u = tket_sim::get_unitary(circ); + const auto u1 = tket_sim::get_unitary(circ_copy); + REQUIRE(tket_sim::compare_statevectors_or_unitaries( + u, u1, tket_sim::MatrixEquivalence::EQUAL)); + std::vector commands = mf->circuit_.get_commands(); + for (Command c : commands) { + REQUIRE(!c.get_op_ptr()->get_desc().is_box()); + } + } + + GIVEN("A conditional box") { + Circuit circ(4, 1); + std::vector qubits = circ.all_qubits(); + Conditional cond(std::make_shared(ubox), 1, 1); + circ.add_op( + std::make_shared(cond), {Bit(0), Qubit(0), Qubit(1)}); + std::map rename_map = { + {qubits[0], nodes[0]}, + {qubits[1], nodes[1]}, + {qubits[2], nodes[2]}, + {qubits[3], nodes[3]}}; + circ.rename_units(rename_map); + std::shared_ptr mf = + std::make_shared(circ); + BoxDecomposition bd(shared_arc, mf); + bd.solve(); + std::vector commands = mf->circuit_.get_commands(); + for (Command c : commands) { + Op_ptr op = c.get_op_ptr(); + REQUIRE( + !(op->get_desc().is_box() || (op->get_type() == OpType::Conditional && + static_cast(*op) + .get_op() + ->get_desc() + .is_box()))); + } + } + + GIVEN("Test BoxDecompositionRoutingMethod") { + Circuit circ(4, 1); + std::vector qubits = circ.all_qubits(); + circ.add_box(ubox, {0, 3}); + circ.add_op(OpType::CZ, {qubits[0], qubits[1]}); + circ.add_op(OpType::CX, {qubits[1], qubits[3]}); + circ.add_box(ubox, {1, 3}); + circ.add_box(ubox, {0, 1}); + circ.add_op(OpType::X, {qubits[1]}); + circ.add_op(OpType::Measure, {0, 0}); + std::map rename_map = { + {qubits[0], nodes[0]}, + {qubits[1], nodes[1]}, + {qubits[2], nodes[2]}, + {qubits[3], nodes[3]}}; + circ.rename_units(rename_map); + std::shared_ptr mf = + std::make_shared(circ); + MappingManager mm(shared_arc); + std::vector vrm = { + std::make_shared(10), + std::make_shared()}; + bool res = mm.route_circuit(circ, vrm); + REQUIRE(res); + PredicatePtr routed_correctly = + std::make_shared(architecture); + REQUIRE(routed_correctly->verify(circ)); + std::vector commands = mf->circuit_.get_commands(); + for (Command c : commands) { + REQUIRE(!c.get_op_ptr()->get_desc().is_box()); + } + } +} + +SCENARIO("Test JSON serialisation for BoxDecompositionRoutingMethod") { + GIVEN("BoxDecompositionRoutingMethod") { + nlohmann::json j_rm; + j_rm["name"] = "BoxDecompositionRoutingMethod"; + BoxDecompositionRoutingMethod rm_loaded = + BoxDecompositionRoutingMethod::deserialize(j_rm); + nlohmann::json j_rm_serialised = rm_loaded.serialize(); + REQUIRE(j_rm == j_rm_serialised); + } + + GIVEN("BoxDecompositionRoutingMethod vector") { + nlohmann::json j_rms = { + {{"name", "BoxDecompositionRoutingMethod"}}, + { + {"name", "LexiRouteRoutingMethod"}, + {"depth", 3}, + }}; + std::vector rms = + j_rms.get>(); + nlohmann::json j_rms_serialised = rms; + REQUIRE(j_rms == j_rms_serialised); + } +} + +} // namespace tket \ No newline at end of file diff --git a/tket/tests/test_MappingFrontier.cpp b/tket/tests/test_MappingFrontier.cpp index bb33f0f095..9a82006a07 100644 --- a/tket/tests/test_MappingFrontier.cpp +++ b/tket/tests/test_MappingFrontier.cpp @@ -86,6 +86,36 @@ SCENARIO("Test MappingFrontier initialisation, advance_frontier_boundary.") { REQUIRE(mf.circuit_.source(e3) == v9); REQUIRE(mf.circuit_.target(e3) == v3); } + + GIVEN("A circuit with measurements and classically controlled operations") { + Circuit circ(3, 1); + std::vector qubits = circ.all_qubits(); + // All gates are physically permitted + Vertex v0 = circ.add_op(OpType::Measure, {0, 0}); + Vertex v1 = + circ.add_conditional_gate(OpType::Rx, {0.6}, {0}, {0}, 1); + Vertex v2 = + circ.add_conditional_gate(OpType::Rz, {0.6}, {1}, {0}, 1); + Vertex v3 = circ.add_op(OpType::X, {2}); + std::vector nodes = {Node(0), Node(1), Node(2)}; + + Architecture arc({{nodes[0], nodes[1]}, {nodes[1], nodes[2]}}); + ArchitecturePtr shared_arc = std::make_shared(arc); + std::map rename_map = { + {qubits[0], nodes[0]}, {qubits[1], nodes[1]}, {qubits[2], nodes[2]}}; + circ.rename_units(rename_map); + MappingFrontier mf(circ); + mf.advance_frontier_boundary(shared_arc); + VertPort vp0 = mf.quantum_boundary->get().find(nodes[0])->second; + VertPort vp1 = mf.quantum_boundary->get().find(nodes[1])->second; + VertPort vp2 = mf.quantum_boundary->get().find(nodes[2])->second; + Op_ptr op = circ.get_Op_ptr_from_Vertex(vp0.first); + Op_ptr op2 = circ.get_Op_ptr_from_Vertex(vp1.first); + Op_ptr op3 = circ.get_Op_ptr_from_Vertex(vp2.first); + REQUIRE(vp0.first == v1); + REQUIRE(vp1.first == v2); + REQUIRE(vp2.first == v3); + } } SCENARIO("Test MappingFrontier get_default_to_quantum_boundary_unit_map") { diff --git a/tket/tests/test_MultiGateReorder.cpp b/tket/tests/test_MultiGateReorder.cpp index 12117c2955..2e8e4c099a 100644 --- a/tket/tests/test_MultiGateReorder.cpp +++ b/tket/tests/test_MultiGateReorder.cpp @@ -44,7 +44,7 @@ SCENARIO("Reorder circuits") { for (auto arg : commands[i].get_args()) { nodes.push_back(Node(arg)); } - REQUIRE(shared_arc->valid_operation(nodes)); + REQUIRE(shared_arc->valid_operation(commands[i].get_op_ptr(), nodes)); } const auto u = tket_sim::get_unitary(circ); const auto u1 = tket_sim::get_unitary(circ_copy); @@ -85,7 +85,7 @@ SCENARIO("Reorder circuits") { for (auto arg : commands[i].get_args()) { nodes.push_back(Node(arg)); } - REQUIRE(shared_arc->valid_operation(nodes)); + REQUIRE(shared_arc->valid_operation(commands[i].get_op_ptr(), nodes)); } const auto u = tket_sim::get_unitary(circ); const auto u1 = tket_sim::get_unitary(circ_copy); @@ -131,7 +131,7 @@ SCENARIO("Reorder circuits") { for (auto arg : commands[i].get_args()) { nodes.push_back(Node(arg)); } - REQUIRE(shared_arc->valid_operation(nodes)); + REQUIRE(shared_arc->valid_operation(commands[i].get_op_ptr(), nodes)); } const auto u = tket_sim::get_unitary(circ); const auto u1 = tket_sim::get_unitary(circ_copy); @@ -178,7 +178,7 @@ SCENARIO("Reorder circuits") { for (auto arg : commands[i].get_args()) { nodes.push_back(Node(arg)); } - REQUIRE(shared_arc->valid_operation(nodes)); + REQUIRE(shared_arc->valid_operation(commands[i].get_op_ptr(), nodes)); } const auto u = tket_sim::get_unitary(circ); const auto u1 = tket_sim::get_unitary(circ_copy); @@ -220,8 +220,10 @@ SCENARIO("Reorder circuits with limited search space") { // Check only the first valid CZ get commuted to the front std::vector commands = circ.get_commands(); REQUIRE(shared_arc->valid_operation( + commands[0].get_op_ptr(), {Node(commands[0].get_args()[0]), Node(commands[0].get_args()[1])})); REQUIRE(!shared_arc->valid_operation( + commands[0].get_op_ptr(), {Node(commands[1].get_args()[0]), Node(commands[1].get_args()[1])})); const auto u = tket_sim::get_unitary(circ); const auto u1 = tket_sim::get_unitary(circ_copy); @@ -273,7 +275,7 @@ SCENARIO("Test MultiGateReorderRoutingMethod") { for (auto arg : commands[i].get_args()) { nodes.push_back(Node(arg)); } - REQUIRE(shared_arc->valid_operation(nodes)); + REQUIRE(shared_arc->valid_operation(commands[i].get_op_ptr(), nodes)); } const auto u = tket_sim::get_unitary(circ); const auto u1 = tket_sim::get_unitary(circ_copy); @@ -297,13 +299,13 @@ SCENARIO("Test MultiGateReorderRoutingMethod") { for (auto arg : commands2[i].get_args()) { nodes.push_back(Node(arg)); } - REQUIRE(shared_arc->valid_operation(nodes)); + REQUIRE(shared_arc->valid_operation(commands2[i].get_op_ptr(), nodes)); } std::vector nodes; for (auto arg : commands2[4].get_args()) { nodes.push_back(Node(arg)); } - REQUIRE(!shared_arc->valid_operation(nodes)); + REQUIRE(!shared_arc->valid_operation(commands2[4].get_op_ptr(), nodes)); const auto u2 = tket_sim::get_unitary(circ2); REQUIRE(tket_sim::compare_statevectors_or_unitaries( u2, u1, tket_sim::MatrixEquivalence::EQUAL)); @@ -358,7 +360,7 @@ SCENARIO("Test MappingManager with MultiGateReorderRoutingMethod") { } } -SCENARIO("Test JSON serialisation") { +SCENARIO("Test JSON serialisation for MultiGateReorderRoutingMethod") { GIVEN("MultiGateReorderRoutingMethod") { nlohmann::json j_rm; j_rm["name"] = "MultiGateReorderRoutingMethod"; diff --git a/tket/tests/test_json.cpp b/tket/tests/test_json.cpp index acc586497d..61766d8162 100644 --- a/tket/tests/test_json.cpp +++ b/tket/tests/test_json.cpp @@ -632,10 +632,11 @@ SCENARIO("Test compiler pass serializations") { nlohmann::json j_loaded = loaded; REQUIRE(j_pp == j_loaded); } - GIVEN("Routing with MultiGateReorderRoutingMethod") { + GIVEN("Routing with multiple routing methods") { RoutingMethodPtr mrmp = std::make_shared(60, 80); - std::vector mrcon = {mrmp, rmp}; + RoutingMethodPtr brmp = std::make_shared(); + std::vector mrcon = {mrmp, rmp, brmp}; Circuit circ = CircuitsForTesting::get().uccsd; CompilationUnit cu{circ}; PassPtr placement = gen_placement_pass(place); diff --git a/tket/tests/tkettestsfiles.cmake b/tket/tests/tkettestsfiles.cmake index 4fcdd0481e..1e40106386 100644 --- a/tket/tests/tkettestsfiles.cmake +++ b/tket/tests/tkettestsfiles.cmake @@ -95,6 +95,7 @@ set(TEST_SOURCES ${TKET_TESTS_DIR}/test_LexicographicalComparison.cpp ${TKET_TESTS_DIR}/test_LexiRoute.cpp ${TKET_TESTS_DIR}/test_MultiGateReorder.cpp + ${TKET_TESTS_DIR}/test_BoxDecompRoutingMethod.cpp ${TKET_TESTS_DIR}/test_DeviceCharacterisation.cpp ${TKET_TESTS_DIR}/test_Clifford.cpp ${TKET_TESTS_DIR}/test_MeasurementSetup.cpp