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

Support any two qubit gates #235

Draft
wants to merge 32 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c845d37
🔥 Remove assert in countGates
JoachimMarin Feb 13, 2023
d526398
✨ Preserve operation type for two qubit gates
JoachimMarin Feb 13, 2023
27b1b20
🚧 Generalize naming for two-qubit gates
JoachimMarin Feb 13, 2023
7002c93
🎨 Rename cnots to twoQubitGates
JoachimMarin Feb 13, 2023
03f4214
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 13, 2023
5f49023
:sparkles: Case distinction for flipping gates
JoachimMarin Feb 20, 2023
4e8648a
🎨 pre-commit fixes
pre-commit-ci[bot] Feb 20, 2023
6d33c87
✨ Function for gate reversal cost
JoachimMarin Mar 4, 2023
3b05b62
:rewind: Assume cnot gates in distance table
JoachimMarin Mar 5, 2023
7b38085
:white_check_mark: Add direction reverse python tests
JoachimMarin Mar 5, 2023
e7127dd
:bulb: Add python test descriptions
JoachimMarin Mar 6, 2023
6dfbb94
🚨 Fix ruff warnings for python tests
JoachimMarin Mar 6, 2023
4016c86
Merge remote-tracking branch 'upstream/main' into support-any-two-qub…
JoachimMarin Mar 6, 2023
ff3929c
🚨 Fix missing return on unexpected enum value
JoachimMarin Mar 6, 2023
91b7969
✅ Remove InstructionSet import
JoachimMarin Mar 6, 2023
219eda2
✅ Use gate objects for python tests
JoachimMarin Mar 7, 2023
2bd795e
🩹 fix Python typing
burgholzer Mar 7, 2023
88a82ee
🩹 silence an external Qiskit warning from the tests
burgholzer Mar 7, 2023
90764c3
Merge remote-tracking branch 'origin/main' into support-any-two-qubit…
burgholzer Mar 7, 2023
f0f19e3
♻️ restructure tests
burgholzer Mar 7, 2023
e71fb87
🐛 Prevent reversals with swaps in exact mapper
JoachimMarin Mar 11, 2023
45deaab
Update include/Mapper.hpp
burgholzer Mar 11, 2023
e43c357
🚨 some `const`
burgholzer Mar 11, 2023
3c982bc
🚚 rename `insertFlippedGate` method
burgholzer Mar 11, 2023
80609be
♻️ refactor condition to simplify code
burgholzer Mar 11, 2023
44d81c8
⚡ add missing conditional skip statement
burgholzer Mar 11, 2023
31e73b3
🚸 add additional sanity check
burgholzer Mar 11, 2023
a33e1d1
🚸 add `const` version of the mapped circuit getter
burgholzer Mar 11, 2023
12ab7fa
♻️ simplify tests
burgholzer Mar 11, 2023
0aec519
🐛 fix coupling limit computation for directed architectures
burgholzer Mar 11, 2023
8897aa4
🧪 Add direction reverse tests for heuristic mapper
JoachimMarin Mar 14, 2023
34cc4bd
✨ Update heuristic algorithm for direction reverse
JoachimMarin Mar 14, 2023
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
54 changes: 38 additions & 16 deletions include/Architecture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <map>
#include <regex>
#include <unordered_map>
#include <unordered_set>
#include <vector>

constexpr std::uint8_t GATES_OF_BIDIRECTIONAL_SWAP = 3U;
Expand All @@ -32,6 +33,8 @@ constexpr std::uint32_t COST_TELEPORTATION =
2 * COST_CNOT_GATE + COST_MEASUREMENT + 4 * COST_SINGLE_QUBIT_GATE;
constexpr std::uint32_t COST_DIRECTION_REVERSE = 4 * COST_SINGLE_QUBIT_GATE;

enum class DirectionReversalStrategy { Identity, Hadamard, NotApplicable };

