Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add StandardPass for basic qubit relabelling #247

Merged
merged 3 commits into from
Feb 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(
cqc-alec marked this conversation as resolved.
Show resolved Hide resolved
"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});
}
}
cqc-alec marked this conversation as resolved.
Show resolved Hide resolved
// 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 {
cqc-alec marked this conversation as resolved.
Show resolved Hide resolved
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