Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel

- ✨ Add Sampler and Estimator Primitives to the QDMI-Qiskit Interface ([#1507]) ([**@marcelwa**])
- ✨ Add conversions between Jeff and QCO ([#1479], [#1548], [#1565]) ([**@denialhaag**])
- ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568]) ([**@MatthiasReumann**])
- ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581]) ([**@MatthiasReumann**])
- ✨ Add initial infrastructure for new QC and QCO MLIR dialects
([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1570], [#1572], [#1573])
([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**])
Expand Down Expand Up @@ -333,6 +333,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool

<!-- PR links -->

[#1581]: https://github.com/munich-quantum-toolkit/core/pull/1581
[#1573]: https://github.com/munich-quantum-toolkit/core/pull/1573
[#1572]: https://github.com/munich-quantum-toolkit/core/pull/1572
[#1571]: https://github.com/munich-quantum-toolkit/core/pull/1571
Expand Down
147 changes: 107 additions & 40 deletions mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,65 @@ struct MappingPass : impl::MappingPassBase<MappingPass> {

using MinQueue = std::priority_queue<Node, std::vector<Node>, std::greater<>>;

struct [[nodiscard]] TrialResult {
explicit TrialResult(Layout layout) : layout(std::move(layout)) {}

/// @brief The computed initial layout.
Layout layout;
/// @brief A vector of SWAPs for each layer.
SmallVector<SmallVector<IndexGate>> swaps;
/// @brief The number of inserted SWAPs.
std::size_t nswaps{};
};

struct SynchronizationMap {
/**
* @returns true if the operation is contained in the map.
*/
bool contains(Operation* op) const { return onHold.contains(op); }

/**
* @brief Add op with respective iterator and ref count to the map.
*/
void add(Operation* op, WireIterator* it, const std::size_t cnt) {
onHold.try_emplace(op, SmallVector<WireIterator*>{it});
// Decrease the cnt by one because the op was visited when adding.
refCount.try_emplace(op, cnt - 1);
}

/**
* @brief Decrement ref count of op and potentially release its iterators.
*/
std::optional<ArrayRef<WireIterator*>> visit(Operation* op,
WireIterator* it) {
assert(refCount.contains(op) && "expected sync map to contain op");

// Add iterator for later release.
onHold[op].push_back(it);

// Release iterators whenever the ref count reaches zero.
if (--refCount[op] == 0) {
return onHold[op];
}

return std::nullopt;
}

/**
* @brief Clear the contents of the map.
*/
void clear() {
onHold.clear();
refCount.clear();
}

private:
/// @brief Maps operations to to-be-released iterators.
DenseMap<Operation*, SmallVector<WireIterator*, 2>> onHold;
/// @brief Maps operations to ref counts.
DenseMap<Operation*, std::size_t> refCount;
};

public:
using MappingPassBase::MappingPassBase;

Expand Down Expand Up @@ -382,17 +441,6 @@ struct MappingPass : impl::MappingPassBase<MappingPass> {
}

private:
struct [[nodiscard]] TrialResult {
explicit TrialResult(Layout layout) : layout(std::move(layout)) {}

/// @brief The computed initial layout.
Layout layout;
/// @brief A vector of SWAPs for each layer.
SmallVector<SmallVector<IndexGate>> swaps;
/// @brief The number of inserted SWAPs.
std::size_t nswaps{};
};

/**
* @brief Find the best trial result in terms of the number of SWAPs.
* @returns the best trial result or nullptr if no result is valid.
Expand Down Expand Up @@ -615,28 +663,33 @@ struct MappingPass : impl::MappingPassBase<MappingPass> {
while (shouldContinue(it)) {
const auto res =
TypeSwitch<Operation*, WalkResult>(it.operation())
.Case<UnitaryOpInterface>([&](UnitaryOpInterface op) {
assert(op.getNumQubits() > 0 && op.getNumQubits() <= 2);
.Case<BarrierOp>([&](auto) {
std::ranges::advance(it, step);
return WalkResult::advance();
})
.template Case<UnitaryOpInterface>(
[&](UnitaryOpInterface op) {
assert(op.getNumQubits() > 0 && op.getNumQubits() <= 2);

if (op.getNumQubits() == 1) {
std::ranges::advance(it, step);
return WalkResult::advance();
}
if (op.getNumQubits() == 1) {
std::ranges::advance(it, step);
return WalkResult::advance();
}

if (visited.contains(op)) {
const auto otherIndex = visited[op];
layer.insert(std::make_pair(index, otherIndex));
if (visited.contains(op)) {
const auto otherIndex = visited[op];
layer.insert(std::make_pair(index, otherIndex));

std::ranges::advance(wires[index], step);
std::ranges::advance(wires[otherIndex], step);
std::ranges::advance(wires[index], step);
std::ranges::advance(wires[otherIndex], step);

visited.erase(op);
} else {
visited.try_emplace(op, index);
}
visited.erase(op);
} else {
visited.try_emplace(op, index);
}

return WalkResult::interrupt();
})
return WalkResult::interrupt();
})
.template Case<AllocOp, StaticOp, ResetOp, MeasureOp,
DeallocOp>([&](auto) {
std::ranges::advance(it, step);
Expand Down Expand Up @@ -709,25 +762,30 @@ struct MappingPass : impl::MappingPassBase<MappingPass> {
// Helper function that advances the iterator to the input qubit (the
// operation producing it) of a deallocation or two-qubit op.
const auto advFront = [](WireIterator& it) {
auto next = std::next(it);
while (true) {
const auto next = std::next(it);
if (isa<DeallocOp>(next.operation())) {
break;
}

if (isa<BarrierOp>(next.operation())) {
break;
}

auto op = dyn_cast<UnitaryOpInterface>(next.operation());
if (op && op.getNumQubits() > 1) {
break;
}

std::ranges::advance(it, 1);
std::ranges::advance(next, 1);
}
};

auto wires = toWires(place(dynQubits, result.layout, funcBody, rewriter));

DenseMap<Operation*, WireIterator*> seen;
for (const auto [i, swaps] : enumerate(result.swaps)) {
SynchronizationMap ready;
for (const auto& swaps : result.swaps) {
// Advance all wires to the next front of one-qubit outputs
// (the SSA values).
for_each(wires, advFront);
Expand Down Expand Up @@ -760,21 +818,30 @@ struct MappingPass : impl::MappingPassBase<MappingPass> {
std::ranges::advance(wires[hw1], 1);
}

// Jump over "ready" two-qubit gates.
// Jump over "ready" gates.
for (auto& it : wires) {
auto op = dyn_cast<UnitaryOpInterface>(std::next(it).operation());
if (op && op.getNumQubits() > 1) {
if (seen.contains(op)) {
std::ranges::advance(it, 1);
std::ranges::advance(*seen[op], 1);
continue;
}
if (!op) {
continue;
}

if (op.getNumQubits() < 2) {
continue;
}

seen.try_emplace(op, &it);
if (!ready.contains(op)) {
ready.add(op, &it, op.getNumQubits());
continue;
}

if (auto opt = ready.visit(op, &it)) {
for (WireIterator* wire : *opt) {
std::ranges::advance(*wire, 1);
}
}
}

seen.clear(); // Prepare for next iteration.
ready.clear(); // Prepare for next iteration.
}
}
};
Expand Down
11 changes: 11 additions & 0 deletions mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class MappingPassTest : public testing::Test,

bool executable = true;
std::ignore = moduleOp->walk([&](qc::UnitaryOpInterface op) {
if (isa<qc::BarrierOp>(op)) {
return WalkResult::advance();
}
if (op.getNumQubits() > 1) {
assert(op.getNumQubits() == 2 &&
"Expected only 2-qubit gates after decomposition");
Expand Down Expand Up @@ -178,6 +181,14 @@ TEST_P(MappingPassTest, Sabre) {

builder.cx(q3, q0);

builder.barrier({q0, q1, q2, q3, q4, q5});
builder.measure(q0);
builder.measure(q1);
builder.measure(q2);
builder.measure(q3);
builder.measure(q4);
builder.measure(q5);

builder.dealloc(q0);
builder.dealloc(q1);
builder.dealloc(q2);
Expand Down
Loading