Skip to content
Draft
Show file tree
Hide file tree
Changes from 13 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
10 changes: 9 additions & 1 deletion include/Architecture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,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 GateFlipStrategy { Unknown, Identity, Swap, Hadamard };
Comment thread
burgholzer marked this conversation as resolved.
Outdated
Comment thread
burgholzer marked this conversation as resolved.
Outdated

class Architecture {
public:
class Properties {
Expand Down Expand Up @@ -348,6 +350,10 @@ class Architecture {

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

static GateFlipStrategy getGateFlipStrategy(qc::OpType opType);
static std::uint32_t computeCostDirectionReverse(qc::OpType opType);
static std::uint32_t computeGatesDirectionReverse(qc::OpType opType);

protected:
std::string name;
std::uint16_t nqubits = 0;
Expand Down Expand Up @@ -377,7 +383,9 @@ class Architecture {
if (node.containsCorrectEdge) {
return length * COST_UNIDIRECTIONAL_SWAP;
}
return length * COST_UNIDIRECTIONAL_SWAP + COST_DIRECTION_REVERSE;
// TODO: distance has no gate context, but gates have different costs
return length * COST_UNIDIRECTIONAL_SWAP +
Architecture::computeCostDirectionReverse(qc::OpType::X);
Comment thread
burgholzer marked this conversation as resolved.
Outdated
}

// added for teleportation
Expand Down
24 changes: 22 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 controlled gates in circuit and
Comment thread
burgholzer marked this conversation as resolved.
Outdated
* 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 insertFlippedGate(const Edge& edge, const qc::Operation& op);

/**
* @brief performs optimizations on the circuit before mapping
*
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
3 changes: 2 additions & 1 deletion include/heuristic/HeuristicMapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,8 @@ class HeuristicMapper : public Mapper {
} else {
costHeur += fidelityCost;
}
if (cost > COST_DIRECTION_REVERSE) {
if (cost >
Architecture::computeCostDirectionReverse(gate.op->getType())) {
done = false;
return;
}
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
45 changes: 45 additions & 0 deletions src/Architecture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -687,3 +687,48 @@ void Architecture::printCouplingMap(const CouplingMap& cm, std::ostream& os) {
}
os << "}" << std::endl;
}

GateFlipStrategy Architecture::getGateFlipStrategy(qc::OpType opType) {
switch (opType) {
case qc::None:
return GateFlipStrategy::Unknown;
case qc::X:
case qc::SX:
case qc::SXdag:
return GateFlipStrategy::Hadamard;
case qc::Z:
case qc::S:
case qc::Sdag:
case qc::T:
case qc::Tdag:
case qc::Phase:
case qc::RZ:
case qc::SWAP:
return GateFlipStrategy::Identity;
default:
return GateFlipStrategy::Swap;
}
}

std::uint32_t Architecture::computeCostDirectionReverse(qc::OpType opType) {
switch (Architecture::getGateFlipStrategy(opType)) {
case GateFlipStrategy::Identity:
return 0;
case GateFlipStrategy::Hadamard:
return 4 * COST_SINGLE_QUBIT_GATE;
case GateFlipStrategy::Swap:
case GateFlipStrategy::Unknown:
return 2 * COST_BIDIRECTIONAL_SWAP;
}
}
Comment thread Fixed
std::uint32_t Architecture::computeGatesDirectionReverse(qc::OpType opType) {
switch (Architecture::getGateFlipStrategy(opType)) {
case GateFlipStrategy::Identity:
return 0;
case GateFlipStrategy::Hadamard:
return 4;
case GateFlipStrategy::Swap:
case GateFlipStrategy::Unknown:
return 2 * GATES_OF_BIDIRECTIONAL_SWAP;
}
}
Comment thread Fixed
42 changes: 37 additions & 5 deletions src/Mapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,18 +301,18 @@ void Mapper::countGates(decltype(qcMapped.cbegin()) it,
if (g->getType() == qc::SWAP) {
if (architecture.bidirectional()) {
info.gates += GATES_OF_BIDIRECTIONAL_SWAP;
info.cnots += GATES_OF_BIDIRECTIONAL_SWAP;
info.twoQubitGates += GATES_OF_BIDIRECTIONAL_SWAP;
} else {
info.gates += GATES_OF_UNIDIRECTIONAL_SWAP;
info.cnots += GATES_OF_BIDIRECTIONAL_SWAP;
info.singleQubitGates += GATES_OF_DIRECTION_REVERSE;
info.twoQubitGates += GATES_OF_BIDIRECTIONAL_SWAP;
info.singleQubitGates +=
Architecture::computeGatesDirectionReverse(g->getType());
}
} else if (g->getControls().empty()) {
++info.singleQubitGates;
++info.gates;
} else {
assert(g->getType() == qc::X);
++info.cnots;
++info.twoQubitGates;
++info.gates;
}
continue;
Expand All @@ -324,3 +324,35 @@ void Mapper::countGates(decltype(qcMapped.cbegin()) it,
}
}
}
std::size_t Mapper::insertGate(const Edge& edge, const qc::Operation& op) {
auto newOp = op.clone();
newOp->setControls({qc::Control{edge.first}});
newOp->setTargets({edge.second});
qcMapped.emplace_back(newOp);
return 1;
}

std::size_t Mapper::insertFlippedGate(const Edge& edge,
Comment thread
burgholzer marked this conversation as resolved.
Outdated
const qc::Operation& op) {
std::size_t tmp = 0;
switch (Architecture::getGateFlipStrategy(op.getType())) {
case GateFlipStrategy::Identity:
// no additional gates required to reverse direction
return insertGate(edge, op);
case GateFlipStrategy::Swap:
case GateFlipStrategy::Unknown:
// swap gates are used to reverse direction
qcMapped.swap(edge.first, edge.second);
tmp = insertGate(edge, op);
qcMapped.swap(edge.second, edge.first);
return 2 + tmp;
case GateFlipStrategy::Hadamard:
// four hadamard gates to reverse direction
qcMapped.h(edge.first);
qcMapped.h(edge.second);
tmp = insertGate(edge, op);
qcMapped.h(edge.first);
qcMapped.h(edge.second);
return tmp + 4;
}
}
Comment thread Fixed
60 changes: 30 additions & 30 deletions src/exact/ExactMapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,38 +275,33 @@ void ExactMapper::map(const Configuration& settings) {
op->getParameter().at(0), op->getParameter().at(1),
op->getParameter().at(2));
} else {
const Edge cnot = {locations.at(static_cast<std::size_t>(gate.control)),
locations.at(gate.target)};
const Edge controlledGate = {
locations.at(static_cast<std::size_t>(gate.control)),
locations.at(gate.target)};

if (architecture.getCouplingMap().find(cnot) ==
if (architecture.getCouplingMap().find(controlledGate) ==
architecture.getCouplingMap().end()) {
const Edge reverse = {cnot.second, cnot.first};
const Edge reverse = {controlledGate.second, controlledGate.first};
if (architecture.getCouplingMap().find(reverse) ==
architecture.getCouplingMap().end()) {
throw QMAPException(
"Invalid CNOT: " + std::to_string(reverse.first) + "-" +
std::to_string(reverse.second));
throw QMAPException("Invalid controlled gate " + op->getName() +
": " + std::to_string(reverse.first) + "-" +
std::to_string(reverse.second));
}
if (settings.verbose) {
std::cout
<< i
<< ": Added (direction-reversed) cnot with control and target: "
<< cnot.first << " " << cnot.second << std::endl;
std::cout << i << ": Added (direction-reversed) controlled gate "
<< op->getName()
<< " with control and target: " << controlledGate.first
<< " " << controlledGate.second << std::endl;
}
qcMapped.h(reverse.first);
qcMapped.h(reverse.second);
qcMapped.x(reverse.second,
qc::Control{static_cast<qc::Qubit>(reverse.first)});
qcMapped.h(reverse.second);
qcMapped.h(reverse.first);
insertFlippedGate(reverse, *op);
Comment thread
burgholzer marked this conversation as resolved.
Outdated
} else {
if (settings.verbose) {
std::cout << i
<< ": Added cnot with control and target: " << cnot.first
<< " " << cnot.second << std::endl;
std::cout << i << ": Added controlled gate " << op->getName()
<< " with control and target: " << controlledGate.first
<< " " << controlledGate.second << std::endl;
}
qcMapped.x(cnot.second,
qc::Control{static_cast<qc::Qubit>(cnot.first)});
insertGate(controlledGate, *op);
}
}
}
Expand Down Expand Up @@ -354,7 +349,7 @@ void ExactMapper::map(const Configuration& settings) {

// 10) re-count gates
results.output.singleQubitGates = 0U;
results.output.cnots = 0U;
results.output.twoQubitGates = 0U;
results.output.gates = 0U;
countGates(qcMapped, results.output);

Expand Down Expand Up @@ -739,9 +734,12 @@ number of variables: (|L|-1) * m!
[static_cast<std::size_t>(gate.control)];
reverse = reverse || (indexFT && indexSC);
}
cost = cost + LogicTerm::ite(reverse,
LogicTerm(::GATES_OF_DIRECTION_REVERSE),
LogicTerm(0));
cost = cost +
LogicTerm::ite(reverse,
LogicTerm(static_cast<int>(
Architecture::computeGatesDirectionReverse(
gate.op->getType()))),
LogicTerm(0));
Comment thread
burgholzer marked this conversation as resolved.
}
}
lb->minimize(cost);
Expand All @@ -763,9 +761,9 @@ number of variables: (|L|-1) * m!
// quickly determine cost
choiceResults.output.singleQubitGates =
choiceResults.input.singleQubitGates;
choiceResults.output.cnots = choiceResults.input.cnots;
choiceResults.output.gates =
choiceResults.output.singleQubitGates + choiceResults.output.cnots;
choiceResults.output.twoQubitGates = choiceResults.input.twoQubitGates;
choiceResults.output.gates = choiceResults.output.singleQubitGates +
choiceResults.output.twoQubitGates;
assert(choiceResults.output.swaps == 0U);
assert(choiceResults.output.directionReverse == 0U);
// swaps
Expand Down Expand Up @@ -842,7 +840,9 @@ number of variables: (|L|-1) * m!
if (m->getBoolValue(indexFT, lb.get()) &&
m->getBoolValue(indexSC, lb.get())) {
choiceResults.output.directionReverse++;
choiceResults.output.gates += GATES_OF_DIRECTION_REVERSE;
choiceResults.output.gates +=
Architecture::computeGatesDirectionReverse(
gate.op->getType());
Comment thread
burgholzer marked this conversation as resolved.
}
}
}
Expand Down
25 changes: 8 additions & 17 deletions src/heuristic/HeuristicMapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,31 +94,22 @@ void HeuristicMapper::map(const Configuration& configuration) {
gateidx++;
}
} else {
const Edge cnot = {
const Edge controlledGate = {
locations.at(static_cast<std::uint16_t>(gate.control)),
locations.at(gate.target)};
if (architecture.getCouplingMap().find(cnot) ==
if (architecture.getCouplingMap().find(controlledGate) ==
architecture.getCouplingMap().end()) {
const Edge reverse = {cnot.second, cnot.first};
const Edge reverse = {controlledGate.second, controlledGate.first};
if (architecture.getCouplingMap().find(reverse) ==
architecture.getCouplingMap().end()) {
throw QMAPException(
"Invalid CNOT: " + std::to_string(reverse.first) + "-" +
std::to_string(reverse.second));
throw QMAPException("Invalid controlled gate " + op->getName() +
": " + std::to_string(reverse.first) + "-" +
std::to_string(reverse.second));
}
qcMapped.h(reverse.first);
qcMapped.h(reverse.second);
qcMapped.x(reverse.second,
qc::Control{static_cast<qc::Qubit>(reverse.first)});
qcMapped.h(reverse.second);
qcMapped.h(reverse.first);

gateidx += insertFlippedGate(reverse, *op);
Comment thread
burgholzer marked this conversation as resolved.
Outdated
results.output.directionReverse++;
gateidx += 5;
} else {
qcMapped.x(cnot.second,
qc::Control{static_cast<qc::Qubit>(cnot.first)});
gateidx++;
gateidx += insertGate(controlledGate, *op);
}
}
}
Expand Down
Loading