Skip to content

Commit

Permalink
Feature/decompose boxes in routing (#197)
Browse files Browse the repository at this point in the history
* Reject boxes in Architecture::valid_operation

* Add `next_q_cut` method to a quantum cut
Only consider quantum edges

* Use `next_q_cut` in `advance_frontier_boundary`

* Add BoxDecompositionRoutingMethod

* Add tests

* Reformat

* Reject boxes in LexiRouteMethod::check_method

* Update tests

* Add JSON serialisation

* Handle unused arguments

* Refactor Circuit::decompose_boxes

* fix naming

Co-authored-by: sjdilkes <[email protected]>
  • Loading branch information
yao-cqc and sjdilkes authored Feb 14, 2022
1 parent 9a9007d commit 86fb61e
Show file tree
Hide file tree
Showing 21 changed files with 452 additions and 47 deletions.
7 changes: 6 additions & 1 deletion tket/src/Architecture/Architecture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@ namespace tket {
// basic implementation that works off same prior assumptions
// TODO: Update this for more mature systems of multi-qubit gates
bool Architecture::valid_operation(
/*const OpType& optype, */ const std::vector<Node>& uids) const {
const Op_ptr& op, const std::vector<Node>& uids) const {
if (op->get_desc().is_box() ||
(op->get_type() == OpType::Conditional &&
static_cast<const Conditional&>(*op).get_op()->get_desc().is_box()))
return false;

if (uids.size() ==
1) { // TODO: for simple case here this should probably not pass if
// node_exists[uids[0]] == FALSE, but should be fine for now?
Expand Down
3 changes: 3 additions & 0 deletions tket/src/Architecture/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ add_library(tket-${COMP}
ArchitectureGraphClasses.cpp)

list(APPEND DEPS_${COMP}
Circuit
OpType
Ops
Graphs
Utils)

Expand Down
5 changes: 3 additions & 2 deletions tket/src/Architecture/include/Architecture/Architecture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
#include <utility>
#include <vector>

#include "Circuit/Conditional.hpp"
#include "Graphs/CompleteGraph.hpp"
#include "Graphs/DirectedGraph.hpp"
#include "Ops/OpPtr.hpp"
#include "Utils/BiMapHeaders.hpp"
#include "Utils/EigenConfig.hpp"
#include "Utils/Json.hpp"
Expand Down Expand Up @@ -102,8 +104,7 @@ class Architecture : public ArchitectureBase<graphs::DirectedGraph<Node>> {
*/
node_set_t get_articulation_points(const Architecture &subarc) const;

bool valid_operation(
/*const OpType& optype, */ const std::vector<Node> &uids) const;
bool valid_operation(const Op_ptr &op, const std::vector<Node> &uids) const;

/**
* Sub-architecture generated by a subset of nodes.
Expand Down
12 changes: 12 additions & 0 deletions tket/src/Circuit/include/Circuit/Circuit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,11 @@ class Circuit {
std::shared_ptr<const b_frontier_t> b_frontier,
const std::function<bool(Op_ptr)> &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<const unit_frontier_t> u_frontier) const;

/**
* Depth of circuit.
*
Expand Down Expand Up @@ -1375,6 +1380,13 @@ class Circuit {
*/
Circuit conditional_circuit(const bit_vector_t &bits, unsigned value) const;

/**
* Replaces one \ref vertex by applying \ref Box::to_circuit
*
* @return whether the vertex holds a box or a conditional box
*/
bool substitute_box_vertex(Vertex &vert, VertexDeletion vertex_deletion);

/**
* Replaces each \ref Box operation by applying \ref Box::to_circuit
*
Expand Down
38 changes: 38 additions & 0 deletions tket/src/Circuit/macro_circ_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,44 @@ CutFrontier Circuit::next_cut(
get_next_b_frontier(*this, b_frontier, u_frontier, next_slice_lookup)};
}

CutFrontier Circuit::next_q_cut(
std::shared_ptr<const unit_frontier_t> u_frontier) const {
auto next_slice = std::make_shared<Slice>();
VertexSet next_slice_lookup;
VertexSet bad_vertices;
EdgeSet edge_lookup;
for (const std::pair<UnitID, Edge>& pair : u_frontier->get<TagKey>()) {
edge_lookup.insert(pair.second);
}

// find the next slice first
for (const std::pair<UnitID, Edge>& pair : u_frontier->get<TagKey>()) {
Vertex try_v = target(pair.second);
if (detect_final_Op(try_v)) continue;
if (next_slice_lookup.find(try_v) != next_slice_lookup.end())
continue; // already going to be in next slice
bool good_vertex = bad_vertices.find(try_v) == bad_vertices.end();
if (!good_vertex) continue;
EdgeVec ins = get_in_edges(try_v);
for (const Edge& in : ins) {
if (edge_lookup.find(in) == edge_lookup.end() &&
get_edgetype(in) == EdgeType::Quantum) {
good_vertex = false;
bad_vertices.insert(try_v);
break;
}
}
if (good_vertex) {
next_slice_lookup.insert(try_v);
next_slice->push_back(try_v);
}
}

return {
next_slice, get_next_u_frontier(*this, u_frontier, next_slice_lookup),
std::make_shared<b_frontier_t>()};
}

SliceVec Circuit::get_reverse_slices() const {
vertex_map_t mapping;
vertex_map_t rev_mapping;
Expand Down
39 changes: 23 additions & 16 deletions tket/src/Circuit/macro_manipulation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<const Conditional&>(*op);
op = cond.get_op();
}
if (!op->get_desc().is_box()) return false;
const Box& b = static_cast<const Box&>(*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<const Conditional&>(*op);
op = cond.get_op();
}
if (!op->get_desc().is_box()) continue;
const Box& b = static_cast<const Box&>(*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;
Expand Down
71 changes: 71 additions & 0 deletions tket/src/Mapping/BoxDecomposition.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "Mapping/BoxDecomposition.hpp"

#include "Mapping/MappingFrontier.hpp"

namespace tket {

BoxDecomposition::BoxDecomposition(
const ArchitecturePtr &_architecture,
std::shared_ptr<MappingFrontier> &_mapping_frontier)
: architecture_(_architecture), mapping_frontier_(_mapping_frontier) {}

void BoxDecomposition::solve() {
// Box type vertices are later removed from DAG
VertexList bin;

std::shared_ptr<unit_frontier_t> frontier_edges =
frontier_convert_vertport_to_edge(
this->mapping_frontier_->circuit_,
this->mapping_frontier_->quantum_boundary);
CutFrontier next_cut =
this->mapping_frontier_->circuit_.next_q_cut(frontier_edges);
for (Vertex &vert : *next_cut.slice) {
if (this->mapping_frontier_->circuit_.substitute_box_vertex(
vert, Circuit::VertexDeletion::No))
bin.push_back(vert);
}

// Delete vertices
this->mapping_frontier_->circuit_.remove_vertices(
bin, Circuit::GraphRewiring::No, Circuit::VertexDeletion::Yes);
}

BoxDecompositionRoutingMethod::BoxDecompositionRoutingMethod(){};

bool BoxDecompositionRoutingMethod::check_method(
const std::shared_ptr<MappingFrontier> &mapping_frontier,
const ArchitecturePtr & /*architecture*/) const {
std::shared_ptr<unit_frontier_t> frontier_edges =
frontier_convert_vertport_to_edge(
mapping_frontier->circuit_, mapping_frontier->quantum_boundary);
CutFrontier next_cut = mapping_frontier->circuit_.next_q_cut(frontier_edges);
for (const Vertex &vert : *next_cut.slice) {
Op_ptr op = mapping_frontier->circuit_.get_Op_ptr_from_Vertex(vert);
if (op->get_desc().is_box() ||
(op->get_type() == OpType::Conditional &&
static_cast<const Conditional &>(*op).get_op()->get_desc().is_box()))
return true;
}
return false;
}

unit_map_t BoxDecompositionRoutingMethod::routing_method(
std::shared_ptr<MappingFrontier> &mapping_frontier,
const ArchitecturePtr &architecture) const {
BoxDecomposition bd(architecture, mapping_frontier);
bd.solve();
return {};
}

nlohmann::json BoxDecompositionRoutingMethod::serialize() const {
nlohmann::json j;
j["name"] = "BoxDecompositionRoutingMethod";
return j;
}

BoxDecompositionRoutingMethod BoxDecompositionRoutingMethod::deserialize(
const nlohmann::json & /*j*/) {
return BoxDecompositionRoutingMethod();
}

} // namespace tket
1 change: 1 addition & 0 deletions tket/src/Mapping/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_library(tket-${COMP}
MappingFrontier.cpp
MappingManager.cpp
MultiGateReorder.cpp
BoxDecomposition.cpp
RoutingMethodCircuit.cpp
RoutingMethodJson.cpp
Verification.cpp)
Expand Down
13 changes: 12 additions & 1 deletion tket/src/Mapping/LexiRoute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,8 +510,19 @@ LexiRouteRoutingMethod::LexiRouteRoutingMethod(unsigned _max_depth)
: max_depth_(_max_depth){};

bool LexiRouteRoutingMethod::check_method(
const std::shared_ptr<MappingFrontier>& /*mapping_frontier*/,
const std::shared_ptr<MappingFrontier>& mapping_frontier,
const ArchitecturePtr& /*architecture*/) const {
std::shared_ptr<unit_frontier_t> frontier_edges =
frontier_convert_vertport_to_edge(
mapping_frontier->circuit_, mapping_frontier->quantum_boundary);
CutFrontier next_cut = mapping_frontier->circuit_.next_q_cut(frontier_edges);
for (const Vertex& vert : *next_cut.slice) {
Op_ptr op = mapping_frontier->circuit_.get_Op_ptr_from_Vertex(vert);
if (op->get_desc().is_box() ||
(op->get_type() == OpType::Conditional &&
static_cast<const Conditional&>(*op).get_op()->get_desc().is_box()))
return false;
}
return true;
}

Expand Down
16 changes: 2 additions & 14 deletions tket/src/Mapping/MappingFrontier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -218,19 +218,8 @@ void MappingFrontier::advance_frontier_boundary(
std::shared_ptr<unit_frontier_t> 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<UnitID, Edge>& pair : frontier_edges->get<TagKey>()) {
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<b_frontier_t>());
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.
Expand All @@ -252,8 +241,7 @@ void MappingFrontier::advance_frontier_boundary(
nodes.push_back(Node(uid));
}
if (architecture->valid_operation(
/* this->circuit_.get_OpType_from_Vertex(vert), */
nodes) ||
this->circuit_.get_Op_ptr_from_Vertex(vert), nodes) ||
this->circuit_.get_OpType_from_Vertex(vert) == OpType::Barrier) {
// if no valid operation, boundary not updated and while loop terminates
boundary_updated = true;
Expand Down
3 changes: 2 additions & 1 deletion tket/src/Mapping/MultiGateReorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ bool is_physically_permitted(
for (port_t port = 0; port < frontier->circuit_.n_ports(vert); ++port) {
nodes.push_back(Node(get_unitid_from_vertex_port(frontier, {vert, port})));
}
Op_ptr op = frontier->circuit_.get_Op_ptr_from_Vertex(vert);

return arc_ptr->valid_operation(nodes);
return arc_ptr->valid_operation(op, nodes);
}

// This method will try to commute a vertex to the quantum frontier
Expand Down
3 changes: 3 additions & 0 deletions tket/src/Mapping/RoutingMethodJson.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ void from_json(const nlohmann::json& j, std::vector<RoutingMethodPtr>& rmp_v) {
} else if (name == "MultiGateReorderRoutingMethod") {
rmp_v.push_back(std::make_shared<MultiGateReorderRoutingMethod>(
MultiGateReorderRoutingMethod::deserialize(c)));
} else if (name == "BoxDecompositionRoutingMethod") {
rmp_v.push_back(std::make_shared<BoxDecompositionRoutingMethod>(
BoxDecompositionRoutingMethod::deserialize(c)));
} else {
std::logic_error(
"Deserialization for given RoutingMethod not supported.");
Expand Down
63 changes: 63 additions & 0 deletions tket/src/Mapping/include/Mapping/BoxDecomposition.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#ifndef _TKET_BoxDecomposition_H_
#define _TKET_BoxDecomposition_H_

#include "Mapping/MappingFrontier.hpp"
#include "Mapping/RoutingMethod.hpp"

namespace tket {

class BoxDecomposition {
public:
/**
* Class Constructor
* @param _architecture Architecture object added operations must respect
* @param _mapping_frontier Contains Circuit object to be modified
*/
BoxDecomposition(
const ArchitecturePtr& _architecture,
std::shared_ptr<MappingFrontier>& _mapping_frontier);

/**
* Decompose any boxes in the next slice after the frontier
*/
void solve();

private:
// Architecture all new physical operations must respect
ArchitecturePtr architecture_;
std::shared_ptr<MappingFrontier> mapping_frontier_;
};

class BoxDecompositionRoutingMethod : public RoutingMethod {
public:
/**
* Decompose any boxes on the frontier
*/
BoxDecompositionRoutingMethod();

/**
* @return true if method can route subcircuit, false if not
*/
bool check_method(
const std::shared_ptr<MappingFrontier>& mapping_frontier,
const ArchitecturePtr& /*architecture*/) const override;

/**
* @param mapping_frontier Contains boundary of routed/unrouted circuit for
* modifying
* @param architecture Architecture providing physical constraints
* @return Logical to Physical mapping at boundary due to modification.
*
*/
unit_map_t routing_method(
std::shared_ptr<MappingFrontier>& mapping_frontier,
const ArchitecturePtr& architecture) const override;

nlohmann::json serialize() const override;

static BoxDecompositionRoutingMethod deserialize(const nlohmann::json& /*j*/);
};

} // namespace tket

#endif
4 changes: 2 additions & 2 deletions tket/src/Mapping/include/Mapping/MultiGateReorder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ class MultiGateReorderRoutingMethod : public RoutingMethod {
* @return true if method can route subcircuit, false if not
*/
bool check_method(
const std::shared_ptr<MappingFrontier>& /*mapping_frontier*/,
const ArchitecturePtr& /*architecture*/) const override;
const std::shared_ptr<MappingFrontier>& mapping_frontier,
const ArchitecturePtr& architecture) const override;

/**
* @param mapping_frontier Contains boundary of routed/unrouted circuit for
Expand Down
Loading

0 comments on commit 86fb61e

Please sign in to comment.