Skip to content

Commit

Permalink
Add StandardPass for basic qubit relabelling (#247)
Browse files Browse the repository at this point in the history
* add NaivePlacement Class and naive_placement_mapping_pass

* clang format

* PR Requested Changes
  • Loading branch information
sjdilkes authored Feb 22, 2022
1 parent d0d679b commit a6f694f
Show file tree
Hide file tree
Showing 12 changed files with 226 additions and 18 deletions.
7 changes: 7 additions & 0 deletions pytket/binders/passes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,13 @@ PYBIND11_MODULE(passes, m) {
":py:class:`Architecture` Nodes",
py::arg("placer"));

m.def(
"NaivePlacementPass", &gen_naive_placement_pass,
":param architecture: The Architecture used for relabelling."
"\n:return: a pass to relabel :py:class:`Circuit` Qubits to "
":py:class:`Architecture` Nodes",
py::arg("arc"));

m.def(
"RenameQubitsPass", &gen_rename_qubits_pass, "Rename some or all qubits.",
"\n\n:param qubit_map: map from old to new qubit names",
Expand Down
2 changes: 2 additions & 0 deletions pytket/docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Minor new features:
* New ``pytket.passes.auto_rebase_pass`` and ``pytket.passes.auto_squash_pass``
which attempt to construct rebase and squash passess given a target gate set from known
decompositions.
* New ``pytket.passes.NaivePlacementPass`` which completes a basic relabelling of all Circuit Qubit
not labelled as some Architecture Node to any available Architecture Node

0.19.2 (February 2022)
----------------------
Expand Down
40 changes: 35 additions & 5 deletions pytket/tests/predicates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
RoutingPass,
CXMappingPass,
PlacementPass,
NaivePlacementPass,
RenameQubitsPass,
FullMappingPass,
DefaultMappingPass,
Expand Down Expand Up @@ -202,14 +203,29 @@ def test_routing_and_placement_pass() -> None:
pl = Placement(arc)
routing = RoutingPass(arc)
placement = PlacementPass(pl)
nplacement = NaivePlacementPass(arc)
cu = CompilationUnit(circ.copy())
assert placement.apply(cu)
assert routing.apply(cu)
assert nplacement.apply(cu)
expected_map = {q[0]: n1, q[1]: n0, q[2]: n2, q[3]: n5, q[4]: n3}
assert cu.initial_map == expected_map

cu1 = CompilationUnit(circ.copy())
assert nplacement.apply(cu1)
arcnodes = arc.nodes
expected_nmap = {
q[0]: arcnodes[0],
q[1]: arcnodes[1],
q[2]: arcnodes[2],
q[3]: arcnodes[3],
q[4]: arcnodes[4],
}
assert cu1.initial_map == expected_nmap
# check composition works ok
seq_pass = SequencePass([SynthesiseTket(), placement, routing, SynthesiseUMD()])
seq_pass = SequencePass(
[SynthesiseTket(), placement, routing, nplacement, SynthesiseUMD()]
)
cu2 = CompilationUnit(circ.copy())
assert seq_pass.apply(cu2)
assert cu2.initial_map == expected_map
Expand All @@ -223,7 +239,7 @@ def test_routing_and_placement_pass() -> None:

def test_default_mapping_pass() -> None:
circ = Circuit()
q = circ.add_q_register("q", 5)
q = circ.add_q_register("q", 6)
circ.CX(0, 1)
circ.H(0)
circ.Z(1)
Expand All @@ -233,14 +249,17 @@ def test_default_mapping_pass() -> None:
circ.X(2)
circ.CX(1, 4)
circ.CX(0, 4)
circ.H(5)
n0 = Node("b", 0)
n1 = Node("b", 1)
n2 = Node("b", 2)
n3 = Node("a", 0)
n4 = Node("f", 0)
arc = Architecture([[n0, n1], [n1, n2], [n2, n3], [n3, n4]])
n5 = Node("g", 7)
arc = Architecture([[n0, n1], [n1, n2], [n2, n3], [n3, n4], [n4, n5]])
pl = GraphPlacement(arc)

nplacement = NaivePlacementPass(arc)
routing = RoutingPass(arc)
placement = PlacementPass(pl)
default = DefaultMappingPass(arc)
Expand All @@ -249,6 +268,7 @@ def test_default_mapping_pass() -> None:

assert placement.apply(cu_rp)
assert routing.apply(cu_rp)
assert nplacement.apply(cu_rp)
assert default.apply(cu_def)
assert cu_rp.circuit == cu_def.circuit

Expand Down Expand Up @@ -650,6 +670,10 @@ def sq(a: float, b: float, c: float) -> Circuit:
assert p_pass.to_dict()["StandardPass"]["name"] == "PlacementPass"
assert p_pass.to_dict()["StandardPass"]["placement"]["type"] == "GraphPlacement"
assert p_pass.to_dict()["StandardPass"]["placement"]["config"]["depth_limit"] == 5
# NaivePlacementPass
np_pass = NaivePlacementPass(arc)
assert np_pass.to_dict()["StandardPass"]["name"] == "NaivePlacementPass"
assert check_arc_dict(arc, np_pass.to_dict()["StandardPass"]["architecture"])
# RenameQubitsPass
qm = {Qubit("a", 0): Qubit("b", 1), Qubit("a", 1): Qubit("b", 0)}
rn_pass = RenameQubitsPass(qm)
Expand All @@ -662,29 +686,35 @@ def sq(a: float, b: float, c: float) -> Circuit:
assert fm_pass.to_dict()["pass_class"] == "SequencePass"
p_pass = fm_pass.get_sequence()[0]
r_pass = fm_pass.get_sequence()[1]
assert p_pass.to_dict()["StandardPass"]["name"] == "PlacementPass"
np_pass = fm_pass.get_sequence()[2]
assert np_pass.to_dict()["StandardPass"]["name"] == "NaivePlacementPass"
assert r_pass.to_dict()["StandardPass"]["name"] == "RoutingPass"
assert p_pass.to_dict()["StandardPass"]["name"] == "PlacementPass"
assert check_arc_dict(arc, r_pass.to_dict()["StandardPass"]["architecture"])
assert p_pass.to_dict()["StandardPass"]["placement"]["type"] == "GraphPlacement"
# DefaultMappingPass
dm_pass = DefaultMappingPass(arc)
assert dm_pass.to_dict()["pass_class"] == "SequencePass"
p_pass = dm_pass.get_sequence()[0].get_sequence()[0]
r_pass = dm_pass.get_sequence()[0].get_sequence()[1]
np_pass = dm_pass.get_sequence()[0].get_sequence()[2]
d_pass = dm_pass.get_sequence()[1]
assert d_pass.to_dict()["StandardPass"]["name"] == "DelayMeasures"
assert p_pass.to_dict()["StandardPass"]["name"] == "PlacementPass"
assert np_pass.to_dict()["StandardPass"]["name"] == "NaivePlacementPass"
assert r_pass.to_dict()["StandardPass"]["name"] == "RoutingPass"
assert check_arc_dict(arc, r_pass.to_dict()["StandardPass"]["architecture"])
assert p_pass.to_dict()["StandardPass"]["placement"]["type"] == "GraphPlacement"
# DefaultMappingPass with delay_measures=False
dm_pass = DefaultMappingPass(arc, False)
assert dm_pass.to_dict()["pass_class"] == "SequencePass"
assert len(dm_pass.get_sequence()) == 2
assert len(dm_pass.get_sequence()) == 3
p_pass = dm_pass.get_sequence()[0]
r_pass = dm_pass.get_sequence()[1]
np_pass = dm_pass.get_sequence()[2]
assert p_pass.to_dict()["StandardPass"]["name"] == "PlacementPass"
assert r_pass.to_dict()["StandardPass"]["name"] == "RoutingPass"
assert np_pass.to_dict()["StandardPass"]["name"] == "NaivePlacementPass"
assert check_arc_dict(arc, r_pass.to_dict()["StandardPass"]["architecture"])
assert p_pass.to_dict()["StandardPass"]["placement"]["type"] == "GraphPlacement"
# AASRouting
Expand Down
14 changes: 14 additions & 0 deletions schemas/compiler_pass_v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,20 @@
]
}
},
{
"if": {
"properties": {
"name": {
"const": "NaivePlacementPass"
}
}
},
"then": {
"required": [
"architecture"
]
}
},
{
"if": {
"properties": {
Expand Down
39 changes: 39 additions & 0 deletions tket/src/Placement/Placement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,45 @@ std::vector<qubit_mapping_t> Placement::get_all_placement_maps(
return {get_placement_map(circ_)};
}

qubit_mapping_t NaivePlacement::get_placement_map(const Circuit &circ_) const {
return get_all_placement_maps(circ_).at(0);
}

std::vector<qubit_mapping_t> NaivePlacement::get_all_placement_maps(
const Circuit &circ_) const {
qubit_mapping_t placement;
qubit_vector_t to_place;
std::vector<Node> placed;

// Find which/if any qubits need placing
for (const Qubit &q : circ_.all_qubits()) {
Node n(q);
if (!this->arc_.node_exists(n)) {
to_place.push_back(n);
} else {
placed.push_back(n);
// if already placed, make sure qubit retains placement
placement.insert({n, n});
}
}
// avoid doing std::set_difference unless qubits need to be placed
unsigned n_placed = to_place.size();
if (n_placed > 0) {
std::vector<Node> difference,
architecture_nodes = this->arc_.get_all_nodes_vec();
std::set_difference(
architecture_nodes.begin(), architecture_nodes.end(), placed.begin(),
placed.end(), std::inserter(difference, difference.begin()));
// should always be enough remaining qubits to assign unplaced qubits to
TKET_ASSERT(difference.size() >= n_placed);
for (unsigned i = 0; i < n_placed; i++) {
// naively assign each qubit to some free node
placement.insert({to_place[i], difference[i]});
}
}
return {placement};
}

qubit_mapping_t LinePlacement::get_placement_map(const Circuit &circ_) const {
return get_all_placement_maps(circ_).at(0);
}
Expand Down
36 changes: 36 additions & 0 deletions tket/src/Placement/include/Placement/Placement.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,42 @@ class Placement {
Architecture arc_;
};

/**
* NaivePlacement class provides methods for relabelling any
* Qubit objects in some Circuit to Node objects in some Architecture
* given the constraint that only Qubit that are not already labelled
* as some Node can be relabelled, and only to Architecture Node
* that are not already in the Circuit.
*/
class NaivePlacement : public Placement {
public:
/**
* @param _arc Architecture object later relabellings are produced for
*/
explicit NaivePlacement(const Architecture& _arc) { arc_ = _arc; }
/**
* Given some circuit, returns a map between Qubit which defines some
* relabelling of some Circuit qubits to Architecture qubits
*
* @param circ_ Circuit map relabelling is defined for
*
* @return Map defining relabelling for circuit Qubit objects
*/
qubit_mapping_t get_placement_map(const Circuit& circ_) const override;

/**
* Given some circuit, returns a single map for relabelling
* in a vector.
*
* @param circ_ Circuit map relabelling is defined for
*
* @return Vector of a single Map defining relabelling for Circuit
* Qubit objects.
*/
std::vector<qubit_mapping_t> get_all_placement_maps(
const Circuit& circ_) const override;
};

class LinePlacement : public Placement {
public:
explicit LinePlacement(const Architecture& _arc) { arc_ = _arc; }
Expand Down
3 changes: 3 additions & 0 deletions tket/src/Predicates/CompilerPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,9 @@ void from_json(const nlohmann::json& j, PassPtr& pp) {

} else if (passname == "PlacementPass") {
pp = gen_placement_pass(content.at("placement").get<PlacementPtr>());
} else if (passname == "NaivePlacementPass") {
pp = gen_naive_placement_pass(
content.at("architecture").get<Architecture>());
} else if (passname == "RenameQubitsPass") {
pp = gen_rename_qubits_pass(
content.at("qubit_map").get<std::map<Qubit, Qubit>>());
Expand Down
26 changes: 25 additions & 1 deletion tket/src/Predicates/PassGenerators.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,10 +182,34 @@ PassPtr gen_placement_pass(const PlacementPtr& placement_ptr) {
return std::make_shared<StandardPass>(precons, t, pc, j);
}

PassPtr gen_naive_placement_pass(const Architecture& arc) {
Transform::Transformation trans = [=](Circuit& circ,
std::shared_ptr<unit_bimaps_t> maps) {
NaivePlacement np(arc);
return np.place(circ, maps);
};
Transform t = Transform(trans);
PredicatePtr n_qubit_pred =
std::make_shared<MaxNQubitsPredicate>(arc.n_nodes());

PredicatePtrMap precons{CompilationUnit::make_type_pair(n_qubit_pred)};
PredicatePtr placement_pred = std::make_shared<PlacementPredicate>(arc);
PredicatePtrMap s_postcons{CompilationUnit::make_type_pair(placement_pred)};
PostConditions pc{s_postcons, {}, Guarantee::Preserve};
// record pass config
nlohmann::json j;
j["name"] = "NaivePlacementPass";
j["architecture"] = arc;
return std::make_shared<StandardPass>(precons, t, pc, j);
}

PassPtr gen_full_mapping_pass(
const Architecture& arc, const PlacementPtr& placement_ptr,
const std::vector<RoutingMethodPtr>& config) {
return gen_placement_pass(placement_ptr) >> gen_routing_pass(arc, config);
std::vector<PassPtr> vpp = {
gen_placement_pass(placement_ptr), gen_routing_pass(arc, config),
gen_naive_placement_pass(arc)};
return std::make_shared<SequencePass>(vpp);
}

PassPtr gen_default_mapping_pass(const Architecture& arc, bool delay_measures) {
Expand Down
2 changes: 2 additions & 0 deletions tket/src/Predicates/include/Predicates/PassGenerators.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ PassPtr gen_clifford_simp_pass(bool allow_swaps = true);
PassPtr gen_rename_qubits_pass(const std::map<Qubit, Qubit>& qm);

PassPtr gen_placement_pass(const PlacementPtr& placement_ptr);

PassPtr gen_naive_placement_pass(const Architecture& arc);
/* This higher order function generates a Routing pass using the
std::vector<RoutingMethodPtr> object */
PassPtr gen_full_mapping_pass(
Expand Down
3 changes: 2 additions & 1 deletion tket/tests/test_CompilerPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,12 +234,13 @@ SCENARIO("Test making (mostly routing) passes using PassGenerators") {
}
}
GIVEN("Synthesise Passes in a row then routing") {
Circuit circ(4);
Circuit circ(5);
circ.add_op<unsigned>(OpType::H, {0});
circ.add_op<unsigned>(OpType::CZ, {0, 1});
circ.add_op<unsigned>(OpType::CH, {0, 2});
circ.add_op<unsigned>(OpType::CnX, {0, 1, 2, 3});
circ.add_op<unsigned>(OpType::CZ, {0, 1});
circ.add_op<unsigned>(OpType::X, {4});
OpTypeSet ots = {OpType::CX, OpType::TK1, OpType::SWAP};
PredicatePtr gsp = std::make_shared<GateSetPredicate>(ots);
SquareGrid grid(2, 3);
Expand Down
Loading

0 comments on commit a6f694f

Please sign in to comment.