diff --git a/pytket/binders/architecture.cpp b/pytket/binders/architecture.cpp index 823bc1455d..e587c3b455 100644 --- a/pytket/binders/architecture.cpp +++ b/pytket/binders/architecture.cpp @@ -19,8 +19,11 @@ #include #include +#include "Circuit/Circuit.hpp" #include "Utils/Json.hpp" #include "binder_json.hpp" +#include "binder_utils.hpp" +#include "typecast.hpp" namespace py = pybind11; using json = nlohmann::json; @@ -56,6 +59,12 @@ PYBIND11_MODULE(architecture, m) { "given two nodes in Architecture, " "returns distance between them", py::arg("node_0"), py::arg("node_1")) + .def( + "valid_operation", &Architecture::valid_operation, + "Returns true if the given operation acting on the given ", + "nodes can be executed on the Architecture connectivity graph." + "\n\n:param uids: list of UnitIDs validity is being checked for", + py::arg("uids")) .def( "get_adjacent_nodes", &Architecture::get_neighbour_nodes, "given a node, returns adjacent nodes in Architecture.", diff --git a/pytket/binders/mapping.cpp b/pytket/binders/mapping.cpp index b1f9622dcb..d202f1d2bd 100644 --- a/pytket/binders/mapping.cpp +++ b/pytket/binders/mapping.cpp @@ -13,12 +13,17 @@ // limitations under the License. #include +#include #include #include +#include +#include "Circuit/Circuit.hpp" +#include "Mapping/LexiLabelling.hpp" #include "Mapping/LexiRoute.hpp" #include "Mapping/MappingManager.hpp" #include "Mapping/RoutingMethodCircuit.hpp" +#include "binder_utils.hpp" namespace py = pybind11; @@ -63,7 +68,8 @@ PYBIND11_MODULE(mapping, m) { RoutingMethod>( m, "LexiRouteRoutingMethod", "Defines a RoutingMethod object for mapping circuits that uses the " - "Lexicographical Comparison approach outlined in arXiv:1902.08091.") + "Lexicographical Comparison approach outlined in arXiv:1902.08091." + "Only supports 1-qubit, 2-qubit and barrier gates.") .def( py::init(), "LexiRoute constructor.\n\n:param lookahead: Maximum depth of " @@ -71,6 +77,13 @@ PYBIND11_MODULE(mapping, m) { "physical mapping.", py::arg("lookahead") = 10); + py::class_< + LexiLabellingMethod, std::shared_ptr, RoutingMethod>( + m, "LexiLabellingMethod", + "Defines a RoutingMethod for labelling Qubits that uses the " + "Lexicographical Comparison approach outlined in arXiv:1902.08091.") + .def(py::init<>(), "LexiLabellingMethod constructor."); + py::class_( m, "MappingManager", "Defined by a pytket Architecture object, maps Circuit logical qubits " diff --git a/pytket/binders/passes.cpp b/pytket/binders/passes.cpp index c7da315a6b..b055137001 100644 --- a/pytket/binders/passes.cpp +++ b/pytket/binders/passes.cpp @@ -15,6 +15,8 @@ #include #include "ArchAwareSynth/SteinerForest.hpp" +#include "Mapping/LexiLabelling.hpp" +#include "Mapping/LexiRoute.hpp" #include "Mapping/RoutingMethod.hpp" #include "Predicates/CompilerPass.hpp" #include "Predicates/PassGenerators.hpp" @@ -33,8 +35,9 @@ namespace tket { static PassPtr gen_cx_mapping_pass_kwargs( const Architecture &arc, const PlacementPtr &placer, py::kwargs kwargs) { - RoutingMethodPtr method = std::make_shared(); - std::vector config = {method}; + std::vector config = { + std::make_shared(), + std::make_shared()}; if (kwargs.contains("config")) { config = py::cast>(kwargs["config"]); } @@ -50,8 +53,9 @@ static PassPtr gen_cx_mapping_pass_kwargs( } static PassPtr gen_default_routing_pass(const Architecture &arc) { - RoutingMethodPtr method = std::make_shared(); - std::vector config = {method}; + std::vector config = { + std::make_shared(), + std::make_shared()}; return gen_routing_pass(arc, config); } diff --git a/pytket/docs/changelog.rst b/pytket/docs/changelog.rst index 7a210b9b86..0375a545ac 100644 --- a/pytket/docs/changelog.rst +++ b/pytket/docs/changelog.rst @@ -35,7 +35,11 @@ Minor new features: * New ``pytket.passes.NaivePlacementPass`` which completes a basic relabelling of all Circuit Qubit not labelled as some Architecture Node to any available Architecture Node * Add ``opgroups`` property to ``Circuit``. - +* ``Architecture`` has new ``valid_operation`` method which returns true if passed UnitIDs that respect + architecture constraints. +* New methods for mapping logical to physical circuits for some ``Architecture``: ``LexiRouteRoutingMethod``, + ``LexiLabellingMethod``, ``MultiGateReorderRoutingMethod``. + 0.19.2 (February 2022) ---------------------- diff --git a/pytket/tests/architecture_test.py b/pytket/tests/architecture_test.py index 7d476f6426..d41740554b 100644 --- a/pytket/tests/architecture_test.py +++ b/pytket/tests/architecture_test.py @@ -12,8 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pytket.circuit import Node # type: ignore +from pytket.circuit import Node, Op, OpType, Circuit, Qubit, PhasePolyBox # type: ignore from pytket.architecture import Architecture, SquareGrid, FullyConnected # type: ignore +import numpy as np def test_architectures() -> None: @@ -78,8 +79,25 @@ def test_arch_types() -> None: assert isinstance(sg, SquareGrid) +def test_valid_operation() -> None: + edges = [(0, 1), (1, 2), (2, 0), (0, 3), (3, 4), (4, 5), (5, 6)] + arc = Architecture(edges) + + assert not arc.valid_operation([Node(1), Node(3)]) + assert arc.valid_operation([Node(0)]) + assert arc.valid_operation([Node(0), Node(1)]) + assert not arc.valid_operation([Node(0), Node(1), Node(2)]) + assert not arc.valid_operation([Node(10)]) + assert not arc.valid_operation([Node(10), Node(11), Node(15)]) + assert not arc.valid_operation([Node(0), Node(1), Node(2), Node(3)]) + assert not arc.valid_operation([Node(0), Node(4)]) + assert not arc.valid_operation([Node(0), Node(1), Node(2)]) + assert not arc.valid_operation([Node(0), Node(1), Node(4)]) + + if __name__ == "__main__": test_architectures() test_architecture_eq() test_fully_connected() test_arch_types() + test_valid_operation() diff --git a/pytket/tests/backend_test.py b/pytket/tests/backend_test.py index f40f361b30..2e6819fd47 100644 --- a/pytket/tests/backend_test.py +++ b/pytket/tests/backend_test.py @@ -24,7 +24,7 @@ from pytket.circuit import Circuit, OpType, BasisOrder, Qubit, Bit, Node # type: ignore from pytket.predicates import CompilationUnit # type: ignore from pytket.passes import PauliSimp, CliffordSimp, ContextSimp # type: ignore -from pytket.mapping import MappingManager, LexiRouteRoutingMethod # type: ignore +from pytket.mapping import MappingManager, LexiRouteRoutingMethod, LexiLabellingMethod # type: ignore from pytket.architecture import Architecture # type: ignore from pytket.utils.outcomearray import OutcomeArray, readout_counts from pytket.utils.prepare import prepare_circuit @@ -528,7 +528,7 @@ def test_postprocess_3() -> None: mm = MappingManager(arc) rc = c.copy() - mm.route_circuit(rc, [LexiRouteRoutingMethod()]) + mm.route_circuit(rc, [LexiLabellingMethod(), LexiRouteRoutingMethod()]) n_shots = 100 h = b.process_circuit(b.get_compiled_circuit(c), n_shots=n_shots, postprocess=True) r = b.get_result(h) diff --git a/pytket/tests/mapping_test.py b/pytket/tests/mapping_test.py index 57ee0281b1..e3f67d808e 100644 --- a/pytket/tests/mapping_test.py +++ b/pytket/tests/mapping_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pytket.mapping import MappingManager, RoutingMethodCircuit, LexiRouteRoutingMethod # type: ignore +from pytket.mapping import MappingManager, RoutingMethodCircuit, LexiRouteRoutingMethod, LexiLabellingMethod # type: ignore from pytket.architecture import Architecture # type: ignore from pytket import Circuit, OpType from pytket.circuit import Node, Qubit # type: ignore @@ -104,7 +104,7 @@ def test_LexiRouteRoutingMethod() -> None: nodes = [Node("test", 0), Node("test", 1), Node("test", 2)] test_a = Architecture([[nodes[0], nodes[1]], [nodes[1], nodes[2]]]) test_mm = MappingManager(test_a) - test_mm.route_circuit(test_c, [LexiRouteRoutingMethod()]) + test_mm.route_circuit(test_c, [LexiLabellingMethod(), LexiRouteRoutingMethod()]) routed_commands = test_c.get_commands() assert routed_commands[0].op.type == OpType.CX @@ -151,11 +151,11 @@ def test_RoutingMethodCircuit_custom_list() -> None: test_c, [ RoutingMethodCircuit(route_subcircuit_func_false, 5, 5), + LexiLabellingMethod(), LexiRouteRoutingMethod(), ], ) routed_commands = test_c.get_commands() - assert routed_commands[0].op.type == OpType.CX assert routed_commands[0].qubits == [nodes[1], nodes[0]] assert routed_commands[1].op.type == OpType.CX @@ -170,6 +170,7 @@ def test_RoutingMethodCircuit_custom_list() -> None: test_c, [ RoutingMethodCircuit(route_subcircuit_func, 5, 5), + LexiLabellingMethod(), LexiRouteRoutingMethod(), ], ) diff --git a/pytket/tests/mitigation_test.py b/pytket/tests/mitigation_test.py index 7eb3032444..494b3993e1 100644 --- a/pytket/tests/mitigation_test.py +++ b/pytket/tests/mitigation_test.py @@ -17,7 +17,7 @@ from pytket.utils.spam import SpamCorrecter, compress_counts from pytket.circuit import Node, Circuit, Qubit # type: ignore -from pytket.mapping import MappingManager, LexiRouteRoutingMethod # type: ignore +from pytket.mapping import MappingManager, LexiLabellingMethod, LexiRouteRoutingMethod # type: ignore from pytket.architecture import Architecture # type: ignore from pytket.placement import place_with_map # type: ignore from pytket.passes import DelayMeasures # type: ignore @@ -112,7 +112,7 @@ def test_spam_integration() -> None: place_with_map(bellcc, qmap) mm = MappingManager(arc) rbell = bellcc.copy() - mm.route_circuit(rbell, [LexiRouteRoutingMethod()]) + mm.route_circuit(rbell, [LexiLabellingMethod(), LexiRouteRoutingMethod()]) def check_correction( counts0: Dict[Tuple[int, ...], int], counts1: Dict[Tuple[int, ...], int] @@ -509,7 +509,7 @@ def test_spam_routing() -> None: testc = Circuit(4, 4).H(0).CX(0, 3).CX(1, 2).CX(0, 1).CX(3, 2).measure_all() routed = testc.copy() mm = MappingManager(arc) - mm.route_circuit(routed, [LexiRouteRoutingMethod()]) + mm.route_circuit(routed, [LexiLabellingMethod(), LexiRouteRoutingMethod()]) DelayMeasures().apply(routed) readout = routed.qubit_readout diff --git a/pytket/tests/placement_test.py b/pytket/tests/placement_test.py index 2db42081be..23149db5c9 100644 --- a/pytket/tests/placement_test.py +++ b/pytket/tests/placement_test.py @@ -24,7 +24,7 @@ place_with_map, ) from pytket.passes import PauliSimp, DefaultMappingPass # type: ignore -from pytket.mapping import MappingManager, LexiRouteRoutingMethod # type: ignore +from pytket.mapping import MappingManager, LexiRouteRoutingMethod, LexiLabellingMethod # type: ignore from pytket.qasm import circuit_from_qasm # type: ignore import json @@ -73,9 +73,9 @@ def test_placements() -> None: assert circ_qbs != graph_placed.qubits mm = MappingManager(test_architecture) - mm.route_circuit(base_placed, [LexiRouteRoutingMethod()]) - mm.route_circuit(line_placed, [LexiRouteRoutingMethod()]) - mm.route_circuit(graph_placed, [LexiRouteRoutingMethod()]) + mm.route_circuit(base_placed, [LexiLabellingMethod(), LexiRouteRoutingMethod()]) + mm.route_circuit(line_placed, [LexiLabellingMethod(), LexiRouteRoutingMethod()]) + mm.route_circuit(graph_placed, [LexiLabellingMethod(), LexiRouteRoutingMethod()]) assert base_placed.valid_connectivity(test_architecture, False) assert line_placed.valid_connectivity(test_architecture, False) @@ -121,8 +121,8 @@ def test_placement_config() -> None: assert map1 != map2 mm = MappingManager(test_architecture) - mm.route_circuit(circ1, [LexiRouteRoutingMethod()]) - mm.route_circuit(circ2, [LexiRouteRoutingMethod()]) + mm.route_circuit(circ1, [LexiLabellingMethod(), LexiRouteRoutingMethod()]) + mm.route_circuit(circ2, [LexiLabellingMethod(), LexiRouteRoutingMethod()]) assert circ1.n_gates < circ2.n_gates diff --git a/pytket/tests/predicates_test.py b/pytket/tests/predicates_test.py index a864d91b49..1ecdbf9bde 100644 --- a/pytket/tests/predicates_test.py +++ b/pytket/tests/predicates_test.py @@ -75,7 +75,7 @@ CompilationUnit, UserDefinedPredicate, ) -from pytket.mapping import LexiRouteRoutingMethod # type: ignore +from pytket.mapping import LexiLabellingMethod, LexiRouteRoutingMethod # type: ignore from pytket.architecture import Architecture # type: ignore from pytket.placement import Placement, GraphPlacement # type: ignore from pytket.transform import Transform, PauliSynthStrat, CXConfigType # type: ignore @@ -230,7 +230,9 @@ def test_routing_and_placement_pass() -> None: assert seq_pass.apply(cu2) assert cu2.initial_map == expected_map - full_pass = FullMappingPass(arc, pl, config=[LexiRouteRoutingMethod()]) + full_pass = FullMappingPass( + arc, pl, config=[LexiLabellingMethod(), LexiRouteRoutingMethod()] + ) cu3 = CompilationUnit(circ.copy()) assert full_pass.apply(cu3) assert cu3.initial_map == expected_map @@ -682,7 +684,9 @@ def sq(a: float, b: float, c: float) -> Circuit: [k.to_list(), v.to_list()] for k, v in qm.items() ] # FullMappingPass - fm_pass = FullMappingPass(arc, placer, config=[LexiRouteRoutingMethod()]) + fm_pass = FullMappingPass( + arc, placer, config=[LexiLabellingMethod(), LexiRouteRoutingMethod()] + ) assert fm_pass.to_dict()["pass_class"] == "SequencePass" p_pass = fm_pass.get_sequence()[0] r_pass = fm_pass.get_sequence()[1] diff --git a/pytket/tests/transform_test.py b/pytket/tests/transform_test.py index a7da886d5a..adeb61ba05 100644 --- a/pytket/tests/transform_test.py +++ b/pytket/tests/transform_test.py @@ -39,7 +39,7 @@ from pytket.transform import Transform, CXConfigType, PauliSynthStrat # type: ignore from pytket.qasm import circuit_from_qasm from pytket.architecture import Architecture # type: ignore -from pytket.mapping import MappingManager, LexiRouteRoutingMethod # type: ignore +from pytket.mapping import MappingManager, LexiRouteRoutingMethod, LexiLabellingMethod # type: ignore from pytket.placement import Placement, GraphPlacement, LinePlacement, NoiseAwarePlacement # type: ignore from sympy import Symbol # type: ignore @@ -770,7 +770,9 @@ def test_decompose_swap_to_cx() -> None: pl = Placement(arc) pl.place_with_map(circ, init_map) - MappingManager(arc).route_circuit(circ, [LexiRouteRoutingMethod()]) + MappingManager(arc).route_circuit( + circ, [LexiLabellingMethod(), LexiRouteRoutingMethod()] + ) assert circ.valid_connectivity(arc, False) Transform.DecomposeSWAPtoCX(arc).apply(circ) assert len(circ.get_commands()) == 20 @@ -813,8 +815,12 @@ def test_FullMappingPass() -> None: cu_1 = CompilationUnit(circ) gp_placer = GraphPlacement(arc) lp_placer = LinePlacement(arc) - m_pass_0 = FullMappingPass(arc, gp_placer, [LexiRouteRoutingMethod()]) - m_pass_1 = FullMappingPass(arc, lp_placer, [LexiRouteRoutingMethod()]) + m_pass_0 = FullMappingPass( + arc, gp_placer, [LexiLabellingMethod(), LexiRouteRoutingMethod()] + ) + m_pass_1 = FullMappingPass( + arc, lp_placer, [LexiLabellingMethod(), LexiRouteRoutingMethod()] + ) m_pass_0.apply(cu_0) m_pass_1.apply(cu_1) out_circ_0 = cu_0.circuit diff --git a/tket/src/Architecture/Architecture.cpp b/tket/src/Architecture/Architecture.cpp index f493b4166a..fc577e5f5d 100644 --- a/tket/src/Architecture/Architecture.cpp +++ b/tket/src/Architecture/Architecture.cpp @@ -24,25 +24,14 @@ 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 { +bool Architecture::valid_operation(const std::vector& uids) const { + for (Node n : uids) { + if (!this->node_exists(Node(n))) return false; + } if (uids.size() == 1) { - // with current Architecture can assume all single qubit gates valid - return true; - } else if (optype == OpType::Barrier) { return true; } else if (uids.size() == 2) { - if (this->node_exists(uids[0]) && this->node_exists(uids[1]) && - this->bidirectional_edge_exists(uids[0], uids[1])) { - return true; - } - } else if (uids.size() == 3 && optype == OpType::BRIDGE) { - bool con_0_exists = this->bidirectional_edge_exists(uids[0], uids[1]); - bool con_1_exists = this->bidirectional_edge_exists(uids[2], uids[1]); - if (this->node_exists(uids[0]) && this->node_exists(uids[1]) && - this->node_exists(uids[2]) && con_0_exists && con_1_exists) { + if (this->bidirectional_edge_exists(uids[0], uids[1])) { return true; } } diff --git a/tket/src/Architecture/CMakeLists.txt b/tket/src/Architecture/CMakeLists.txt index 8bf9bcffa1..ff18aaaa3d 100644 --- a/tket/src/Architecture/CMakeLists.txt +++ b/tket/src/Architecture/CMakeLists.txt @@ -28,10 +28,10 @@ add_library(tket-${COMP} list(APPEND DEPS_${COMP} Graphs - OpType TokenSwapping Utils) + foreach(DEP ${DEPS_${COMP}}) target_include_directories( tket-${COMP} PRIVATE ${TKET_${DEP}_INCLUDE_DIR}) diff --git a/tket/src/Architecture/include/Architecture/Architecture.hpp b/tket/src/Architecture/include/Architecture/Architecture.hpp index 7765014a41..8015634e50 100644 --- a/tket/src/Architecture/include/Architecture/Architecture.hpp +++ b/tket/src/Architecture/include/Architecture/Architecture.hpp @@ -23,7 +23,6 @@ #include "Graphs/CompleteGraph.hpp" #include "Graphs/DirectedGraph.hpp" -#include "OpType/OpType.hpp" #include "Utils/BiMapHeaders.hpp" #include "Utils/EigenConfig.hpp" #include "Utils/Json.hpp" @@ -107,8 +106,7 @@ class Architecture : public ArchitectureBase> { * Returns true if the given operation acting on the given nodes * can be executed on the Architecture connectivity graph. */ - bool valid_operation( - const OpType &optype, const std::vector &uids) const; + bool valid_operation(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 6fd9af3668..e6c7db4aa0 100644 --- a/tket/src/Circuit/include/Circuit/Circuit.hpp +++ b/tket/src/Circuit/include/Circuit/Circuit.hpp @@ -1064,6 +1064,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. * @@ -1380,6 +1385,13 @@ class Circuit { */ Circuit conditional_circuit(const bit_vector_t &bits, unsigned value) const; + /** + * Replaces one 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..25f1b9a37a 100644 --- a/tket/src/Circuit/macro_circ_info.cpp +++ b/tket/src/Circuit/macro_circ_info.cpp @@ -517,6 +517,43 @@ 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.contains(try_v)) + continue; // already going to be in next slice + bool good_vertex = !bad_vertices.contains(try_v); + if (!good_vertex) continue; + EdgeVec ins = get_in_edges(try_v); + for (const Edge& in : ins) { + if (!edge_lookup.contains(in) && 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..efd6bdd365 --- /dev/null +++ b/tket/src/Mapping/BoxDecomposition.cpp @@ -0,0 +1,77 @@ +// Copyright 2019-2022 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 "Mapping/BoxDecomposition.hpp" + +#include "Mapping/MappingFrontier.hpp" + +namespace tket { + +BoxDecomposition::BoxDecomposition( + const ArchitecturePtr &_architecture, + MappingFrontier_ptr &_mapping_frontier) + : architecture_(_architecture), mapping_frontier_(_mapping_frontier) {} + +bool BoxDecomposition::solve() { + // Box type vertices are later removed from DAG + VertexList bin; + bool modified = false; + 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) { + Op_ptr op = this->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())) { + if (this->mapping_frontier_->circuit_.substitute_box_vertex( + vert, Circuit::VertexDeletion::No)) { + modified = true; + bin.push_back(vert); + } + } + } + if (!modified) { + return false; + } + // Delete vertices + this->mapping_frontier_->circuit_.remove_vertices( + bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes); + return true; +} + +BoxDecompositionRoutingMethod::BoxDecompositionRoutingMethod(){}; + +std::pair BoxDecompositionRoutingMethod::routing_method( + MappingFrontier_ptr &mapping_frontier, + const ArchitecturePtr &architecture) const { + BoxDecomposition bd(architecture, mapping_frontier); + bool modified = bd.solve(); + return {modified, {}}; +} + +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 b7c6c74769..a300ee831f 100644 --- a/tket/src/Mapping/CMakeLists.txt +++ b/tket/src/Mapping/CMakeLists.txt @@ -21,9 +21,12 @@ endif() add_library(tket-${COMP} LexicographicalComparison.cpp LexiRoute.cpp + LexiRouteRoutingMethod.cpp + LexiLabelling.cpp MappingFrontier.cpp MappingManager.cpp MultiGateReorder.cpp + BoxDecomposition.cpp RoutingMethodCircuit.cpp RoutingMethodJson.cpp Verification.cpp) diff --git a/tket/src/Mapping/LexiLabelling.cpp b/tket/src/Mapping/LexiLabelling.cpp new file mode 100644 index 0000000000..00be47608b --- /dev/null +++ b/tket/src/Mapping/LexiLabelling.cpp @@ -0,0 +1,36 @@ +// Copyright 2019-2022 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 "Mapping/LexiLabelling.hpp" + +namespace tket { + +std::pair LexiLabellingMethod::routing_method( + MappingFrontier_ptr& mapping_frontier, + const ArchitecturePtr& architecture) const { + LexiRoute lr(architecture, mapping_frontier); + return {lr.solve_labelling(), {}}; +} + +nlohmann::json LexiLabellingMethod::serialize() const { + nlohmann::json j; + j["name"] = "LexiLabellingMethod"; + return j; +} + +LexiLabellingMethod LexiLabellingMethod::deserialize( + const nlohmann::json& /*j*/) { + return LexiLabellingMethod(); +} + +} // namespace tket diff --git a/tket/src/Mapping/LexiRoute.cpp b/tket/src/Mapping/LexiRoute.cpp index 890394d4c0..f4beb4251a 100644 --- a/tket/src/Mapping/LexiRoute.cpp +++ b/tket/src/Mapping/LexiRoute.cpp @@ -21,9 +21,8 @@ namespace tket { LexiRoute::LexiRoute( const ArchitecturePtr& _architecture, - std::shared_ptr& _mapping_frontier) + MappingFrontier_ptr& _mapping_frontier) : architecture_(_architecture), mapping_frontier_(_mapping_frontier) { - this->set_interacting_uids(); // set initial logical->physical labelling for (const Qubit& qb : this->mapping_frontier_->circuit_.all_qubits()) { this->labelling_.insert({qb, qb}); @@ -178,9 +177,12 @@ bool LexiRoute::update_labelling() { * Updates this->interacting_uids_ with all "interacting" pairs * of UnitID in this->mapping_frontier_ */ -void LexiRoute::set_interacting_uids(bool assigned_only) { +bool LexiRoute::set_interacting_uids( + AssignedOnly assigned_only, CheckRoutingValidity route_check, + CheckLabellingValidity label_check) { // return types this->interacting_uids_.clear(); + bool all_placed = true; for (auto it = this->mapping_frontier_->quantum_boundary->get().begin(); it != this->mapping_frontier_->quantum_boundary->get().end(); @@ -189,36 +191,59 @@ void LexiRoute::set_interacting_uids(bool assigned_only) { it->second.first, it->second.second); Vertex v0 = this->mapping_frontier_->circuit_.target(e0); // should never be input vertex, so can always use in_edges - int n_edges = this->mapping_frontier_->circuit_.n_in_edges_of_type( - v0, EdgeType::Quantum); - if (n_edges == 2) { - auto jt = it; - ++jt; - for (; - jt != this->mapping_frontier_->quantum_boundary->get().end(); - ++jt) { - // i.e. if vertices match - Edge e1 = this->mapping_frontier_->circuit_.get_nth_out_edge( - jt->second.first, jt->second.second); - Vertex v1 = this->mapping_frontier_->circuit_.target(e1); - if (v0 == v1) { - // we can assume a qubit will only be in one interaction - // we can assume from how we iterate through pairs that each qubit - // will only be found in one match - if (!assigned_only || - (this->architecture_->node_exists(Node(it->first)) && - this->architecture_->node_exists(Node(jt->first)))) { - interacting_uids_.insert({it->first, jt->first}); - interacting_uids_.insert({jt->first, it->first}); + Op_ptr op = this->mapping_frontier_->circuit_.get_Op_ptr_from_Vertex(v0); + if (op->get_type() != OpType::Barrier) { + int n_edges = this->mapping_frontier_->circuit_.n_in_edges_of_type( + v0, EdgeType::Quantum); + // make forwards = backwards + if (n_edges == 2) { + auto jt = it; + ++jt; + while (jt != + this->mapping_frontier_->quantum_boundary->get().end()) { + // i.e. if vertices match + Edge e1 = this->mapping_frontier_->circuit_.get_nth_out_edge( + jt->second.first, jt->second.second); + Vertex v1 = this->mapping_frontier_->circuit_.target(e1); + if (v0 == v1) { + // we can assume a qubit will only be in one interaction + // we can assume from how we iterate through pairs that each qubit + // will only be found in one match + bool node0_exists = + this->architecture_->node_exists(Node(it->first)); + bool node1_exists = + this->architecture_->node_exists(Node(jt->first)); + if (!node0_exists || !node1_exists || op->get_desc().is_box()) { + all_placed = false; + if (route_check == CheckRoutingValidity::Yes) return false; + } + + if (assigned_only == AssignedOnly::No || + (node0_exists && node1_exists)) { + interacting_uids_.insert({it->first, jt->first}); + interacting_uids_.insert({jt->first, it->first}); + } } + ++jt; } + } else if ( + n_edges > 2 && + this->mapping_frontier_->circuit_.get_OpType_from_Vertex(v0) != + OpType::Barrier) { + if (label_check == CheckLabellingValidity::Yes) return true; + if (route_check == CheckRoutingValidity::Yes) return false; + throw LexiRouteError( + "LexiRoute only supports non-Barrier vertices with 1 or 2 edges."); } - } else if ( - n_edges > 2 && this->mapping_frontier_->circuit_.get_OpType_from_Vertex( - v0) != OpType::Barrier) { - TKET_ASSERT(!"Non-Barrier vertex should only have 1 or 2 edges."); } } + if (label_check == CheckLabellingValidity::Yes || + route_check == CheckRoutingValidity::Yes) { + return all_placed; + } + // => either route_check true and all_placed so valid + // or !route_check and !label_check so return true and discard + return true; } swap_set_t LexiRoute::get_candidate_swaps() { @@ -311,7 +336,9 @@ std::pair LexiRoute::check_bridge( this->mapping_frontier_->advance_next_2qb_slice(lookahead); // true bool means it only sets interacting uids if both uids are in // architecture - this->set_interacting_uids(true); + this->set_interacting_uids( + AssignedOnly::Yes, CheckRoutingValidity::No, + CheckLabellingValidity::No); // if 0, just take first swap rather than place if (this->interacting_uids_.size() == 0) { candidate_swaps = {*candidate_swaps.begin()}; @@ -394,7 +421,26 @@ void LexiRoute::remove_swaps_decreasing(swap_set_t& swaps) { } } -void LexiRoute::solve(unsigned lookahead) { +bool LexiRoute::solve_labelling() { + bool all_labelled = this->set_interacting_uids( + AssignedOnly::No, CheckRoutingValidity::No, CheckLabellingValidity::Yes); + if (!all_labelled) { + this->update_labelling(); + this->mapping_frontier_->update_quantum_boundary_uids(this->labelling_); + return true; + } + return false; +} + +bool LexiRoute::solve(unsigned lookahead) { + // work out if valid + + bool all_labelled = this->set_interacting_uids( + AssignedOnly::No, CheckRoutingValidity::Yes, CheckLabellingValidity::No); + if (!all_labelled) { + return false; + } + // store a copy of the original this->mapping_frontier_->quantum_boundray // this object will be updated and reset throughout the swap picking procedure // so need to return it to original setting at end @@ -403,22 +449,8 @@ void LexiRoute::solve(unsigned lookahead) { this->mapping_frontier_->quantum_boundary->get()) { copy.insert({pair.first, pair.second}); } - // some Qubits in boundary of this->mapping_frontier_->circuit_ may not be - // this->architecture_ Node If true, assign physical meaning by replacing with - // Node from this->architecture_ - // "candidate_swaps" are connected pairs of Node in this->architecture_ s.t. - // at least one is in an "interaction" and both are "assigned" i.e. present in - // this->mapping_frontier_->circuit - - bool updated = this->update_labelling(); - if (updated) { - // update unit id at boundary in case of relabelling - this->mapping_frontier_->update_quantum_boundary_uids(this->labelling_); - return; - } swap_set_t candidate_swaps = this->get_candidate_swaps(); this->remove_swaps_decreasing(candidate_swaps); - TKET_ASSERT(candidate_swaps.size() != 0); // Only want to substitute a single swap // check next layer of interacting qubits and remove swaps until only one @@ -442,7 +474,9 @@ void LexiRoute::solve(unsigned lookahead) { this->mapping_frontier_->advance_next_2qb_slice(lookahead); // true bool means it only sets interacting uids if both uids are in // architecture - this->set_interacting_uids(true); + this->set_interacting_uids( + AssignedOnly::Yes, CheckRoutingValidity::No, + CheckLabellingValidity::No); } // find best swap auto it = candidate_swaps.end(); @@ -451,7 +485,8 @@ void LexiRoute::solve(unsigned lookahead) { std::pair chosen_swap = *it; this->mapping_frontier_->set_quantum_boundary(copy); - this->set_interacting_uids(); + this->set_interacting_uids( + AssignedOnly::No, CheckRoutingValidity::No, CheckLabellingValidity::No); std::pair check = this->check_bridge(chosen_swap, lookahead); // set for final time, to allow gates to be correctly inserted, but then leave // as is @@ -463,7 +498,8 @@ void LexiRoute::solve(unsigned lookahead) { this->mapping_frontier_->add_swap(chosen_swap.first, chosen_swap.second); } else { // only need to reset in bridge case - this->set_interacting_uids(); + this->set_interacting_uids( + AssignedOnly::No, CheckRoutingValidity::No, CheckLabellingValidity::No); auto add_ordered_bridge = [&](const Node& n) { auto it0 = this->mapping_frontier_->quantum_boundary->find(n); @@ -503,34 +539,7 @@ void LexiRoute::solve(unsigned lookahead) { add_ordered_bridge(chosen_swap.second); } } - return; -} - -LexiRouteRoutingMethod::LexiRouteRoutingMethod(unsigned _max_depth) - : max_depth_(_max_depth){}; - -std::pair LexiRouteRoutingMethod::routing_method( - std::shared_ptr& mapping_frontier, - const ArchitecturePtr& architecture) const { - LexiRoute lr(architecture, mapping_frontier); - lr.solve(this->max_depth_); - return {true, {}}; -} - -unsigned LexiRouteRoutingMethod::get_max_depth() const { - return this->max_depth_; -} - -nlohmann::json LexiRouteRoutingMethod::serialize() const { - nlohmann::json j; - j["depth"] = this->get_max_depth(); - j["name"] = "LexiRouteRoutingMethod"; - return j; -} - -LexiRouteRoutingMethod LexiRouteRoutingMethod::deserialize( - const nlohmann::json& j) { - return LexiRouteRoutingMethod(j.at("depth").get()); + return true; } } // namespace tket diff --git a/tket/src/Mapping/LexiRouteRoutingMethod.cpp b/tket/src/Mapping/LexiRouteRoutingMethod.cpp new file mode 100644 index 0000000000..672ed670cd --- /dev/null +++ b/tket/src/Mapping/LexiRouteRoutingMethod.cpp @@ -0,0 +1,45 @@ +// Copyright 2019-2022 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 "Mapping/LexiRouteRoutingMethod.hpp" + +namespace tket { + +LexiRouteRoutingMethod::LexiRouteRoutingMethod(unsigned _max_depth) + : max_depth_(_max_depth){}; + +std::pair LexiRouteRoutingMethod::routing_method( + MappingFrontier_ptr& mapping_frontier, + const ArchitecturePtr& architecture) const { + LexiRoute lr(architecture, mapping_frontier); + return {lr.solve(this->max_depth_), {}}; +} + +unsigned LexiRouteRoutingMethod::get_max_depth() const { + return this->max_depth_; +} + +nlohmann::json LexiRouteRoutingMethod::serialize() const { + nlohmann::json j; + j["depth"] = this->get_max_depth(); + j["name"] = "LexiRouteRoutingMethod"; + return j; +} + +LexiRouteRoutingMethod LexiRouteRoutingMethod::deserialize( + const nlohmann::json& j) { + return LexiRouteRoutingMethod(j.at("depth").get()); +} + +} // namespace tket \ No newline at end of file diff --git a/tket/src/Mapping/MappingFrontier.cpp b/tket/src/Mapping/MappingFrontier.cpp index ca719efee1..7ed60d5fde 100644 --- a/tket/src/Mapping/MappingFrontier.cpp +++ b/tket/src/Mapping/MappingFrontier.cpp @@ -220,19 +220,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. @@ -253,8 +242,9 @@ void MappingFrontier::advance_frontier_boundary( for (const UnitID& uid : uids) { nodes.push_back(Node(uid)); } - if (architecture->valid_operation( - this->circuit_.get_OpType_from_Vertex(vert), nodes)) { + if (this->valid_boundary_operation( + architecture, this->circuit_.get_Op_ptr_from_Vertex(vert), + nodes)) { // if no valid operation, boundary not updated and while loop terminates boundary_updated = true; for (const UnitID& uid : uids) { @@ -596,4 +586,59 @@ void MappingFrontier::merge_ancilla( this->bimaps_->final.left.erase(merge); } +bool MappingFrontier::valid_boundary_operation( + const ArchitecturePtr& architecture, const Op_ptr& op, + const std::vector& uids) const { + // boxes are never allowed + OpType ot = op->get_type(); + if (is_box_type(ot)) { + return false; + } + + if (ot == OpType::Conditional) { + Op_ptr cond_op_ptr = static_cast(*op).get_op(); + // conditional boxes are never allowed, too + OpType ot = cond_op_ptr->get_type(); + while (ot == OpType::Conditional) { + cond_op_ptr = static_cast(*op).get_op(); + ot = cond_op_ptr->get_type(); + if (is_box_type(ot)) { + return false; + } + } + } + + // Barriers are allways allowed + if (ot == OpType::Barrier) { + return true; + } + + // this currently allows unplaced single qubits gates + // this should be changes in the future + if (uids.size() == 1) { + return true; + } + + // allow two qubit gates only for placed and connected nodes + if (uids.size() == 2) { + if (architecture->node_exists(uids[0]) && + architecture->node_exists(uids[1]) && + architecture->bidirectional_edge_exists(uids[0], uids[1])) { + return true; + } + } else if (uids.size() == 3 && ot == OpType::BRIDGE) { + bool con_0_exists = + architecture->bidirectional_edge_exists(uids[0], uids[1]); + bool con_1_exists = + architecture->bidirectional_edge_exists(uids[2], uids[1]); + if (architecture->node_exists(uids[0]) && + architecture->node_exists(uids[1]) && + architecture->node_exists(uids[2]) && con_0_exists && con_1_exists) { + return true; + } + } + + return false; +} + } // namespace tket diff --git a/tket/src/Mapping/MappingManager.cpp b/tket/src/Mapping/MappingManager.cpp index 7ae89751af..cf14d25c4d 100644 --- a/tket/src/Mapping/MappingManager.cpp +++ b/tket/src/Mapping/MappingManager.cpp @@ -43,7 +43,7 @@ bool MappingManager::route_circuit_with_maps( // mapping_frontier tracks boundary between routed & un-routed in circuit // when initialised, boundary is over output edges of input vertices - std::shared_ptr mapping_frontier; + MappingFrontier_ptr mapping_frontier; if (maps) { mapping_frontier = std::make_shared(circuit, maps); } else { diff --git a/tket/src/Mapping/MultiGateReorder.cpp b/tket/src/Mapping/MultiGateReorder.cpp index 7a3d539e92..9ed429c7a5 100644 --- a/tket/src/Mapping/MultiGateReorder.cpp +++ b/tket/src/Mapping/MultiGateReorder.cpp @@ -20,7 +20,7 @@ namespace tket { MultiGateReorder::MultiGateReorder( const ArchitecturePtr &_architecture, - std::shared_ptr &_mapping_frontier) + MappingFrontier_ptr &_mapping_frontier) : architecture_(_architecture), mapping_frontier_(_mapping_frontier) { // This needs to be updated every time the frontier changes this->u_frontier_edges_ = @@ -31,8 +31,7 @@ MultiGateReorder::MultiGateReorder( // Traverse the DAG to the quantum frontier // to find the UnitID associated with an VertPort static UnitID get_unitid_from_vertex_port( - const std::shared_ptr &frontier, - const VertPort &vert_port) { + const MappingFrontier_ptr &frontier, const VertPort &vert_port) { VertPort current_vert_port = vert_port; while (true) { auto it = @@ -61,14 +60,14 @@ static bool is_multiq_quantum_gate(const Circuit &circ, const Vertex &vert) { } static bool is_physically_permitted( - const std::shared_ptr &frontier, - const ArchitecturePtr &arc_ptr, const Vertex &vert) { + const MappingFrontier_ptr &frontier, const ArchitecturePtr &arc_ptr, + const Vertex &vert) { std::vector nodes; for (port_t port = 0; port < frontier->circuit_.n_ports(vert); ++port) { nodes.push_back(Node(get_unitid_from_vertex_port(frontier, {vert, port}))); } - return arc_ptr->valid_operation( - frontier->circuit_.get_OpType_from_Vertex(vert), nodes); + return frontier->valid_boundary_operation( + arc_ptr, frontier->circuit_.get_Op_ptr_from_Vertex(vert), nodes); } // This method will try to commute a vertex to the quantum frontier @@ -239,7 +238,7 @@ MultiGateReorderRoutingMethod::MultiGateReorderRoutingMethod( : max_depth_(_max_depth), max_size_(_max_size) {} std::pair MultiGateReorderRoutingMethod::routing_method( - std::shared_ptr &mapping_frontier, + MappingFrontier_ptr &mapping_frontier, const ArchitecturePtr &architecture) const { MultiGateReorder mr(architecture, mapping_frontier); return {mr.solve(this->max_depth_, this->max_size_), {}}; diff --git a/tket/src/Mapping/RoutingMethodCircuit.cpp b/tket/src/Mapping/RoutingMethodCircuit.cpp index 6657dfacad..de6b400455 100644 --- a/tket/src/Mapping/RoutingMethodCircuit.cpp +++ b/tket/src/Mapping/RoutingMethodCircuit.cpp @@ -26,7 +26,7 @@ RoutingMethodCircuit::RoutingMethodCircuit( max_depth_(_max_depth){}; std::pair RoutingMethodCircuit::routing_method( - std::shared_ptr& mapping_frontier, + MappingFrontier_ptr& mapping_frontier, const ArchitecturePtr& architecture) const { // Produce subcircuit and circuit Subcircuit frontier_subcircuit = mapping_frontier->get_frontier_subcircuit( diff --git a/tket/src/Mapping/RoutingMethodJson.cpp b/tket/src/Mapping/RoutingMethodJson.cpp index 1f9479c89f..2461c5c4d1 100644 --- a/tket/src/Mapping/RoutingMethodJson.cpp +++ b/tket/src/Mapping/RoutingMethodJson.cpp @@ -14,6 +14,8 @@ #include "Mapping/RoutingMethodJson.hpp" +#include "Mapping/LexiLabelling.hpp" + namespace tket { void to_json(nlohmann::json& j, const RoutingMethod& rm) { j = rm.serialize(); } @@ -31,7 +33,10 @@ void to_json(nlohmann::json& j, const std::vector& rmp_v) { void from_json(const nlohmann::json& j, std::vector& rmp_v) { for (const auto& c : j) { std::string name = c.at("name").get(); - if (name == "LexiRouteRoutingMethod") { + if (name == "LexiLabellingMethod") { + rmp_v.push_back(std::make_shared( + LexiLabellingMethod::deserialize(c))); + } else if (name == "LexiRouteRoutingMethod") { rmp_v.push_back(std::make_shared( LexiRouteRoutingMethod::deserialize(c))); } else if (name == "RoutingMethod") { @@ -39,6 +44,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..e194d00e81 --- /dev/null +++ b/tket/src/Mapping/include/Mapping/BoxDecomposition.hpp @@ -0,0 +1,69 @@ +// Copyright 2019-2022 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 "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, + MappingFrontier_ptr& _mapping_frontier); + + /** + * Decompose any boxes in the next slice after the frontier + * + * @return True if Box is decomposed + */ + bool solve(); + + private: + // Architecture all new physical operations must respect + ArchitecturePtr architecture_; + MappingFrontier_ptr mapping_frontier_; +}; + +class BoxDecompositionRoutingMethod : public RoutingMethod { + public: + /** + * Decompose any boxes on the frontier + */ + BoxDecompositionRoutingMethod(); + + /** + * @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. + * + */ + std::pair routing_method( + MappingFrontier_ptr& mapping_frontier, + const ArchitecturePtr& architecture) const override; + + nlohmann::json serialize() const override; + + static BoxDecompositionRoutingMethod deserialize(const nlohmann::json& /*j*/); +}; + +} // namespace tket diff --git a/tket/src/Mapping/include/Mapping/LexiLabelling.hpp b/tket/src/Mapping/include/Mapping/LexiLabelling.hpp new file mode 100644 index 0000000000..9d5268e19b --- /dev/null +++ b/tket/src/Mapping/include/Mapping/LexiLabelling.hpp @@ -0,0 +1,46 @@ +// Copyright 2019-2022 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 "Mapping/LexiRoute.hpp" +#include "Mapping/RoutingMethod.hpp" + +namespace tket { + +class LexiLabellingMethod : public RoutingMethod { + public: + /** + * Checking and Routing methods redefined for dynamically assigning qubits to + * some Architecture. + */ + LexiLabellingMethod(){}; + + /** + * @param mapping_frontier Contains boundary of routed/unrouted circuit for + * modifying + * @param architecture Architecture providing physical constraints + * @return True if transformation made, Logical to Physical mapping at + * boundary due to modification. + * + */ + std::pair routing_method( + MappingFrontier_ptr& mapping_frontier, + const ArchitecturePtr& architecture) const override; + + nlohmann::json serialize() const override; + + static LexiLabellingMethod deserialize(const nlohmann::json& j); +}; +} // namespace tket \ No newline at end of file diff --git a/tket/src/Mapping/include/Mapping/LexiRoute.hpp b/tket/src/Mapping/include/Mapping/LexiRoute.hpp index 99229895a3..d711d10376 100644 --- a/tket/src/Mapping/include/Mapping/LexiRoute.hpp +++ b/tket/src/Mapping/include/Mapping/LexiRoute.hpp @@ -43,35 +43,59 @@ class LexiRoute { */ LexiRoute( const ArchitecturePtr& _architecture, - std::shared_ptr& _mapping_frontier); + MappingFrontier_ptr& _mapping_frontier); /** * When called, LexiRoute::solve will modify the Circuit held in * MappingFrontier object passed at class construction. Either a SWAP gate * will be inserted at the input boundary of the held Circuit or a CX gate * will be transformed into a BRIDGE gate. The added SWAP or BRIDGE gate will - * be valid for the Architecture passed at class construction. Additionally, - * an "unlabelled" Qubit in the Circuit may be relabelled to a Node in the - * Architecture, or an "unlabelled" Qubit may have its path merged with an - * ancilla qubit. + * be valid for the Architecture passed at class construction. * The decision making is based on the heuristic outlined in arXiv:1902.08091. * * @param lookahead Number of slices to lookahead at when determining best * SWAP or BRIDGE + * + * @return True if solve has modified circuit for mapping purposes */ - void solve(unsigned lookahead); + bool solve(unsigned lookahead); + + /** + * When called an "unlabelled" Qubit in the Circuit may be relabelled to a + * Node in the Architecture, or an "unlabelled" Qubit may have its path merged + * with an ancilla qubit. The decision making is based on the heuristic + * outlined in arXiv:1902.08091. + * + * @return True if solve_labelling has modified circuit for mapping purposes + */ + bool solve_labelling(); private: + /** Only considers two-qubit vertices if both qubits are labelled to + * Architecture */ + enum class AssignedOnly { Yes, No }; + /** Returns a bool confirming if vertices are valid for LexiRoute::solve */ + enum class CheckRoutingValidity { Yes, No }; + /** Returns a bool confirming if vertices are valid for + * LexiRoute::solve_labelling */ + enum class CheckLabellingValidity { Yes, No }; + /** * this->interacting_uids_ attribute is a map where key is one UnitID * and value is the UnitID it needs to be adjacent to. * This map is implicitly updated whenever a logical SWAP is inserted. * set_interacting_uids determines this map for the first parallel set of * interacting UnitID in the Circuit held in this->mapping_frontier_ - * @param assigned_only If true, only include interactions where both UnitID + * @param assigned_only If Yes, only include interactions where both UnitID * are in this->architecture_. + * @param route_check If Yes, return false if solve not possible + * @param label_check If Yes, return false if solve_labelling not possible + * + * @return bool depending on ENUM conditions */ - void set_interacting_uids(bool assigned_only = false); + bool set_interacting_uids( + AssignedOnly assigned_only, CheckRoutingValidity route_check, + CheckLabellingValidity label_check); /** * If there is some "free" Node in Architecture at distance "distances" on @@ -147,7 +171,7 @@ class LexiRoute { // Architecture all new physical operations must respect ArchitecturePtr architecture_; // Contains circuit for finding SWAP from and non-routed/routed boundary - std::shared_ptr& mapping_frontier_; + MappingFrontier_ptr& mapping_frontier_; // Map between UnitID and UnitID they interact with at boundary unit_map_t interacting_uids_; // Map between original circuit UnitID and new UnitID due to dynamic @@ -157,43 +181,4 @@ class LexiRoute { std::set assigned_nodes_; }; -// Child class of RoutingMethod, with overloaded methods for routing -// MappingFrontier objects -class LexiRouteRoutingMethod : public RoutingMethod { - public: - /** - * Checking and Routing methods redefined using LexiRoute. Only circuit depth, - * corresponding to lookahead, is a required parameter. - * - * @param _max_depth Number of layers of gates checked inr outed subcircuit. - */ - LexiRouteRoutingMethod(unsigned _max_depth = 100); - - /** - * @param mapping_frontier Contains boundary of routed/unrouted circuit for - * modifying - * @param architecture Architecture providing physical constraints - * - * @return true bool, map between relabelled Qubit, always empty. - * - */ - std::pair routing_method( - std::shared_ptr& mapping_frontier, - const ArchitecturePtr& architecture) const override; - - /** - * @return Max depth used in lookahead - */ - unsigned get_max_depth() const; - - nlohmann::json serialize() const override; - - static LexiRouteRoutingMethod deserialize(const nlohmann::json& j); - - private: - unsigned max_depth_; -}; - -JSON_DECL(LexiRouteRoutingMethod); - } // namespace tket diff --git a/tket/src/Mapping/include/Mapping/LexiRouteRoutingMethod.hpp b/tket/src/Mapping/include/Mapping/LexiRouteRoutingMethod.hpp new file mode 100644 index 0000000000..be13fc51a9 --- /dev/null +++ b/tket/src/Mapping/include/Mapping/LexiRouteRoutingMethod.hpp @@ -0,0 +1,60 @@ +// Copyright 2019-2022 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 "Mapping/LexiRoute.hpp" +#include "Mapping/RoutingMethod.hpp" + +namespace tket { + +class LexiRouteRoutingMethod : public RoutingMethod { + public: + /** + * Checking and Routing methods redefined using LexiRoute. Only circuit depth, + * corresponding to lookahead, is a required parameter. + * + * @param _max_depth Number of layers of gates checked inr outed subcircuit. + */ + LexiRouteRoutingMethod(unsigned _max_depth = 100); + + /** + * @param mapping_frontier Contains boundary of routed/unrouted circuit for + * modifying + * @param architecture Architecture providing physical constraints + * + * @return True if modification made, map between relabelled Qubit, always + * empty. + * + */ + std::pair routing_method( + MappingFrontier_ptr& mapping_frontier, + const ArchitecturePtr& architecture) const override; + + /** + * @return Max depth used in lookahead + */ + unsigned get_max_depth() const; + + nlohmann::json serialize() const override; + + static LexiRouteRoutingMethod deserialize(const nlohmann::json& j); + + private: + unsigned max_depth_; +}; + +JSON_DECL(LexiRouteRoutingMethod); + +} // namespace tket \ No newline at end of file diff --git a/tket/src/Mapping/include/Mapping/MappingFrontier.hpp b/tket/src/Mapping/include/Mapping/MappingFrontier.hpp index 5fdbc695fc..d6278fe9ed 100644 --- a/tket/src/Mapping/include/Mapping/MappingFrontier.hpp +++ b/tket/src/Mapping/include/Mapping/MappingFrontier.hpp @@ -179,6 +179,19 @@ struct MappingFrontier { * @param new_boundary Object to reassign with. */ void set_quantum_boundary(const unit_vertport_frontier_t& new_boundary); + + /** + * Returns true if the given operation acting on the given nodes + * can be executed on the Architecture connectivity graph. + * @param architecture given architecture to check the operation on + * @param op operation to check + * @param uids vector of nodes which is included in the operation + */ + bool valid_boundary_operation( + const ArchitecturePtr& architecture, const Op_ptr& op, + const std::vector& uids) const; }; +typedef std::shared_ptr MappingFrontier_ptr; + } // namespace tket \ 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 63434fb383..cb5162f97c 100644 --- a/tket/src/Mapping/include/Mapping/MultiGateReorder.hpp +++ b/tket/src/Mapping/include/Mapping/MultiGateReorder.hpp @@ -28,7 +28,7 @@ class MultiGateReorder { */ MultiGateReorder( const ArchitecturePtr& _architecture, - std::shared_ptr& _mapping_frontier); + MappingFrontier_ptr& _mapping_frontier); /** * Try to commute any multi-qubit gates to the quantum frontier @@ -42,7 +42,7 @@ class MultiGateReorder { private: // Architecture all new physical operations must respect ArchitecturePtr architecture_; - std::shared_ptr mapping_frontier_; + MappingFrontier_ptr mapping_frontier_; EdgeVec u_frontier_edges_; }; @@ -66,7 +66,7 @@ class MultiGateReorderRoutingMethod : public RoutingMethod { * */ std::pair routing_method( - std::shared_ptr& mapping_frontier, + MappingFrontier_ptr& mapping_frontier, const ArchitecturePtr& architecture) const override; nlohmann::json serialize() const override; diff --git a/tket/src/Mapping/include/Mapping/RoutingMethod.hpp b/tket/src/Mapping/include/Mapping/RoutingMethod.hpp index de4e440ebf..ac29cbaf37 100644 --- a/tket/src/Mapping/include/Mapping/RoutingMethod.hpp +++ b/tket/src/Mapping/include/Mapping/RoutingMethod.hpp @@ -43,7 +43,7 @@ class RoutingMethod { * */ virtual std::pair routing_method( - std::shared_ptr& /*mapping_frontier*/, + MappingFrontier_ptr& /*mapping_frontier*/, const ArchitecturePtr& /*architecture*/) const { return {false, {}}; } diff --git a/tket/src/Mapping/include/Mapping/RoutingMethodCircuit.hpp b/tket/src/Mapping/include/Mapping/RoutingMethodCircuit.hpp index 3189bb6504..ea5de4455e 100644 --- a/tket/src/Mapping/include/Mapping/RoutingMethodCircuit.hpp +++ b/tket/src/Mapping/include/Mapping/RoutingMethodCircuit.hpp @@ -43,7 +43,7 @@ class RoutingMethodCircuit : public RoutingMethod { * */ std::pair routing_method( - std::shared_ptr& mapping_frontier, + MappingFrontier_ptr& mapping_frontier, const ArchitecturePtr& architecture) const; private: diff --git a/tket/src/Mapping/include/Mapping/RoutingMethodJson.hpp b/tket/src/Mapping/include/Mapping/RoutingMethodJson.hpp index 9cbdb22e90..475e98ffcb 100644 --- a/tket/src/Mapping/include/Mapping/RoutingMethodJson.hpp +++ b/tket/src/Mapping/include/Mapping/RoutingMethodJson.hpp @@ -14,7 +14,9 @@ #pragma once -#include "Mapping/LexiRoute.hpp" +#include "Mapping/BoxDecomposition.hpp" +#include "Mapping/LexiLabelling.hpp" +#include "Mapping/LexiRouteRoutingMethod.hpp" #include "Mapping/MultiGateReorder.hpp" #include "Mapping/RoutingMethod.hpp" #include "Utils/Json.hpp" diff --git a/tket/src/Predicates/PassGenerators.cpp b/tket/src/Predicates/PassGenerators.cpp index 95af0feb2e..23ee444480 100644 --- a/tket/src/Predicates/PassGenerators.cpp +++ b/tket/src/Predicates/PassGenerators.cpp @@ -20,8 +20,9 @@ #include "Circuit/CircPool.hpp" #include "Circuit/Circuit.hpp" #include "Converters/PhasePoly.hpp" +#include "Mapping/LexiLabelling.hpp" +#include "Mapping/LexiRoute.hpp" #include "Mapping/MappingManager.hpp" -#include "Mapping/RoutingMethod.hpp" #include "Placement/Placement.hpp" #include "Predicates/CompilationUnit.hpp" #include "Predicates/CompilerPass.hpp" @@ -213,9 +214,10 @@ PassPtr gen_full_mapping_pass( } PassPtr gen_default_mapping_pass(const Architecture& arc, bool delay_measures) { - PlacementPtr pp = std::make_shared(arc); - RoutingMethodPtr rmw = std::make_shared(100); - PassPtr return_pass = gen_full_mapping_pass(arc, pp, {rmw}); + PassPtr return_pass = gen_full_mapping_pass( + arc, std::make_shared(arc), + {std::make_shared(), + std::make_shared()}); if (delay_measures) { return_pass = return_pass >> DelayMeasures(); } diff --git a/tket/tests/Circuit/test_Circ.cpp b/tket/tests/Circuit/test_Circ.cpp index eeaa74e0cc..af5c647588 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..00d4b19323 --- /dev/null +++ b/tket/tests/test_BoxDecompRoutingMethod.cpp @@ -0,0 +1,148 @@ +// Copyright 2019-2022 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 "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); + MappingFrontier_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); + MappingFrontier_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); + MappingFrontier_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_CompilerPass.cpp b/tket/tests/test_CompilerPass.cpp index 15855ae98b..875fed5251 100644 --- a/tket/tests/test_CompilerPass.cpp +++ b/tket/tests/test_CompilerPass.cpp @@ -17,6 +17,8 @@ #include "Circuit/CircPool.hpp" #include "Circuit/Circuit.hpp" +#include "Mapping/LexiLabelling.hpp" +#include "Mapping/LexiRoute.hpp" #include "OpType/OpType.hpp" #include "OpType/OpTypeFunctions.hpp" #include "Placement/Placement.hpp" @@ -254,9 +256,10 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") { CompilationUnit cu(circ, preds); PlacementPtr pp = std::make_shared(grid); - LexiRouteRoutingMethod lrrm(50); - RoutingMethodPtr rmw = std::make_shared(lrrm); - PassPtr cp_route = gen_full_mapping_pass(grid, pp, {rmw}); + PassPtr cp_route = gen_full_mapping_pass( + grid, pp, + {std::make_shared(), + std::make_shared()}); PassPtr all_passes = SynthesiseHQS() >> SynthesiseOQC() >> SynthesiseUMD() >> SynthesiseTket() >> cp_route; @@ -902,9 +905,9 @@ SCENARIO("DecomposeArbitrarilyControlledGates test") { SCENARIO("Precomposed passes successfully compose") { GIVEN("gen_directed_cx_routing_pass") { RingArch arc(6); - LexiRouteRoutingMethod lrrm(50); - RoutingMethodPtr rmw = std::make_shared(lrrm); - REQUIRE_NOTHROW(gen_directed_cx_routing_pass(arc, {rmw})); + REQUIRE_NOTHROW(gen_directed_cx_routing_pass( + arc, {std::make_shared(), + std::make_shared()})); } } @@ -925,9 +928,9 @@ SCENARIO("Test Pauli Graph Synthesis Pass") { SCENARIO("Compose Pauli Graph synthesis Passes") { RingArch arc(10); - LexiRouteRoutingMethod lrrm(50); - RoutingMethodPtr rmw = std::make_shared(lrrm); - PassPtr dir_pass = gen_directed_cx_routing_pass(arc, {rmw}); + PassPtr dir_pass = gen_directed_cx_routing_pass( + arc, {std::make_shared(), + std::make_shared()}); GIVEN("Special UCC Synthesis") { PassPtr spec_ucc = gen_special_UCC_synthesis(); REQUIRE_NOTHROW(spec_ucc >> dir_pass); @@ -1010,9 +1013,10 @@ SCENARIO("Commute measurements to the end of a circuit") { Architecture line({{0, 1}, {1, 2}, {2, 3}}); PlacementPtr pp = std::make_shared(line); - LexiRouteRoutingMethod lrrm(50); - RoutingMethodPtr rmw = std::make_shared(lrrm); - PassPtr route_pass = gen_full_mapping_pass(line, pp, {rmw}); + PassPtr route_pass = gen_full_mapping_pass( + line, pp, + {std::make_shared(), + std::make_shared()}); CompilationUnit cu(test); route_pass->apply(cu); REQUIRE(delay_pass->apply(cu)); @@ -1085,7 +1089,10 @@ SCENARIO("CX mapping pass") { LexiRouteRoutingMethod lrrm(50); RoutingMethodPtr rmw = std::make_shared(lrrm); CompilationUnit cu_route(c_placed); - gen_routing_pass(line, {rmw})->apply(cu_route); + gen_routing_pass( + line, {std::make_shared(), + std::make_shared()}) + ->apply(cu_route); const Circuit& c_routed = cu_route.get_circ_ref(); // Rebase again diff --git a/tket/tests/test_LexiRoute.cpp b/tket/tests/test_LexiRoute.cpp index ac20c92285..8402f24dc5 100644 --- a/tket/tests/test_LexiRoute.cpp +++ b/tket/tests/test_LexiRoute.cpp @@ -14,6 +14,7 @@ #include +#include "Mapping/LexiLabelling.hpp" #include "Mapping/LexiRoute.hpp" #include "Mapping/MappingManager.hpp" #include "Mapping/Verification.hpp" @@ -23,8 +24,10 @@ #include "Predicates/PassLibrary.hpp" #include "Transformations/Decomposition.hpp" #include "testutil.hpp" + namespace tket { -SCENARIO("Test LexiRoute::solve") { + +SCENARIO("Test LexiRoute::solve and LexiRoute::solve_labelling") { std::vector nodes = {Node("test_node", 0), Node("test_node", 1), Node("test_node", 2), Node("node_test", 3), Node("node_test", 4), Node("node_test", 5), @@ -58,8 +61,7 @@ SCENARIO("Test LexiRoute::solve") { {qubits[0], nodes[0]}, {qubits[1], nodes[1]}, {qubits[2], nodes[2]}, {qubits[3], nodes[3]}, {qubits[4], nodes[6]}, {qubits[5], nodes[5]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); LexiRoute lr(shared_arc, mf); lr.solve(4); @@ -88,21 +90,19 @@ SCENARIO("Test LexiRoute::solve") { {qubits[3], nodes[3]}, {qubits[5], nodes[5]}}; circ.rename_units(rename_map); - std::shared_ptr mf0 = - std::make_shared(circ); + MappingFrontier_ptr mf0 = std::make_shared(circ); LexiRoute lr(shared_arc, mf0); - - lr.solve(4); + lr.solve_labelling(); REQUIRE(mf0->circuit_.n_gates() == 3); rename_map = {{qubits[4], nodes[6]}}; mf0->circuit_.rename_units(rename_map); - std::shared_ptr mf1 = - std::make_shared(circ); + MappingFrontier_ptr mf1 = std::make_shared(circ); LexiRoute lr1(shared_arc, mf1); lr1.solve(4); + std::vector commands = mf1->circuit_.get_commands(); Command swap_c = commands[1]; unit_vector_t uids = {nodes[1], nodes[2]}; @@ -127,8 +127,7 @@ SCENARIO("Test LexiRoute::solve") { {qubits[3], nodes[3]}, {qubits[4], nodes[4]}, {qubits[5], nodes[5]}, {qubits[6], nodes[6]}, {qubits[7], nodes[7]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); LexiRoute lr(shared_arc, mf); lr.solve(4); @@ -151,10 +150,9 @@ SCENARIO("Test LexiRoute::solve") { circ.add_op(OpType::CX, {qubits[0], qubits[3]}); circ.add_op(OpType::CX, {qubits[3], qubits[4]}); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); LexiRoute lr0(shared_arc, mf); - lr0.solve(20); + lr0.solve_labelling(); std::vector commands = mf->circuit_.get_commands(); REQUIRE(commands.size() == 4); Command c = commands[0]; @@ -163,19 +161,19 @@ SCENARIO("Test LexiRoute::solve") { mf->advance_frontier_boundary(shared_arc); LexiRoute lr1(shared_arc, mf); - lr1.solve(20); + lr1.solve_labelling(); uids = {nodes[2], nodes[3]}; REQUIRE(mf->circuit_.get_commands()[1].get_args() == uids); mf->advance_frontier_boundary(shared_arc); LexiRoute lr2(shared_arc, mf); - lr2.solve(20); + lr2.solve_labelling(); uids = {nodes[2], nodes[5]}; REQUIRE(mf->circuit_.get_commands()[2].get_args() == uids); mf->advance_frontier_boundary(shared_arc); LexiRoute lr3(shared_arc, mf); - lr3.solve(20); + lr3.solve_labelling(); uids = {nodes[5], nodes[6]}; REQUIRE(mf->circuit_.get_commands()[3].get_args() == uids); } @@ -192,8 +190,7 @@ SCENARIO("Test LexiRoute::solve") { {qubits[3], nodes[7]}, {qubits[4], nodes[2]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); mf->advance_frontier_boundary(shared_arc); LexiRoute lr(shared_arc, mf); @@ -216,8 +213,7 @@ SCENARIO("Test LexiRoute::solve") { {qubits[3], nodes[7]}, {qubits[4], nodes[2]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); mf->advance_frontier_boundary(shared_arc); LexiRoute lr(shared_arc, mf); @@ -237,8 +233,7 @@ SCENARIO("Test LexiRoute::solve") { {qubits[3], nodes[7]}, {qubits[4], nodes[2]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); mf->advance_frontier_boundary(shared_arc); LexiRoute lr(shared_arc, mf); @@ -258,8 +253,7 @@ SCENARIO("Test LexiRoute::solve") { {qubits[3], nodes[7]}, {qubits[4], nodes[2]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); mf->advance_frontier_boundary(shared_arc); LexiRoute lr(shared_arc, mf); @@ -290,8 +284,7 @@ SCENARIO("Test LexiRoute::solve") { {qubits[0], nodes[2]}, {qubits[1], nodes[4]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); mf->advance_frontier_boundary(shared_arc); LexiRoute lr0(shared_arc, mf); lr0.solve(20); @@ -299,7 +292,7 @@ SCENARIO("Test LexiRoute::solve") { mf->advance_frontier_boundary(shared_arc); LexiRoute lr1(shared_arc, mf); - lr1.solve(20); + lr1.solve_labelling(); REQUIRE(circ.all_qubits()[0] == nodes[3]); } @@ -331,11 +324,10 @@ SCENARIO("Test LexiRoute::solve") { {qubits[0], nodes[2]}, {qubits[1], nodes[4]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); mf->advance_frontier_boundary(shared_arc); LexiRoute lr0(shared_arc, mf); - lr0.solve(20); + lr0.solve_labelling(); mf->advance_frontier_boundary(shared_arc); LexiRoute lr1(shared_arc, mf); @@ -367,12 +359,12 @@ SCENARIO("Test LexiRoute::solve") { {qubits[0], nodes[2]}, {qubits[1], nodes[4]}, {qubits[3], nodes[3]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); mf->ancilla_nodes_.insert(nodes[3]); mf->advance_frontier_boundary(shared_arc); + LexiRoute lr0(shared_arc, mf); - lr0.solve(20); + lr0.solve_labelling(); REQUIRE(circ.all_qubits()[1] == nodes[4]); REQUIRE(circ.all_qubits()[0] == nodes[3]); @@ -397,8 +389,7 @@ SCENARIO("Test LexiRoute::solve") { {qubits[0], nodes[0]}, {qubits[1], nodes[1]}, {qubits[2], nodes[2]}, {qubits[3], nodes[3]}, {qubits[4], nodes[6]}, {qubits[5], nodes[5]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); LexiRoute lr(shared_arc, mf); lr.solve(4); @@ -410,8 +401,8 @@ SCENARIO("Test LexiRoute::solve") { REQUIRE(*swap_c.get_op_ptr() == *get_op_ptr(OpType::SWAP)); } GIVEN( - "Labelling is required, but there are no free remaining qubits, for one " - "updated label, order 0.") { + "Labelling is required, but there are no free remaining qubits, for" + " one updated label, order 0.") { Circuit circ(9); std::vector qubits = circ.all_qubits(); circ.add_op(OpType::CX, {qubits[1], qubits[8]}); @@ -425,14 +416,14 @@ SCENARIO("Test LexiRoute::solve") { {qubits[3], nodes[3]}, {qubits[4], nodes[4]}, {qubits[5], nodes[5]}, {qubits[6], nodes[6]}, {qubits[7], nodes[7]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); LexiRoute lr(shared_arc, mf); - REQUIRE_THROWS_AS(lr.solve(1), LexiRouteError); + + REQUIRE_THROWS_AS(lr.solve_labelling(), LexiRouteError); } GIVEN( - "Labelling is required, but there are no free remaining qubits, for one " - "updated label, order 1.") { + "Labelling is required, but there are no free remaining qubits, for " + "one updated label, order 1.") { Circuit circ(9); std::vector qubits = circ.all_qubits(); circ.add_op(OpType::CX, {qubits[1], qubits[8]}); @@ -446,14 +437,13 @@ SCENARIO("Test LexiRoute::solve") { {qubits[3], nodes[3]}, {qubits[4], nodes[4]}, {qubits[5], nodes[5]}, {qubits[6], nodes[6]}, {qubits[7], nodes[7]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); LexiRoute lr(shared_arc, mf); - REQUIRE_THROWS_AS(lr.solve(1), LexiRouteError); + REQUIRE_THROWS_AS(lr.solve_labelling(), LexiRouteError); } GIVEN( - "Labelling is required, but there are no free remaining qubits, for two " - "updated labels.") { + "Labelling is required, but there are no free remaining qubits, for" + "two updated labels.") { Circuit circ(10); std::vector qubits = circ.all_qubits(); circ.add_op(OpType::CX, {qubits[9], qubits[8]}); @@ -467,13 +457,237 @@ SCENARIO("Test LexiRoute::solve") { {qubits[3], nodes[3]}, {qubits[4], nodes[4]}, {qubits[5], nodes[5]}, {qubits[6], nodes[6]}, {qubits[7], nodes[7]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); LexiRoute lr(shared_arc, mf); - REQUIRE_THROWS_AS(lr.solve(1), LexiRouteError); + REQUIRE_THROWS_AS(lr.solve_labelling(), LexiRouteError); } } +SCENARIO("Test LexiLabellingMethod") { + std::vector nodes = { + Node("test_node", 0), Node("test_node", 1), Node("test_node", 2), + Node("node_test", 3), Node("node_test", 4)}; + + // straight line + Architecture architecture( + {{nodes[0], nodes[1]}, + {nodes[1], nodes[2]}, + {nodes[2], nodes[3]}, + {nodes[3], nodes[4]}}); + ArchitecturePtr shared_arc = std::make_shared(architecture); + GIVEN("No qubit to label, empty frontier, routing_method false.") { + Circuit circ(5); + MappingFrontier_ptr mf = std::make_shared(circ); + LexiLabellingMethod lrm; + REQUIRE(!lrm.routing_method(mf, shared_arc).first); + } + GIVEN("No qubit to label, partially filled frontier, routing_method false.") { + Circuit circ(5); + std::vector qubits = circ.all_qubits(); + circ.add_op(OpType::CX, {qubits[0], qubits[4]}); + circ.add_op(OpType::CZ, {qubits[1], qubits[2]}); + circ.add_op(OpType::ZZPhase, 0.3, {qubits[3], qubits[0]}); + std::map rename_map = { + {qubits[0], nodes[0]}, + {qubits[1], nodes[1]}, + {qubits[2], nodes[2]}, + {qubits[3], nodes[3]}, + {qubits[4], nodes[4]}}; + circ.rename_units(rename_map); + MappingFrontier_ptr mf = std::make_shared(circ); + LexiLabellingMethod lrm; + REQUIRE(!lrm.routing_method(mf, shared_arc).first); + } + GIVEN("Qubit to label, but casually restricted, routing_method false.") { + Circuit circ(5); + std::vector qubits = circ.all_qubits(); + circ.add_op(OpType::CX, {qubits[0], qubits[4]}); + circ.add_op(OpType::CZ, {qubits[1], qubits[2]}); + circ.add_op(OpType::ZZPhase, 0.3, {qubits[3], qubits[0]}); + std::map rename_map = { + {qubits[0], nodes[0]}, + {qubits[1], nodes[1]}, + {qubits[2], nodes[2]}, + {qubits[4], nodes[4]}}; + circ.rename_units(rename_map); + MappingFrontier_ptr mf = std::make_shared(circ); + LexiLabellingMethod lrm; + REQUIRE(!lrm.routing_method(mf, shared_arc).first); + } + GIVEN( + "Two Qubit to label in future slice, causally restricted, " + "routing_method false.") { + Circuit circ(5); + std::vector qubits = circ.all_qubits(); + circ.add_op(OpType::CX, {qubits[0], qubits[1]}); + circ.add_op(OpType::CZ, {qubits[1], qubits[2]}); + circ.add_op(OpType::CZ, {qubits[2], qubits[3]}); + circ.add_op(OpType::ZZPhase, 0.3, {qubits[3], qubits[4]}); + std::map rename_map = { + {qubits[0], nodes[0]}, {qubits[1], nodes[1]}, {qubits[2], nodes[2]}}; + circ.rename_units(rename_map); + MappingFrontier_ptr mf = std::make_shared(circ); + LexiLabellingMethod lrm; + REQUIRE(!lrm.routing_method(mf, shared_arc).first); + } + GIVEN("Three Qubit Gate, all labelled, first slice, routing_method false.") { + Circuit circ(5); + std::vector qubits = circ.all_qubits(); + circ.add_op(OpType::CX, {qubits[0], qubits[4]}); + circ.add_op(OpType::CCX, {qubits[1], qubits[2], qubits[3]}); + std::map rename_map = { + {qubits[0], nodes[0]}, + {qubits[1], nodes[1]}, + {qubits[2], nodes[2]}, + {qubits[3], nodes[3]}, + {qubits[4], nodes[4]}}; + circ.rename_units(rename_map); + MappingFrontier_ptr mf = std::make_shared(circ); + LexiLabellingMethod lrm; + REQUIRE(!lrm.routing_method(mf, shared_arc).first); + } + GIVEN("One unlabelled qubit, one slice, check and route.") { + Circuit circ(5); + std::vector qubits = circ.all_qubits(); + circ.add_op(OpType::CX, {qubits[0], qubits[1]}); + circ.add_op(OpType::CX, {qubits[2], qubits[3]}); + std::map rename_map = { + {qubits[0], nodes[0]}, {qubits[1], nodes[1]}, {qubits[2], nodes[2]}}; + circ.rename_units(rename_map); + MappingFrontier_ptr mf = std::make_shared(circ); + VertPort pre_label = + mf->quantum_boundary->get().find(qubits[3])->second; + LexiLabellingMethod lrm; + std::pair out = lrm.routing_method(mf, shared_arc); + REQUIRE(out.first); + REQUIRE( + mf->quantum_boundary->get().find(qubits[3]) == + mf->quantum_boundary->get().end()); + VertPort post_label = + mf->quantum_boundary->get().find(nodes[3])->second; + REQUIRE(pre_label == post_label); + } + GIVEN( + "One unlabelled qubit, two slices, lookahead for better solution, check" + " and route.") { + Circuit circ(5); + std::vector qubits = circ.all_qubits(); + circ.add_op(OpType::CX, {qubits[0], qubits[1]}); + circ.add_op(OpType::ZZPhase, 0.8, {qubits[2], qubits[3]}); + circ.add_op(OpType::CZ, {qubits[2], qubits[0]}); + + std::map rename_map = { + {qubits[0], nodes[0]}, {qubits[1], nodes[1]}, {qubits[3], nodes[3]}}; + circ.rename_units(rename_map); + MappingFrontier_ptr mf = std::make_shared(circ); + VertPort pre_label = + mf->quantum_boundary->get().find(qubits[2])->second; + LexiLabellingMethod lrm; + + std::pair out = lrm.routing_method(mf, shared_arc); + REQUIRE(out.first); + REQUIRE( + mf->quantum_boundary->get().find(qubits[2]) == + mf->quantum_boundary->get().end()); + VertPort post_label = + mf->quantum_boundary->get().find(nodes[2])->second; + REQUIRE(pre_label == post_label); + } + GIVEN("Two unlabelled qubits, one slice, check and route.") { + Circuit circ(5); + std::vector qubits = circ.all_qubits(); + circ.add_op(OpType::CX, {qubits[0], qubits[1]}); + circ.add_op(OpType::ZZPhase, 0.8, {qubits[2], qubits[3]}); + + std::map rename_map = { + {qubits[2], nodes[2]}, {qubits[1], nodes[1]}}; + circ.rename_units(rename_map); + MappingFrontier_ptr mf = std::make_shared(circ); + VertPort pre_label_0 = + mf->quantum_boundary->get().find(qubits[0])->second; + VertPort pre_label_3 = + mf->quantum_boundary->get().find(qubits[3])->second; + LexiLabellingMethod lrm; + std::pair out = lrm.routing_method(mf, shared_arc); + REQUIRE(out.first); + REQUIRE( + mf->quantum_boundary->get().find(qubits[0]) == + mf->quantum_boundary->get().end()); + REQUIRE( + mf->quantum_boundary->get().find(qubits[3]) == + mf->quantum_boundary->get().end()); + VertPort post_label_0 = + mf->quantum_boundary->get().find(nodes[0])->second; + REQUIRE(pre_label_0 == post_label_0); + VertPort post_label_3 = + mf->quantum_boundary->get().find(nodes[3])->second; + REQUIRE(pre_label_3 == post_label_3); + } + GIVEN("Two unlabelled qubits, two slices, lookahead, check and route.") { + Circuit circ(5); + std::vector qubits = circ.all_qubits(); + circ.add_op(OpType::CX, {qubits[2], qubits[1]}); + circ.add_op(OpType::ZZPhase, 0.8, {qubits[4], qubits[3]}); + circ.add_op(OpType::CX, {qubits[2], qubits[4]}); + + std::map rename_map = { + {qubits[4], nodes[4]}, {qubits[1], nodes[1]}}; + circ.rename_units(rename_map); + MappingFrontier_ptr mf = std::make_shared(circ); + VertPort pre_label_0 = + mf->quantum_boundary->get().find(qubits[2])->second; + VertPort pre_label_3 = + mf->quantum_boundary->get().find(qubits[3])->second; + LexiLabellingMethod lrm; + std::pair out = lrm.routing_method(mf, shared_arc); + REQUIRE(out.first); + REQUIRE( + mf->quantum_boundary->get().find(qubits[2]) == + mf->quantum_boundary->get().end()); + REQUIRE( + mf->quantum_boundary->get().find(qubits[3]) == + mf->quantum_boundary->get().end()); + VertPort post_label_0 = + mf->quantum_boundary->get().find(nodes[0])->second; + REQUIRE(pre_label_0 == post_label_0); + VertPort post_label_3 = + mf->quantum_boundary->get().find(nodes[3])->second; + REQUIRE(pre_label_3 == post_label_3); + } + GIVEN( + "Two unlabelled qubits, two slices, lookahead unrouted, check and " + "route.") { + Circuit circ(5); + std::vector qubits = circ.all_qubits(); + circ.add_op(OpType::CX, {qubits[2], qubits[1]}); + circ.add_op(OpType::ZZPhase, 0.8, {qubits[4], qubits[3]}); + circ.add_op(OpType::CX, {qubits[2], qubits[0]}); + + std::map rename_map = { + {qubits[4], nodes[4]}, {qubits[1], nodes[1]}}; + circ.rename_units(rename_map); + MappingFrontier_ptr mf = std::make_shared(circ); + VertPort pre_label_0 = + mf->quantum_boundary->get().find(qubits[2])->second; + VertPort pre_label_3 = + mf->quantum_boundary->get().find(qubits[3])->second; + LexiLabellingMethod lrm; + std::pair out = lrm.routing_method(mf, shared_arc); + REQUIRE(out.first); + REQUIRE( + mf->quantum_boundary->get().find(qubits[2]) == + mf->quantum_boundary->get().end()); + REQUIRE( + mf->quantum_boundary->get().find(qubits[3]) == + mf->quantum_boundary->get().end()); + VertPort post_label_0 = + mf->quantum_boundary->get().find(nodes[0])->second; + REQUIRE(pre_label_0 == post_label_0); + VertPort post_label_3 = + mf->quantum_boundary->get().find(nodes[3])->second; + REQUIRE(pre_label_3 == post_label_3); + } +} SCENARIO("Test LexiRouteRoutingMethod") { std::vector nodes = { Node("test_node", 0), Node("test_node", 1), Node("test_node", 2), @@ -521,8 +735,7 @@ SCENARIO("Test LexiRouteRoutingMethod") { {qubits[9], nodes[9]}, {qubits[10], nodes[10]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); LexiRouteRoutingMethod lrrm(100); std::pair bool_init_map = lrrm.routing_method(mf, shared_arc); @@ -563,8 +776,7 @@ SCENARIO("Test LexiRouteRoutingMethod") { {qubits[9], nodes[9]}, {qubits[10], nodes[10]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); LexiRouteRoutingMethod lrrm(100); std::pair bool_init_map = lrrm.routing_method(mf, shared_arc); @@ -578,7 +790,7 @@ SCENARIO("Test LexiRouteRoutingMethod") { REQUIRE(*swap_c.get_op_ptr() == *get_op_ptr(OpType::SWAP)); } } -SCENARIO("Test MappingManager::route_circuit with lc_route_subcircuit") { +SCENARIO("Test MappingManager with LexiRouteRoutingMethod and LexiLabelling") { GIVEN("11 Node Architecture, 11 Qubit circuit, multiple SWAP required.") { std::vector nodes = { Node("test_node", 0), Node("test_node", 1), Node("test_node", 2), @@ -607,7 +819,7 @@ SCENARIO("Test MappingManager::route_circuit with lc_route_subcircuit") { ArchitecturePtr shared_arc = std::make_shared(architecture); Circuit circ(11); std::vector qubits = circ.all_qubits(); - for (unsigned i = 0; i < 10; i++) { + for (unsigned i = 0; i < 11; i++) { circ.add_op(OpType::CX, {qubits[0], qubits[4]}); circ.add_op(OpType::CX, {qubits[6], qubits[7]}); circ.add_op(OpType::CX, {qubits[1], qubits[10]}); @@ -626,11 +838,12 @@ SCENARIO("Test MappingManager::route_circuit with lc_route_subcircuit") { PassPtr dec = gen_decompose_routing_gates_to_cxs_pass(architecture, false); MappingManager mm(shared_arc); - std::shared_ptr mf = - std::make_shared(copy_circ); + MappingFrontier_ptr mf = std::make_shared(copy_circ); + LexiLabellingMethod lrm; std::vector vrm = { - std::make_shared(100)}; + std::make_shared(lrm), + std::make_shared()}; bool res = mm.route_circuit(circ, vrm); @@ -657,8 +870,10 @@ SCENARIO("Test MappingManager::route_circuit with lc_route_subcircuit") { PassPtr dec = gen_decompose_routing_gates_to_cxs_pass(sg, false); MappingManager mm(shared_arc); + LexiLabellingMethod lrm; std::vector vrm = { - std::make_shared(100)}; + std::make_shared(lrm), + std::make_shared()}; bool res = mm.route_circuit(circ, vrm); PredicatePtr routed_correctly = std::make_shared(sg); @@ -683,7 +898,8 @@ SCENARIO( RingArch arc(4); MappingManager mm(std::make_shared(arc)); REQUIRE(mm.route_circuit( - test_circuit, {std::make_shared()})); + test_circuit, {std::make_shared(), + std::make_shared()})); REQUIRE(test_circuit.n_gates() == 4); } GIVEN("A solved problem supplied with map and custom architecture") { @@ -703,7 +919,8 @@ SCENARIO( MappingManager mm(std::make_shared(test_arc)); REQUIRE(!mm.route_circuit( - test_circuit, {std::make_shared()})); + test_circuit, {std::make_shared(), + std::make_shared()})); qubit_vector_t all_qs_post_solve = test_circuit.all_qubits(); REQUIRE(all_qs_post_place == all_qs_post_solve); @@ -717,8 +934,9 @@ SCENARIO("Empty Circuit test") { circ.add_blank_wires(4); Architecture arc({{0, 1}, {1, 2}, {2, 3}}); MappingManager mm(std::make_shared(arc)); - REQUIRE( - !mm.route_circuit(circ, {std::make_shared()})); + REQUIRE(!mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); REQUIRE(circ.n_gates() == 0); } } @@ -734,8 +952,9 @@ SCENARIO("Routing on circuit with no multi-qubit gates") { unsigned orig_vertices = circ.n_vertices(); Architecture arc({{0, 1}, {1, 2}, {2, 3}}); MappingManager mm(std::make_shared(arc)); - REQUIRE( - !mm.route_circuit(circ, {std::make_shared()})); + REQUIRE(!mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); REQUIRE(orig_vertices - 8 == circ.n_gates()); } } @@ -750,8 +969,9 @@ SCENARIO("Test routing on a directed architecture with bidirectional edges") { // routing ignored bi directional edge and solves correctly MappingManager mm(std::make_shared(arc)); - REQUIRE( - mm.route_circuit(circ, {std::make_shared()})); + REQUIRE(mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); REQUIRE(circ.n_gates() == 2); CHECK(respects_connectivity_constraints(circ, arc, false)); } @@ -775,8 +995,9 @@ SCENARIO( Architecture arc(std::vector>{{0, 1}}); MappingManager mm(std::make_shared(arc)); - REQUIRE( - mm.route_circuit(circ, {std::make_shared()})); + REQUIRE(mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); REQUIRE(circ.n_gates() == 8); } } @@ -803,8 +1024,9 @@ SCENARIO("Dense CX circuits route succesfully") { {11, 12}, {12, 16}, {12, 17}, {12, 13}, {13, 18}, {13, 19}, {13, 14}, {14, 18}, {14, 19}, {15, 16}, {16, 17}, {17, 18}, {18, 19}}); MappingManager mm(std::make_shared(arc)); - REQUIRE( - mm.route_circuit(circ, {std::make_shared()})); + REQUIRE(mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); (Transforms::decompose_SWAP_to_CX() >> Transforms::decompose_BRIDGE_to_CX()) .apply(circ); @@ -829,8 +1051,9 @@ SCENARIO( } RingArch arc(29); MappingManager mm(std::make_shared(arc)); - REQUIRE( - mm.route_circuit(circ, {std::make_shared()})); + REQUIRE(mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); Transforms::decompose_SWAP_to_CX().apply(circ); REQUIRE(respects_connectivity_constraints(circ, arc, false, true)); } @@ -867,8 +1090,9 @@ SCENARIO( {12, 13}, {6, 7}}); MappingManager mm(std::make_shared(arc)); - REQUIRE( - mm.route_circuit(circ, {std::make_shared()})); + REQUIRE(mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); REQUIRE(respects_connectivity_constraints(circ, arc, false, true)); } } @@ -878,8 +1102,9 @@ SCENARIO("Empty circuits, with and without blank wires") { Circuit circ(6); RingArch arc(6); MappingManager mm(std::make_shared(arc)); - REQUIRE( - !mm.route_circuit(circ, {std::make_shared()})); + REQUIRE(!mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); REQUIRE(circ.depth() == 0); REQUIRE(circ.n_gates() == 0); REQUIRE(circ.n_qubits() == 6); @@ -889,8 +1114,9 @@ SCENARIO("Empty circuits, with and without blank wires") { Circuit circ(0); RingArch arc(6); MappingManager mm(std::make_shared(arc)); - REQUIRE( - !mm.route_circuit(circ, {std::make_shared()})); + REQUIRE(!mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); REQUIRE(circ.depth() == 0); REQUIRE(circ.n_gates() == 0); REQUIRE(circ.n_qubits() == 0); diff --git a/tket/tests/test_MappingFrontier.cpp b/tket/tests/test_MappingFrontier.cpp index 414e807c81..8d18afba22 100644 --- a/tket/tests/test_MappingFrontier.cpp +++ b/tket/tests/test_MappingFrontier.cpp @@ -100,6 +100,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_MappingManager.cpp b/tket/tests/test_MappingManager.cpp index 17edafbead..10e74a0f43 100644 --- a/tket/tests/test_MappingManager.cpp +++ b/tket/tests/test_MappingManager.cpp @@ -33,7 +33,7 @@ class TokenSwappingTester : public RoutingMethod { * */ std::pair routing_method( - std::shared_ptr& /*mapping_frontier*/, + MappingFrontier_ptr& /*mapping_frontier*/, const ArchitecturePtr& /*architecture*/) const { Node node0("test_node", 0), node1("test_node", 1), node2("test_node", 2); return {true, {{node0, node1}, {node1, node2}, {node2, node0}}}; diff --git a/tket/tests/test_MappingVerification.cpp b/tket/tests/test_MappingVerification.cpp index 915ef5c7df..1bb3439d18 100644 --- a/tket/tests/test_MappingVerification.cpp +++ b/tket/tests/test_MappingVerification.cpp @@ -34,6 +34,7 @@ SCENARIO( LinePlacement lp_obj(test_arc); lp_obj.place(circ); MappingManager mm(std::make_shared(test_arc)); + REQUIRE( mm.route_circuit(circ, {std::make_shared()})); CHECK(respects_connectivity_constraints(circ, test_arc, false)); diff --git a/tket/tests/test_MultiGateReorder.cpp b/tket/tests/test_MultiGateReorder.cpp index b8a9185aaa..276ee7f970 100644 --- a/tket/tests/test_MultiGateReorder.cpp +++ b/tket/tests/test_MultiGateReorder.cpp @@ -46,8 +46,7 @@ SCENARIO("Reorder circuits") { {qubits[3], nodes[3]}}; circ.rename_units(rename_map); Circuit circ_copy(circ); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); mf->advance_frontier_boundary(shared_arc); MultiGateReorder mr(shared_arc, mf); mr.solve(20, 20); @@ -57,8 +56,8 @@ SCENARIO("Reorder circuits") { for (auto arg : commands[i].get_args()) { nodes.push_back(Node(arg)); } - REQUIRE(shared_arc->valid_operation( - commands[i].get_op_ptr()->get_type(), nodes)); + REQUIRE(mf->valid_boundary_operation( + shared_arc, commands[i].get_op_ptr(), nodes)); } const auto u = tket_sim::get_unitary(circ); const auto u1 = tket_sim::get_unitary(circ_copy); @@ -88,8 +87,7 @@ SCENARIO("Reorder circuits") { {qubits[3], nodes[3]}}; circ.rename_units(rename_map); Circuit circ_copy(circ); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); mf->advance_frontier_boundary(shared_arc); MultiGateReorder mr(shared_arc, mf); mr.solve(20, 20); @@ -99,8 +97,8 @@ SCENARIO("Reorder circuits") { for (auto arg : commands[i].get_args()) { nodes.push_back(Node(arg)); } - REQUIRE(shared_arc->valid_operation( - commands[i].get_op_ptr()->get_type(), nodes)); + REQUIRE(mf->valid_boundary_operation( + shared_arc, commands[i].get_op_ptr(), nodes)); } const auto u = tket_sim::get_unitary(circ); const auto u1 = tket_sim::get_unitary(circ_copy); @@ -135,8 +133,7 @@ SCENARIO("Reorder circuits") { circ.rename_units(rename_map); Circuit circ_copy(circ); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); mf->advance_frontier_boundary(shared_arc); MultiGateReorder mr(shared_arc, mf); mr.solve(20, 20); @@ -146,8 +143,8 @@ SCENARIO("Reorder circuits") { for (auto arg : commands[i].get_args()) { nodes.push_back(Node(arg)); } - REQUIRE(shared_arc->valid_operation( - commands[i].get_op_ptr()->get_type(), nodes)); + REQUIRE(mf->valid_boundary_operation( + shared_arc, commands[i].get_op_ptr(), nodes)); } const auto u = tket_sim::get_unitary(circ); const auto u1 = tket_sim::get_unitary(circ_copy); @@ -183,8 +180,7 @@ SCENARIO("Reorder circuits") { circ.rename_units(rename_map); Circuit circ_copy(circ); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); mf->advance_frontier_boundary(shared_arc); MultiGateReorder mr(shared_arc, mf); mr.solve(20, 20); @@ -194,8 +190,8 @@ SCENARIO("Reorder circuits") { for (auto arg : commands[i].get_args()) { nodes.push_back(Node(arg)); } - REQUIRE(shared_arc->valid_operation( - commands[i].get_op_ptr()->get_type(), nodes)); + REQUIRE(mf->valid_boundary_operation( + shared_arc, commands[i].get_op_ptr(), nodes)); } const auto u = tket_sim::get_unitary(circ); const auto u1 = tket_sim::get_unitary(circ_copy); @@ -229,18 +225,17 @@ SCENARIO("Reorder circuits with limited search space") { {qubits[3], nodes[3]}}; circ.rename_units(rename_map); Circuit circ_copy(circ); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); mf->advance_frontier_boundary(shared_arc); MultiGateReorder mr(shared_arc, mf); mr.solve(3, 3); // 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()->get_type(), + REQUIRE(mf->valid_boundary_operation( + shared_arc, commands[0].get_op_ptr(), {Node(commands[0].get_args()[0]), Node(commands[0].get_args()[1])})); - REQUIRE(!shared_arc->valid_operation( - commands[1].get_op_ptr()->get_type(), + REQUIRE(!mf->valid_boundary_operation( + shared_arc, 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); @@ -278,8 +273,7 @@ SCENARIO("Test MultiGateReorderRoutingMethod") { {qubits[3], nodes[3]}}; circ.rename_units(rename_map); Circuit circ_copy(circ); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); mf->advance_frontier_boundary(shared_arc); MultiGateReorderRoutingMethod mrrm; @@ -293,8 +287,8 @@ SCENARIO("Test MultiGateReorderRoutingMethod") { for (auto arg : commands[i].get_args()) { nodes.push_back(Node(arg)); } - REQUIRE(shared_arc->valid_operation( - commands[i].get_op_ptr()->get_type(), nodes)); + REQUIRE(mf->valid_boundary_operation( + shared_arc, commands[i].get_op_ptr(), nodes)); } const auto u = tket_sim::get_unitary(circ); const auto u1 = tket_sim::get_unitary(circ_copy); @@ -304,8 +298,7 @@ SCENARIO("Test MultiGateReorderRoutingMethod") { // Test with limits Circuit circ2(circ_copy); - std::shared_ptr mf2 = - std::make_shared(circ2); + MappingFrontier_ptr mf2 = std::make_shared(circ2); mf2->advance_frontier_boundary(shared_arc); MultiGateReorderRoutingMethod mrrm2(4, 4); @@ -319,15 +312,15 @@ SCENARIO("Test MultiGateReorderRoutingMethod") { for (auto arg : commands2[i].get_args()) { nodes.push_back(Node(arg)); } - REQUIRE(shared_arc->valid_operation( - commands2[i].get_op_ptr()->get_type(), nodes)); + REQUIRE(mf2->valid_boundary_operation( + shared_arc, 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( - commands2[4].get_op_ptr()->get_type(), nodes)); + REQUIRE(!mf2->valid_boundary_operation( + shared_arc, 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)); @@ -360,8 +353,7 @@ SCENARIO("Test MappingManager with MultiGateReorderRoutingMethod") { {qubits[2], nodes[2]}, {qubits[3], nodes[3]}}; circ.rename_units(rename_map); - std::shared_ptr mf = - std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); MappingManager mm(shared_arc); // MultiGateReorderRoutingMethod should first commute the last two gates // then only one swap is needed. @@ -382,7 +374,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_RoutingMethod.cpp b/tket/tests/test_RoutingMethod.cpp index 56d9f82eca..5c47a988c2 100644 --- a/tket/tests/test_RoutingMethod.cpp +++ b/tket/tests/test_RoutingMethod.cpp @@ -14,24 +14,13 @@ SCENARIO("Test RoutingMethod default methods.") { {{Node("t", 1), Node("t", 0)}, {Node("t", 2), Node("t", 1)}}); ArchitecturePtr shared_arc = std::make_shared(arc); Circuit circ(3); - std::shared_ptr mf = std::make_shared(circ); + MappingFrontier_ptr mf = std::make_shared(circ); unit_map_t empty; std::pair rm_return = rm.routing_method(mf, shared_arc); REQUIRE(!rm_return.first); REQUIRE(rm_return.second == empty); } -// These two method are not completely reflective of what is necessary for -// routing Their design is to minimally test the required features of the -// methods, not to actually succesfully route a circuit -bool test_check_method(const Circuit& c, const ArchitecturePtr& a) { - if (c.n_qubits() > 2 && a->n_nodes() > 2) { - return true; - } else { - return false; - } -} - std::tuple test_routing_method_mf_swap_perm(const Circuit& c, const ArchitecturePtr& a) { if (c.n_qubits() > 2 && a->n_nodes() > 2) { @@ -114,9 +103,8 @@ SCENARIO("Test RoutingMethodCircuit checking criteria") { c.add_op(OpType::CX, {0, 1}); circ3.add_op(OpType::CX, {0, 2}); circ3.add_op(OpType::CX, {2, 1}); - std::shared_ptr mf2 = std::make_shared(c); - std::shared_ptr mf3 = - std::make_shared(circ3); + MappingFrontier_ptr mf2 = std::make_shared(c); + MappingFrontier_ptr mf3 = std::make_shared(circ3); Architecture arc( {{Node("t", 1), Node("t", 0)}, {Node("t", 2), Node("t", 1)}}); @@ -150,7 +138,7 @@ SCENARIO("Test RoutingMethodCircuit::routing_method") { c.add_op(OpType::CX, {0, 1}); c.add_op(OpType::CX, {0, 1}); - std::shared_ptr mf = std::make_shared(c); + MappingFrontier_ptr mf = std::make_shared(c); Architecture arc( {{Node("t", 1), Node("t", 0)}, {Node("t", 2), Node("t", 1)}}); ArchitecturePtr shared_arc = std::make_shared(arc); @@ -168,7 +156,7 @@ SCENARIO("Test RoutingMethodCircuit::routing_method") { c.add_op(OpType::CX, {0, 1}); c.add_op(OpType::CX, {0, 1}); - std::shared_ptr mf = std::make_shared(c); + MappingFrontier_ptr mf = std::make_shared(c); Architecture arc( {{Node("t", 1), Node("t", 0)}, {Node("t", 2), Node("t", 1)}}); ArchitecturePtr shared_arc = std::make_shared(arc); @@ -186,7 +174,7 @@ SCENARIO("Test RoutingMethodCircuit::routing_method") { c.add_op(OpType::CX, {0, 1}); c.add_op(OpType::CX, {0, 1}); - std::shared_ptr mf = std::make_shared(c); + MappingFrontier_ptr mf = std::make_shared(c); Architecture arc( {{Node("t", 1), Node("t", 0)}, {Node("t", 2), Node("t", 1)}}); ArchitecturePtr shared_arc = std::make_shared(arc); diff --git a/tket/tests/test_RoutingPasses.cpp b/tket/tests/test_RoutingPasses.cpp index 1c2981b331..b3973a6b22 100644 --- a/tket/tests/test_RoutingPasses.cpp +++ b/tket/tests/test_RoutingPasses.cpp @@ -18,6 +18,7 @@ #include "Characterisation/DeviceCharacterisation.hpp" #include "Circuit/Circuit.hpp" +#include "Mapping/LexiLabelling.hpp" #include "Mapping/LexiRoute.hpp" #include "Mapping/MappingManager.hpp" #include "Mapping/Verification.hpp" @@ -192,8 +193,9 @@ SCENARIO("Test decompose_SWAP_to_CX pass", "[routing]") { GIVEN("A routed network of SWAP gates.") { SquareGrid grid(2, 5); MappingManager mm(std::make_shared(grid)); - REQUIRE( - mm.route_circuit(circ, {std::make_shared()})); + REQUIRE(mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); Transforms::decompose_SWAP_to_CX().apply(circ); REQUIRE(respects_connectivity_constraints(circ, grid, false, true)); GIVEN("Directed CX gates") { @@ -278,8 +280,9 @@ SCENARIO("Test redirect_CX_gates pass", "[routing]") { } } MappingManager mm(std::make_shared(grid)); - REQUIRE( - mm.route_circuit(circ, {std::make_shared()})); + REQUIRE(mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); Transforms::decompose_BRIDGE_to_CX().apply(circ); Transforms::decompose_SWAP_to_CX(arc).apply(circ); Transforms::decompose_CX_directed(grid).apply(circ); @@ -330,6 +333,111 @@ SCENARIO("Default mapping pass delays measurements") { REQUIRE(!mid_meas_pred->verify(cu.get_circ_ref())); REQUIRE(mid_meas_pred->verify(cu2.get_circ_ref())); } + +SCENARIO( + "Methods related to correct routing and decomposition of circuits with " + "classical wires.") { + GIVEN("A circuit with classical wires on CX gates.") { + Architecture test_arc({{0, 1}, {1, 2}}); + Circuit circ(3, 2); + circ.add_op(OpType::CX, {0, 1}); + circ.add_op(OpType::H, {0}); + circ.add_conditional_gate(OpType::CX, {}, {0, 1}, {0, 1}, 0); + circ.add_conditional_gate(OpType::CX, {}, {2, 1}, {0, 1}, 1); + circ.add_conditional_gate(OpType::CX, {}, {0, 1}, {0, 1}, 2); + circ.add_conditional_gate(OpType::CX, {}, {2, 1}, {1, 0}, 3); + circ.add_conditional_gate(OpType::CX, {}, {0, 2}, {0, 1}, 0); + MappingManager mm(std::make_shared(test_arc)); + REQUIRE(mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); + + Transforms::decompose_SWAP_to_CX().apply(circ); + REQUIRE(respects_connectivity_constraints(circ, test_arc, false, false)); + Transforms::decompose_BRIDGE_to_CX().apply(circ); + REQUIRE(respects_connectivity_constraints(circ, test_arc, false, false)); + } + GIVEN( + "A circuit that requires modification to satisfy architecture " + "constraints.") { + Architecture arc({{0, 1}, {1, 2}, {2, 3}, {3, 4}}); + Circuit circ(5, 1); + circ.add_conditional_gate(OpType::CX, {}, {0, 1}, {0}, 1); + add_2qb_gates(circ, OpType::CX, {{0, 1}, {1, 2}, {1, 3}, {1, 4}, {0, 1}}); + + MappingManager mm(std::make_shared(arc)); + REQUIRE(mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); + + Transforms::decompose_SWAP_to_CX().apply(circ); + REQUIRE(respects_connectivity_constraints(circ, arc, false, true)); + Transforms::decompose_BRIDGE_to_CX().apply(circ); + REQUIRE(respects_connectivity_constraints(circ, arc, false, false)); + Command classical_com = circ.get_commands()[0]; + REQUIRE(classical_com.get_args()[0] == circ.all_bits()[0]); + } + GIVEN("A single Bridge gate with multiple classical wires, decomposed.") { + Architecture arc({{0, 1}, {1, 2}}); + Circuit circ(3, 3); + circ.add_conditional_gate( + OpType::BRIDGE, {}, {0, 1, 2}, {0, 1, 2}, 1); + reassign_boundary(circ); + REQUIRE(respects_connectivity_constraints(circ, arc, false, true)); + Transforms::decompose_BRIDGE_to_CX().apply(circ); + REQUIRE(respects_connectivity_constraints(circ, arc, false, true)); + for (Command com : circ.get_commands()) { + REQUIRE(com.get_args()[0] == circ.all_bits()[0]); + REQUIRE(com.get_args()[1] == circ.all_bits()[1]); + REQUIRE(com.get_args()[2] == circ.all_bits()[2]); + } + } + GIVEN("A directed architecture, a single CX gate that requires flipping.") { + Architecture arc(std::vector>{{0, 1}}); + Circuit circ(2, 2); + circ.add_conditional_gate(OpType::CX, {}, {0, 1}, {1, 0}, 0); + circ.add_conditional_gate(OpType::CX, {}, {1, 0}, {0, 1}, 1); + reassign_boundary(circ); + REQUIRE(respects_connectivity_constraints(circ, arc, false, false)); + REQUIRE(!respects_connectivity_constraints(circ, arc, true, false)); + Transforms::decompose_CX_directed(arc).apply(circ); + REQUIRE(respects_connectivity_constraints(circ, arc, true, false)); + std::vector all_coms = circ.get_commands(); + REQUIRE(all_coms[0].get_args()[0] == circ.all_bits()[1]); + REQUIRE(all_coms[0].get_args()[1] == circ.all_bits()[0]); + REQUIRE(all_coms[1].get_args()[0] == circ.all_bits()[0]); + REQUIRE(all_coms[1].get_args()[1] == circ.all_bits()[1]); + } + GIVEN( + "A large circuit, with a mixture of conditional CX and CZ with " + "multiple classical wires, non conditional CX and CZ, and single " + "qubit gates.") { + SquareGrid arc(5, 10); + Circuit circ(50, 10); + for (unsigned i = 0; i < 48; i++) { + circ.add_op(OpType::CX, {i, i + 1}); + circ.add_conditional_gate( + OpType::CX, {}, {i + 2, i}, {0, 2, 3, 5}, 1); + circ.add_conditional_gate(OpType::H, {}, {i}, {0, 7}, 1); + circ.add_conditional_gate( + OpType::CX, {}, {i + 2, i + 1}, {1, 2, 3, 5, 9}, 0); + circ.add_conditional_gate(OpType::S, {}, {i + 1}, {1, 2, 7}, 1); + circ.add_conditional_gate( + OpType::CZ, {}, {i, i + 1}, {4, 6, 8, 7, 9}, 0); + circ.add_conditional_gate(OpType::X, {}, {i + 2}, {0, 3}, 0); + } + MappingManager mm(std::make_shared(arc)); + REQUIRE(mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); + + Transforms::decompose_SWAP_to_CX().apply(circ); + REQUIRE(respects_connectivity_constraints(circ, arc, false, true)); + Transforms::decompose_BRIDGE_to_CX().apply(circ); + REQUIRE(respects_connectivity_constraints(circ, arc, false, true)); + } +} + SCENARIO( "Does copying decompose_SWAP_to_CX pass and applying it to a routed " "Circuit work correctly?") { @@ -348,9 +456,9 @@ SCENARIO( {0, 3}}); Architecture arc({{1, 0}, {0, 2}, {1, 2}, {2, 3}, {2, 4}, {4, 3}}); MappingManager mm(std::make_shared(arc)); - REQUIRE( - mm.route_circuit(circ, {std::make_shared()})); - + REQUIRE(mm.route_circuit( + circ, {std::make_shared(), + std::make_shared()})); Transform T_1 = Transforms::decompose_SWAP_to_CX(); T_1.apply(circ); REQUIRE(circ.count_gates(OpType::SWAP) == 0); diff --git a/tket/tests/test_json.cpp b/tket/tests/test_json.cpp index 68d087e347..beab6979e5 100644 --- a/tket/tests/test_json.cpp +++ b/tket/tests/test_json.cpp @@ -24,6 +24,7 @@ #include "CircuitsForTesting.hpp" #include "Converters/PhasePoly.hpp" #include "Gate/SymTable.hpp" +#include "Mapping/LexiLabelling.hpp" #include "Mapping/LexiRoute.hpp" #include "Mapping/RoutingMethod.hpp" #include "OpType/OpType.hpp" @@ -35,6 +36,7 @@ #include "Transformations/Transform.hpp" #include "Utils/Json.hpp" #include "testutil.hpp" + namespace tket { namespace test_json { @@ -431,14 +433,15 @@ SCENARIO("Test RoutingMethod serializations") { c.add_op(OpType::CX, {0, 1}); MappingFrontier mf(c); - std::shared_ptr mf_sp = - std::make_shared(mf); + MappingFrontier_ptr mf_sp = std::make_shared(mf); CHECK(!loaded_rm_j.routing_method(mf_sp, std::make_shared(2, 2)) .first); std::vector rmp = { std::make_shared(rm), + std::make_shared(), std::make_shared(5)}; + nlohmann::json rmp_j = rmp; std::vector loaded_rmp_j = rmp_j.get>(); @@ -637,10 +640,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 f3f056fd34..caace6107c 100644 --- a/tket/tests/tkettestsfiles.cmake +++ b/tket/tests/tkettestsfiles.cmake @@ -79,6 +79,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_RoutingPasses.cpp ${TKET_TESTS_DIR}/test_DeviceCharacterisation.cpp ${TKET_TESTS_DIR}/test_Clifford.cpp