Skip to content

Commit 740e340

Browse files
authored
🐛 Fix Circuit Extraction in Exact Mapper (#217)
2 parents c4ebdf7 + f43d24a commit 740e340

File tree

2 files changed

+88
-23
lines changed

2 files changed

+88
-23
lines changed

src/exact/ExactMapper.cpp

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -256,25 +256,27 @@ void ExactMapper::map(const Configuration& settings) {
256256
for (std::size_t i = 0U; i < layers.size(); ++i) {
257257
if (i == 0U) {
258258
qcMapped.initialLayout.clear();
259-
qcMapped.outputPermutation.clear();
260259

261260
// no swaps but initial permutation
262261
for (const auto& [physical, logical] : *swapsIterator) {
263262
locations.at(logical) = static_cast<std::int16_t>(physical);
264263
qubits.at(physical) = static_cast<std::int16_t>(logical);
265264
qcMapped.initialLayout[static_cast<qc::Qubit>(physical)] =
266265
static_cast<qc::Qubit>(logical);
267-
qcMapped.outputPermutation[static_cast<qc::Qubit>(physical)] =
268-
static_cast<qc::Qubit>(logical);
269266
}
270267

271268
// place remaining architecture qubits
272269
placeRemainingArchitectureQubits();
273270

274271
if (settings.verbose) {
272+
std::cout << "Qubits: ";
275273
for (auto q = 0U; q < architecture.getNqubits(); ++q) {
276274
std::cout << qubits.at(q) << " ";
277275
}
276+
std::cout << " Locations: ";
277+
for (std::size_t q = 0; q < qc.getNqubits(); ++q) {
278+
std::cout << locations.at(q) << " ";
279+
}
278280
std::cout << std::endl;
279281
}
280282
++swapsIterator;
@@ -341,19 +343,23 @@ void ExactMapper::map(const Configuration& settings) {
341343
// apply swaps before layer
342344
for (auto it = (*swapsIterator).rbegin(); it != (*swapsIterator).rend();
343345
++it) {
344-
auto& swap = *it;
345-
qcMapped.swap(swap.first, swap.second);
346-
std::swap(qcMapped.outputPermutation.at(swap.first),
347-
qcMapped.outputPermutation.at(swap.second));
348-
std::swap(qubits.at(swap.first), qubits.at(swap.second));
349-
std::swap(
350-
locations.at(static_cast<std::size_t>(qubits.at(swap.first))),
351-
locations.at(static_cast<std::size_t>(qubits.at(swap.second))));
346+
const auto& [q0, q1] = *it;
347+
const auto logical0 = static_cast<qc::Qubit>(qubits.at(q0));
348+
const auto logical1 = static_cast<qc::Qubit>(qubits.at(q1));
349+
qcMapped.swap(q0, q1);
350+
std::swap(qubits.at(q0), qubits.at(q1));
351+
locations.at(logical0) = static_cast<std::int16_t>(q1);
352+
locations.at(logical1) = static_cast<std::int16_t>(q0);
352353

353354
if (settings.verbose) {
355+
std::cout << "Qubits: ";
354356
for (auto q = 0U; q < architecture.getNqubits(); ++q) {
355357
std::cout << qubits.at(q) << " ";
356358
}
359+
std::cout << " Locations: ";
360+
for (std::size_t q = 0; q < qc.getNqubits(); ++q) {
361+
std::cout << locations.at(q) << " ";
362+
}
357363
std::cout << std::endl;
358364
}
359365
}
@@ -363,6 +369,13 @@ void ExactMapper::map(const Configuration& settings) {
363369
}
364370
}
365371

372+
// set output permutation
373+
qcMapped.outputPermutation.clear();
374+
for (qc::Qubit logical = 0; logical < qc.getNqubits(); ++logical) {
375+
const auto physical = static_cast<qc::Qubit>(locations.at(logical));
376+
qcMapped.outputPermutation[physical] = logical;
377+
}
378+
366379
// 9) apply post mapping optimizations
367380
postMappingOptimizations(config);
368381

@@ -783,22 +796,51 @@ number of variables: (|L|-1) * m!
783796
assert(choiceResults.output.directionReverse == 0U);
784797
// swaps
785798
for (std::size_t k = 1; k < reducedLayerIndices.size(); ++k) {
786-
auto& i = x[k - 1];
787-
auto& j = x[k];
788-
789-
for (const auto qubit : qubitChoice) {
790-
for (std::size_t q = 0; q < qc.getNqubits(); ++q) {
791-
if (m->getBoolValue(i[physicalQubitIndex[qubit]][q], lb.get())) {
792-
// logical qubit q was mapped to physical qubit Q
793-
for (const auto otherQubit : qubitChoice) {
794-
// and has been assigned to physical qubit P going forward
795-
if (m->getBoolValue(j[physicalQubitIndex[otherQubit]][q],
796-
lb.get())) {
797-
pi[physicalQubitIndex[qubit]] = otherQubit;
799+
if (qubitChoice.size() == qc.getNqubits()) {
800+
// When as many qubits of the architecture are being considered
801+
// as in the circuit, the assignment of the logical to the physical
802+
// qubits is a bijection. Hence, we the assignment matrices X can be
803+
// used to directly infer the permutation of the qubits in each layer.
804+
auto& oldAssignment = x[k - 1];
805+
auto& newAssignment = x[k];
806+
for (const auto physicalQubit : qubitChoice) {
807+
for (std::size_t logicalQubit = 0; logicalQubit < qc.getNqubits();
808+
++logicalQubit) {
809+
if (const auto oldIndex = physicalQubitIndex[physicalQubit];
810+
m->getBoolValue(oldAssignment[oldIndex][logicalQubit],
811+
lb.get())) {
812+
for (const auto newPhysicalQubit : qubitChoice) {
813+
if (const auto newIndex = physicalQubitIndex[newPhysicalQubit];
814+
m->getBoolValue(newAssignment[newIndex][logicalQubit],
815+
lb.get())) {
816+
pi[oldIndex] = newPhysicalQubit;
817+
break;
818+
}
798819
}
820+
break;
799821
}
800822
}
801823
}
824+
} else {
825+
// When more qubits of the architecture are being considered than are in
826+
// the circuit, the assignment of the logical to the physical qubits
827+
// cannot be a bijection. Hence, the permutation variables y have to be
828+
// used to infer the permutation of the qubits in each layer. This is
829+
// mainly because the additional qubits movement cannot be inferred
830+
// from the assignment matrices X.
831+
piCount = 0;
832+
internalPiCount = 0;
833+
// sort the permutation of the qubits to start fresh
834+
std::sort(pi.begin(), pi.end());
835+
do {
836+
if (skippedPi.count(piCount) == 0 || !config.enableSwapLimits) {
837+
if (m->getBoolValue(y[k - 1][internalPiCount], lb.get())) {
838+
break;
839+
}
840+
++internalPiCount;
841+
}
842+
++piCount;
843+
} while (std::next_permutation(pi.begin(), pi.end()));
802844
}
803845

804846
architecture.minimumNumberOfSwaps(pi, swaps.at(k));

test/test_exact.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,3 +497,26 @@ TEST_F(ExactTest, NoMeasurmentsAdded) {
497497
EXPECT_EQ(qcMapped.getNops(), 4U);
498498
EXPECT_NE(qcMapped.back()->getType(), qc::Measure);
499499
}
500+
501+
TEST_F(ExactTest, Test4QCircuitThatUsesAll5Q) {
502+
Architecture arch;
503+
const CouplingMap cm = {{0, 1}, {1, 0}, {1, 2}, {2, 1}, {2, 3},
504+
{3, 2}, {3, 4}, {4, 3}, {4, 0}, {0, 4}};
505+
arch.loadCouplingMap(5, cm);
506+
507+
std::stringstream ss{"OPENQASM 2.0;\ninclude \"qelib1.inc\";\n"
508+
"qreg q[4];\n"
509+
"cx q[0],q[1];\n"
510+
"cx q[1],q[2];\n"
511+
"cx q[2],q[3];\n"
512+
"cx q[3],q[0];\n"};
513+
qc.import(ss, qc::Format::OpenQASM);
514+
515+
auto mapper = ExactMapper(qc, arch);
516+
// explicitly do not use subsets, but the full architecture
517+
settings.useSubsets = false;
518+
519+
ASSERT_NO_THROW(mapper.map(settings););
520+
const auto& results = mapper.getResults();
521+
EXPECT_EQ(results.output.swaps, 1);
522+
}

0 commit comments

Comments
 (0)