Skip to content

Commit

Permalink
[feature] [rv3] Generate random nearby placements (#232)
Browse files Browse the repository at this point in the history
  • Loading branch information
lmondada authored Feb 25, 2022
1 parent 8049079 commit 021c6f0
Show file tree
Hide file tree
Showing 7 changed files with 398 additions and 3 deletions.
4 changes: 3 additions & 1 deletion tket/src/Placement/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ add_library(tket-${COMP}
Qubit_Placement.cpp
subgraph_mapping.cpp
Placement.cpp
PlacementGraphClasses.cpp)
PlacementGraphClasses.cpp
NeighbourPlacements.cpp)

list(APPEND DEPS_${COMP}
Architecture
Expand All @@ -32,6 +33,7 @@ list(APPEND DEPS_${COMP}
Graphs
Ops
OpType
TokenSwapping
Utils)

foreach(DEP ${DEPS_${COMP}})
Expand Down
145 changes: 145 additions & 0 deletions tket/src/Placement/NeighbourPlacements.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// 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 "NeighbourPlacements.hpp"

#include <cstdlib>
#include <string>

#include "TokenSwapping/SwapListOptimiser.hpp"
#include "Utils/TketLog.hpp"

namespace tket {

NeighbourPlacements::NeighbourPlacements(
const Architecture& arc, const qubit_mapping_t& init_map)
: arc_(arc), init_map_(init_map), u_to_node_(), rng_() {
auto nodes = arc_.get_all_nodes_vec();
for (unsigned i = 0; i < nodes.size(); ++i) {
u_to_node_.left.insert({i, nodes[i]});
}
}

NeighbourPlacements::ResultVec NeighbourPlacements::get(
unsigned dist, unsigned n, bool optimise, unsigned seed,
unsigned max_tries) {
rng_.set_seed(seed);

// define a comparison function for placements
std::vector<Qubit> keys;
for (auto [k, v] : init_map_) {
keys.push_back(k);
}
auto map_compare = [&keys](
const qubit_mapping_t& a, const qubit_mapping_t& b) {
for (auto k : keys) {
if (a.at(k) < b.at(k)) {
return true;
} else if (a.at(k) > b.at(k)) {
return false;
}
}
return false;
};
// set of all generated placement maps
std::set<qubit_mapping_t, decltype(map_compare)> placements(map_compare);

ResultVec resvec;
for (unsigned i = 0; i < n; ++i) {
unsigned n_unsuccessful = 0;
while (n_unsuccessful < max_tries) {
Result res = gen_result(dist, optimise, max_tries);
if (!placements.contains(res.map)) {
resvec.push_back(res);
placements.insert(res.map);
break;
}
++n_unsuccessful;
}
if (n_unsuccessful == max_tries) {
tket_log()->warn(
"Could not generate " + std::to_string(n) + " distinct placements");
}
}
return resvec;
}

NeighbourPlacements::Result NeighbourPlacements::gen_result(
unsigned dist, bool optimise, unsigned max_tries) {
SwapList swaps;
tsa_internal::SwapListOptimiser optimiser;

// it might be impossible to find `dist` non-trivial swaps
unsigned n_unsuccessful = 0;

while (swaps.size() < dist && n_unsuccessful < max_tries) {
Swap new_swap = gen_swap();

if (optimise) {
SwapList swaps_candidate = swaps;
swaps_candidate.push_back(new_swap);
optimiser.full_optimise(swaps_candidate);
if (swaps_candidate.size() > swaps.size()) {
swaps = std::move(swaps_candidate);
n_unsuccessful = 0;
} else {
++n_unsuccessful;
}
} else {
swaps.push_back(new_swap);
}
}

if (n_unsuccessful == max_tries) {
tket_log()->warn(
"Unable to generate " + std::to_string(dist) +
" swaps for given architecture");
}

return convert_to_res(swaps.to_vector());
}

Swap NeighbourPlacements::gen_swap() {
auto edges = arc_.get_all_edges_vec();
unsigned m = edges.size();
auto [n1, n2] = edges[rng_.get_size_t(m - 1)];
Swap new_swap{u_to_node_.right.at(n1), u_to_node_.right.at(n2)};
return new_swap;
}

NeighbourPlacements::Result NeighbourPlacements::convert_to_res(
const SwapVec& swaps) {
NodeSwapVec node_swaps;
for (auto [u1, u2] : swaps) {
node_swaps.push_back({u_to_node_.left.at(u1), u_to_node_.left.at(u2)});
}

qubit_bimap_t qubit_to_node;
qubit_to_node.left.insert(init_map_.begin(), init_map_.end());
for (auto [n1, n2] : node_swaps) {
const Qubit q1 = qubit_to_node.right.at(n1);
const Qubit q2 = qubit_to_node.right.at(n2);
qubit_to_node.left.erase(q1);
qubit_to_node.left.erase(q2);
qubit_to_node.left.insert({q1, n2});
qubit_to_node.left.insert({q2, n1});
}
qubit_mapping_t map;
for (auto [k, v] : qubit_to_node.left) {
map.insert({k, v});
}
return {map, node_swaps};
}

} // namespace tket
99 changes: 99 additions & 0 deletions tket/src/Placement/include/Placement/NeighbourPlacements.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// 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 <map>

#include "Placement.hpp"
#include "TokenSwapping/SwapFunctions.hpp"
#include "Utils/BiMapHeaders.hpp"
#include "Utils/RNG.hpp"

namespace tket {

/**
* @brief Given a placement map generates `n` nearby placement maps.
*
* Based on an architecture and a placement map, generates random
* placements that can be achieved with `m` swaps.
*
* Optionally uses token swapping optimisations to try to ensure
* that the generated placements cannot be obtained in less than `m`
* swaps, but this cannot be guaranteed.
*/
class NeighbourPlacements {
public:
using SwapVec = std::vector<Swap>;
using NodeSwap = std::pair<Node, Node>;
using NodeSwapVec = std::vector<NodeSwap>;
struct Result {
qubit_mapping_t map;
NodeSwapVec swaps;
};
using ResultVec = std::vector<Result>;

/**
* @brief Construct a new Swap Placement object.
*
* @param arc The architecture defining the allowed swaps.
* @param init_map The initial Qubit => Node map.
*/
NeighbourPlacements(const Architecture& arc, const qubit_mapping_t& init_map);

/**
* @brief Generate `n` distinct placement maps using `dist` swaps for each map
*
* The sequences of swaps are generated randomly. Note that it cannot be
* guaranteed that the generated placement cannot be obtained in less than
* `dist` swaps. When optimise=true (default), we attempt to simplify
* chains of swaps to make it more likely that `dist` swaps are indeed
* necessary for the generated placement maps.
*
* If optimise=true, it is also possible that placements `dist` swaps away
* do not exist. `max_tries` controls the number of attempts to generate
* placements.
*
* If it is impossible (or very hard) to generate `n` distinct placement maps
* of distance `dist` swaps away, then this method will raise a warning
* and return fewer results and/or results with less than `dist` swaps.
*
* @param dist The number of swaps allowed on the architecture.
* @param n The number of placement maps to generate (default n=1).
* @param optimise Simplify the generated swap sequences (default true).
* @param seed Seed for random number generator (default seed=5489).
* @param max_tries Number of tries before aborting placement map generation
* (default max_tries=10).
* @return ResultVec A vector of the generated maps and swaps
*/
ResultVec get(
unsigned dist, unsigned n = 1, bool optimise = true, unsigned seed = 5489,
unsigned max_tries = 10);

private:
// generate a single Result
Result gen_result(
unsigned dist, bool optimise = true, unsigned max_tries = 10);
// generate a single swap
Swap gen_swap();
// apply swap list to init_map and return new placement map
Result convert_to_res(const SwapVec& swaps);

Architecture arc_;
qubit_mapping_t init_map_;
boost::bimap<unsigned, Node> u_to_node_;
RNG rng_;
};

} // namespace tket
1 change: 1 addition & 0 deletions tket/src/Utils/include/Utils/UnitID.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class UnitID {
if (n < 0) return true;
return data_->index_ < other.data_->index_;
}
bool operator>(const UnitID &other) const { return other < *this; }
bool operator==(const UnitID &other) const {
return (this->data_->name_ == other.data_->name_) &&
(this->data_->index_ == other.data_->index_);
Expand Down
Loading

0 comments on commit 021c6f0

Please sign in to comment.