Skip to content

Commit

Permalink
🐛 Fix Circuit Extraction in Exact Mapper (#217)
Browse files Browse the repository at this point in the history
  • Loading branch information
burgholzer authored Jan 19, 2023
2 parents c4ebdf7 + f43d24a commit 740e340
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 23 deletions.
88 changes: 65 additions & 23 deletions src/exact/ExactMapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,25 +256,27 @@ void ExactMapper::map(const Configuration& settings) {
for (std::size_t i = 0U; i < layers.size(); ++i) {
if (i == 0U) {
qcMapped.initialLayout.clear();
qcMapped.outputPermutation.clear();

// no swaps but initial permutation
for (const auto& [physical, logical] : *swapsIterator) {
locations.at(logical) = static_cast<std::int16_t>(physical);
qubits.at(physical) = static_cast<std::int16_t>(logical);
qcMapped.initialLayout[static_cast<qc::Qubit>(physical)] =
static_cast<qc::Qubit>(logical);
qcMapped.outputPermutation[static_cast<qc::Qubit>(physical)] =
static_cast<qc::Qubit>(logical);
}

// place remaining architecture qubits
placeRemainingArchitectureQubits();

if (settings.verbose) {
std::cout << "Qubits: ";
for (auto q = 0U; q < architecture.getNqubits(); ++q) {
std::cout << qubits.at(q) << " ";
}
std::cout << " Locations: ";
for (std::size_t q = 0; q < qc.getNqubits(); ++q) {
std::cout << locations.at(q) << " ";
}
std::cout << std::endl;
}
++swapsIterator;
Expand Down Expand Up @@ -341,19 +343,23 @@ void ExactMapper::map(const Configuration& settings) {
// apply swaps before layer
for (auto it = (*swapsIterator).rbegin(); it != (*swapsIterator).rend();
++it) {
auto& swap = *it;
qcMapped.swap(swap.first, swap.second);
std::swap(qcMapped.outputPermutation.at(swap.first),
qcMapped.outputPermutation.at(swap.second));
std::swap(qubits.at(swap.first), qubits.at(swap.second));
std::swap(
locations.at(static_cast<std::size_t>(qubits.at(swap.first))),
locations.at(static_cast<std::size_t>(qubits.at(swap.second))));
const auto& [q0, q1] = *it;
const auto logical0 = static_cast<qc::Qubit>(qubits.at(q0));
const auto logical1 = static_cast<qc::Qubit>(qubits.at(q1));
qcMapped.swap(q0, q1);
std::swap(qubits.at(q0), qubits.at(q1));
locations.at(logical0) = static_cast<std::int16_t>(q1);
locations.at(logical1) = static_cast<std::int16_t>(q0);

if (settings.verbose) {
std::cout << "Qubits: ";
for (auto q = 0U; q < architecture.getNqubits(); ++q) {
std::cout << qubits.at(q) << " ";
}
std::cout << " Locations: ";
for (std::size_t q = 0; q < qc.getNqubits(); ++q) {
std::cout << locations.at(q) << " ";
}
std::cout << std::endl;
}
}
Expand All @@ -363,6 +369,13 @@ void ExactMapper::map(const Configuration& settings) {
}
}

// set output permutation
qcMapped.outputPermutation.clear();
for (qc::Qubit logical = 0; logical < qc.getNqubits(); ++logical) {
const auto physical = static_cast<qc::Qubit>(locations.at(logical));
qcMapped.outputPermutation[physical] = logical;
}

// 9) apply post mapping optimizations
postMappingOptimizations(config);

Expand Down Expand Up @@ -783,22 +796,51 @@ number of variables: (|L|-1) * m!
assert(choiceResults.output.directionReverse == 0U);
// swaps
for (std::size_t k = 1; k < reducedLayerIndices.size(); ++k) {
auto& i = x[k - 1];
auto& j = x[k];

for (const auto qubit : qubitChoice) {
for (std::size_t q = 0; q < qc.getNqubits(); ++q) {
if (m->getBoolValue(i[physicalQubitIndex[qubit]][q], lb.get())) {
// logical qubit q was mapped to physical qubit Q
for (const auto otherQubit : qubitChoice) {
// and has been assigned to physical qubit P going forward
if (m->getBoolValue(j[physicalQubitIndex[otherQubit]][q],
lb.get())) {
pi[physicalQubitIndex[qubit]] = otherQubit;
if (qubitChoice.size() == qc.getNqubits()) {
// When as many qubits of the architecture are being considered
// as in the circuit, the assignment of the logical to the physical
// qubits is a bijection. Hence, we the assignment matrices X can be
// used to directly infer the permutation of the qubits in each layer.
auto& oldAssignment = x[k - 1];
auto& newAssignment = x[k];
for (const auto physicalQubit : qubitChoice) {
for (std::size_t logicalQubit = 0; logicalQubit < qc.getNqubits();
++logicalQubit) {
if (const auto oldIndex = physicalQubitIndex[physicalQubit];
m->getBoolValue(oldAssignment[oldIndex][logicalQubit],
lb.get())) {
for (const auto newPhysicalQubit : qubitChoice) {
if (const auto newIndex = physicalQubitIndex[newPhysicalQubit];
m->getBoolValue(newAssignment[newIndex][logicalQubit],
lb.get())) {
pi[oldIndex] = newPhysicalQubit;
break;
}
}
break;
}
}
}
} else {
// When more qubits of the architecture are being considered than are in
// the circuit, the assignment of the logical to the physical qubits
// cannot be a bijection. Hence, the permutation variables y have to be
// used to infer the permutation of the qubits in each layer. This is
// mainly because the additional qubits movement cannot be inferred
// from the assignment matrices X.
piCount = 0;
internalPiCount = 0;
// sort the permutation of the qubits to start fresh
std::sort(pi.begin(), pi.end());
do {
if (skippedPi.count(piCount) == 0 || !config.enableSwapLimits) {
if (m->getBoolValue(y[k - 1][internalPiCount], lb.get())) {
break;
}
++internalPiCount;
}
++piCount;
} while (std::next_permutation(pi.begin(), pi.end()));
}

architecture.minimumNumberOfSwaps(pi, swaps.at(k));
Expand Down
23 changes: 23 additions & 0 deletions test/test_exact.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -497,3 +497,26 @@ TEST_F(ExactTest, NoMeasurmentsAdded) {
EXPECT_EQ(qcMapped.getNops(), 4U);
EXPECT_NE(qcMapped.back()->getType(), qc::Measure);
}

TEST_F(ExactTest, Test4QCircuitThatUsesAll5Q) {
Architecture arch;
const CouplingMap cm = {{0, 1}, {1, 0}, {1, 2}, {2, 1}, {2, 3},
{3, 2}, {3, 4}, {4, 3}, {4, 0}, {0, 4}};
arch.loadCouplingMap(5, cm);

std::stringstream ss{"OPENQASM 2.0;\ninclude \"qelib1.inc\";\n"
"qreg q[4];\n"
"cx q[0],q[1];\n"
"cx q[1],q[2];\n"
"cx q[2],q[3];\n"
"cx q[3],q[0];\n"};
qc.import(ss, qc::Format::OpenQASM);

auto mapper = ExactMapper(qc, arch);
// explicitly do not use subsets, but the full architecture
settings.useSubsets = false;

ASSERT_NO_THROW(mapper.map(settings););
const auto& results = mapper.getResults();
EXPECT_EQ(results.output.swaps, 1);
}

0 comments on commit 740e340

Please sign in to comment.