class Architecture {
public:
class Properties {
Expand Down Expand Up @@ -242,7 +245,9 @@ class Architecture {
return teleportationQubits;
}

[[nodiscard]] const Matrix& getDistanceTable() const { return distanceTable; }
[[nodiscard]] const DistanceTable& getDistanceTable() const {
return distanceTable;
}

[[nodiscard]] const Properties& getProperties() const { return properties; }

Expand Down Expand Up @@ -279,10 +284,19 @@ class Architecture {
singleQubitFidelities.clear();
}

[[nodiscard]] double distance(std::uint16_t control,
std::uint16_t target) const {
[[nodiscard]] double distance(std::uint16_t control, std::uint16_t target,
const qc::OpType opType) const {
if (currentTeleportations.empty()) {
return distanceTable.at(control).at(target);
const auto& distanceEntry = distanceTable.at(control).at(target);
auto dist = distanceEntry.first;
if (distanceEntry.second && opType != qc::None) {
if (supportsDirectionReversal(opType)) {
dist += computeCostDirectionReverse(opType);
} else {
dist += 10000;
}
}
return dist;
}
return static_cast<double>(bfs(control, target, currentTeleportations));
}
Expand Down Expand Up @@ -348,13 +362,19 @@ class Architecture {

static void printCouplingMap(const CouplingMap& cm, std::ostream& os);

static DirectionReversalStrategy
getDirectionReversalStrategy(qc::OpType opType);
static std::uint32_t computeCostDirectionReverse(qc::OpType opType);
static std::uint32_t computeGatesDirectionReverse(qc::OpType opType);
static bool supportsDirectionReversal(qc::OpType opType);

protected:
std::string name;
std::uint16_t nqubits = 0;
CouplingMap couplingMap = {};
CouplingMap currentTeleportations = {};
bool isBidirectional = true;
Matrix distanceTable = {};
DistanceTable distanceTable = {};
std::vector<std::pair<std::int16_t, std::int16_t>> teleportationQubits{};
Properties properties = {};
Matrix fidelityTable = {};
Expand All @@ -363,21 +383,23 @@ class Architecture {
void createDistanceTable();
void createFidelityTable();

static double costHeuristicBidirectional(const Dijkstra::Node& node) {
static std::pair<double, bool>
costHeuristicBidirectional(const Dijkstra::Node& node) {
auto length = node.cost - 1;
if (node.containsCorrectEdge) {
return length * COST_BIDIRECTIONAL_SWAP;
return {length * COST_BIDIRECTIONAL_SWAP, false};
}
throw QMAPException("In a bidrectional architecture it should not happen "
"that a node does not contain the right edge.");
}

static double costHeuristicUnidirectional(const Dijkstra::Node& node) {
static std::pair<double, bool>
costHeuristicUnidirectional(const Dijkstra::Node& node) {
auto length = node.cost - 1;
if (node.containsCorrectEdge) {
return length * COST_UNIDIRECTIONAL_SWAP;
return {length * COST_UNIDIRECTIONAL_SWAP, false};
}
return length * COST_UNIDIRECTIONAL_SWAP + COST_DIRECTION_REVERSE;
return {length * COST_UNIDIRECTIONAL_SWAP, true};
}

// added for teleportation
Expand All @@ -390,10 +412,10 @@ class Architecture {
static std::size_t findCouplingLimit(const CouplingMap& cm,
std::uint16_t nQubits);
static std::size_t
findCouplingLimit(const CouplingMap& cm, std::uint16_t nQubits,
const std::set<std::uint16_t>& qubitChoice);
static void
findCouplingLimit(std::uint16_t node, std::uint16_t curSum,
const std::vector<std::vector<std::uint16_t>>& connections,
std::vector<std::uint16_t>& d, std::vector<bool>& visited);
findCouplingLimit(const CouplingMap& cm, std::uint16_t nQubits,
const std::set<std::uint16_t>& qubitChoice);
static void findCouplingLimit(
std::uint16_t node, std::uint16_t curSum,
const std::vector<std::unordered_set<std::uint16_t>>& connections,
std::vector<std::uint16_t>& d, std::vector<bool>& visited);
};
27 changes: 25 additions & 2 deletions include/Mapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,33 @@ class Mapper {
countGates(circuit.cbegin(), circuit.cend(), info);
}
/**
* @brief count number of elementary gates and cnots in circuit and save the
* results in `info.gates` and `info.cnots`
* @brief count number of elementary gates and two-qubit gates in circuit and
* save the results in `info.gates` and `info.twoQubitGates`
*/
virtual void countGates(decltype(qcMapped.cbegin()) it,
const decltype(qcMapped.cend())& end,
MappingResults::CircuitInfo& info);

/**
* @brief inserts a two qubit operation op along edge.
* @param edge the edge in the coupling graph that controls on which physical
* qubits the operation will act. The logical and physical direction are the
* same.
* @param op the operation which is inserted
* @return total number of inserted operations
*/
std::size_t insertGate(const Edge& edge, const qc::Operation& op);

/**
* @brief inserts a two qubit operation op along a direction reversed edge.
* @param edge the edge in the coupling graph that controls on which physical
* qubits the operation will act. The logical direction of the operation is
* the reverse of the physical direction.
* @param op the operation which is inserted
* @return total number of inserted operations
*/
std::size_t insertReversedGate(const Edge& edge, const qc::Operation& op);

/**
* @brief performs optimizations on the circuit before mapping
*
Expand Down Expand Up @@ -234,6 +254,9 @@ class Mapper {

virtual MappingResults& getResults() { return results; }

const qc::QuantumComputation& getMappedCircuit() const { return qcMapped; }
qc::QuantumComputation& getMappedCircuit() { return qcMapped; }

virtual nlohmann::json json() { return results.json(); }

virtual std::string csv() { return results.csv(); }
Expand Down
16 changes: 8 additions & 8 deletions include/MappingResults.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ struct MappingResults {
std::uint16_t qubits = 0;
std::size_t gates = 0;
std::size_t singleQubitGates = 0;
std::size_t cnots = 0;
std::size_t twoQubitGates = 0;
std::size_t layers = 0;

// info in output circuit
Expand Down Expand Up @@ -62,14 +62,14 @@ struct MappingResults {
circuit["qubits"] = input.qubits;
circuit["gates"] = input.gates;
circuit["single_qubit_gates"] = input.singleQubitGates;
circuit["cnots"] = input.cnots;
circuit["cnots"] = input.twoQubitGates;

auto& mappedCirc = resultJSON["mapped_circuit"];
mappedCirc["name"] = output.name;
mappedCirc["qubits"] = output.qubits;
mappedCirc["gates"] = output.gates;
mappedCirc["single_qubit_gates"] = output.singleQubitGates;
mappedCirc["cnots"] = output.cnots;
mappedCirc["cnots"] = output.twoQubitGates;
if (!mappedCircuit.empty()) {
mappedCirc["qasm"] = mappedCircuit;
}
Expand Down Expand Up @@ -100,11 +100,11 @@ struct MappingResults {
virtual std::string csv() {
std::stringstream ss{};
ss << input.name << ";" << input.qubits << ";" << input.gates << ";"
<< input.singleQubitGates << ";" << input.cnots << ";" << architecture
<< ";" << output.name << ";" << output.qubits << ";" << output.gates
<< ";" << output.singleQubitGates << ";" << output.cnots << ";"
<< output.swaps << ";" << output.directionReverse << ";"
<< output.teleportations << ";";
<< input.singleQubitGates << ";" << input.twoQubitGates << ";"
<< architecture << ";" << output.name << ";" << output.qubits << ";"
<< output.gates << ";" << output.singleQubitGates << ";"
<< output.twoQubitGates << ";" << output.swaps << ";"
<< output.directionReverse << ";" << output.teleportations << ";";
if (timeout) {
ss << "TO";
} else {
Expand Down
40 changes: 26 additions & 14 deletions include/heuristic/HeuristicMapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,19 +179,28 @@ class HeuristicMapper : public Mapper {
continue;
}

auto cost = arch.distance(
static_cast<std::uint16_t>(
locations.at(static_cast<std::uint16_t>(gate.control))),
static_cast<std::uint16_t>(locations.at(gate.target)));
auto cost =
arch.distance(static_cast<std::uint16_t>(locations.at(
static_cast<std::uint16_t>(gate.control))),
static_cast<std::uint16_t>(locations.at(gate.target)),
gate.op->getType());
auto fidelityCost = cost;
if (admissibleHeuristic) {
costHeur = std::max(costHeur, fidelityCost);
} else {
costHeur += fidelityCost;
}
if (cost > COST_DIRECTION_REVERSE) {
done = false;
return;
if (Architecture::supportsDirectionReversal(gate.op->getType())) {
if (cost >
Architecture::computeCostDirectionReverse(gate.op->getType())) {
done = false;
return;
}
} else {
if (cost > COST_UNIDIRECTIONAL_SWAP * 2) {
done = false;
return;
}
}
}
}
Expand Down Expand Up @@ -230,20 +239,22 @@ class HeuristicMapper : public Mapper {
* @brief returns distance of the given logical qubit pair according to the
* current mapping
*/
double distanceOnArchitectureOfLogicalQubits(std::uint16_t control,
std::uint16_t target) {
double distanceOnArchitectureOfLogicalQubits(std::uint16_t control,
std::uint16_t target,
const qc::OpType opType) {
return architecture.distance(
static_cast<std::uint16_t>(locations.at(control)),
static_cast<std::uint16_t>(locations.at(target)));
static_cast<std::uint16_t>(locations.at(target)), opType);
}

/**
* @brief returns distance of the given physical qubit pair on the
* architecture
*/
double distanceOnArchitectureOfPhysicalQubits(std::uint16_t control,
std::uint16_t target) {
return architecture.distance(control, target);
double distanceOnArchitectureOfPhysicalQubits(std::uint16_t control,
std::uint16_t target,
const qc::OpType opType) {
return architecture.distance(control, target, opType);
}

/**
Expand All @@ -254,7 +265,8 @@ class HeuristicMapper : public Mapper {
* to `target`
* @param target an unmapped logical qubit
*/
virtual void mapToMinDistance(std::uint16_t source, std::uint16_t target);
virtual void mapToMinDistance(std::uint16_t source, std::uint16_t target,
const qc::OpType opType);

/**
* @brief gathers all qubits that are acted on by a 2-qubit-gate in the given
Expand Down
16 changes: 9 additions & 7 deletions include/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
#include <unordered_set>
#include <vector>

using Matrix = std::vector<std::vector<double>>;
using Edge = std::pair<std::uint16_t, std::uint16_t>;
using CouplingMap = std::set<Edge>;
using QubitSubset = std::set<std::uint16_t>;
using Matrix = std::vector<std::vector<double>>;
using DistanceTable = std::vector<std::vector<std::pair<double, bool>>>;
using Edge = std::pair<std::uint16_t, std::uint16_t>;
using CouplingMap = std::set<Edge>;
using QubitSubset = std::set<std::uint16_t>;

struct Exchange {
Exchange(const std::uint16_t f, const std::uint16_t s, const qc::OpType type)
Expand Down Expand Up @@ -60,9 +61,10 @@ class Dijkstra {
double cost = -1.;
};

static void buildTable(std::uint16_t n, const CouplingMap& couplingMap,
Matrix& distanceTable,
const std::function<double(const Node&)>& cost);
static void
buildTable(std::uint16_t n, const CouplingMap& couplingMap,
DistanceTable& distanceTable,
const std::function<std::pair<double, bool>(const Node&)>& cost);

protected:
static void dijkstra(const CouplingMap& couplingMap, std::vector<Node>& nodes,
Expand Down
3 changes: 2 additions & 1 deletion mqt/qmap/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,8 @@ PYBIND11_MODULE(pyqmap, m) {
.def_readwrite("gates", &MappingResults::CircuitInfo::gates)
.def_readwrite("single_qubit_gates",
&MappingResults::CircuitInfo::singleQubitGates)
.def_readwrite("cnots", &MappingResults::CircuitInfo::cnots)
.def_readwrite("two_qubit_gates",
&MappingResults::CircuitInfo::twoQubitGates)
.def_readwrite("layers", &MappingResults::CircuitInfo::layers)
.def_readwrite("swaps", &MappingResults::CircuitInfo::swaps)
.def_readwrite("direction_reverse",
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,7 @@ log_cli_level = "INFO"
xfail_strict = true
filterwarnings = [
"error",
# See https://github.com/Qiskit/rustworkx/pull/728
'ignore:RetworkxLoader.exec_module\(\) not found; falling back to load_module\(\):ImportWarning',
'ignore:__package__ != __spec__.parent:ImportWarning',
]
[tool.coverage.run]
source = ["mqt.qmap"]
Expand Down
Loading