Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6b714ec
Add Graph class
MatthiasReumann May 27, 2026
6431cfd
Update graph data structure
MatthiasReumann May 28, 2026
63f2a58
Merge branch 'main' into enh/sc-map-scf-for-loops
MatthiasReumann May 28, 2026
a2982d1
Add GHZUnrolled test
MatthiasReumann May 28, 2026
2d2438d
Add findCycle method
MatthiasReumann May 29, 2026
f1261ce
Rename Algorithms.{h,cpp} to Graph.{h, cpp}
MatthiasReumann May 29, 2026
a539116
Add GroverLike unit test
MatthiasReumann May 29, 2026
adf567a
Merge branch 'main' into enh/sc-map-scf-for-loops
MatthiasReumann May 29, 2026
6c55512
Add scf.for and qco.if support to wireiterator
MatthiasReumann May 29, 2026
db09903
Fix recursive wire iterator behavior
MatthiasReumann May 29, 2026
aff7da2
WiP: recursive for-loop mapping
MatthiasReumann Jun 1, 2026
20243a5
Update walkProgram driver
MatthiasReumann Jun 1, 2026
5547a1c
WiP: Extend for loop iter_args
MatthiasReumann Jun 3, 2026
5061016
Implement working prototype
MatthiasReumann Jun 19, 2026
4d01401
Minor cleanup
MatthiasReumann Jun 19, 2026
f0931f3
Extract layout to seperate module
MatthiasReumann Jun 19, 2026
cc4b43c
Extract layout to seperate module
MatthiasReumann Jun 19, 2026
aabd471
Merge branch 'enh/sc-map-scf-for-loops' of https://github.com/munich-…
MatthiasReumann Jun 19, 2026
9c2bf0c
Merge branch 'enh/sc-map-scf-for-loops' of https://github.com/munich-…
MatthiasReumann Jun 19, 2026
c3ef5f1
Merge branch 'enh/sc-map-scf-for-loops' of https://github.com/munich-…
MatthiasReumann Jun 19, 2026
fd818ff
Update isExecutable to handle nested regions
MatthiasReumann Jun 19, 2026
cbab3f8
Cleanup
MatthiasReumann Jun 22, 2026
a7bd6f1
Add parallelLoop unit test
MatthiasReumann Jun 23, 2026
5bb508b
Minor cleanup
MatthiasReumann Jun 23, 2026
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
6 changes: 3 additions & 3 deletions mlir/include/mlir/Dialect/QCO/Transforms/Mapping/Mapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#pragma once

#include "mlir/Dialect/QCO/Transforms/Passes.h"
#include "mlir/Dialect/QCO/Utils/Algorithms.h"

#include <llvm/Support/LogicalResult.h>
#include <mlir/IR/Region.h>
Expand All @@ -25,7 +24,8 @@ namespace mlir::qco {
* @brief Create a mapping pass instance with the given target architecture.
* @returns a pass object.
*/
std::unique_ptr<Pass> createMappingPass(size_t nqubits, const Edges& coupling,
MappingPassOptions options);
std::unique_ptr<Pass>
createMappingPass(const llvm::DenseSet<std::pair<size_t, size_t>>&,
MappingPassOptions);

} // namespace mlir::qco
38 changes: 0 additions & 38 deletions mlir/include/mlir/Dialect/QCO/Utils/Algorithms.h

This file was deleted.

135 changes: 61 additions & 74 deletions mlir/include/mlir/Dialect/QCO/Utils/Drivers.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@

#pragma once

#include "mlir/Dialect/QCO/IR/QCODialect.h"
#include "mlir/Dialect/QCO/IR/QCOInterfaces.h"
#include "mlir/Dialect/QCO/IR/QCOOps.h"
#include "mlir/Dialect/QCO/Utils/Qubits.h"
#include "mlir/Dialect/QCO/Utils/WireIterator.h"
#include "mlir/Dialect/QTensor/IR/QTensorOps.h"

#include <llvm/ADT/TypeSwitch.h>
#include <llvm/Support/ErrorHandling.h>
#include <mlir/Dialect/SCF/IR/SCF.h>
#include <mlir/IR/Region.h>
#include <mlir/IR/Value.h>
#include <mlir/IR/Visitors.h>
#include <mlir/Support/LLVM.h>

#include <cassert>
#include <cstddef>
#include <functional>
#include <iterator>
Expand All @@ -31,70 +32,22 @@

namespace mlir::qco {

using WalkProgramFn = function_ref<WalkResult(Operation*, Qubits&)>;

/**
* @brief Perform top-down non-recursive walk of all operations within a
* region of a quantum program and apply a callback function.
* @details The signature of the callback function is:
*
* (Operation*, Qubits& q) -> WalkResult
*
* where the Qubits object tracks the front of qubit SSA values.
* Depending on the template parameter, the callback is executed before or after
* updating the Qubits state.
* @param region The targeted region.
* @param fn The callback function.
* @returns success(), if all operations have been visited.
*/
template <WalkOrder Order = WalkOrder::PreOrder>
LogicalResult walkProgram(Region& region, const WalkProgramFn& fn) {
Qubits qubits;
for (Operation& curr : region.getOps()) {
if constexpr (Order == WalkOrder::PreOrder) {
if (fn(&curr, qubits).wasInterrupted()) {
return failure();
}
}

TypeSwitch<Operation*>(&curr)
.template Case<StaticOp>(
[&](StaticOp op) { qubits.add(op.getQubit(), op.getIndex()); })
.template Case<AllocOp>([&](AllocOp op) { qubits.add(op.getResult()); })
.template Case<UnitaryOpInterface>([&](UnitaryOpInterface& op) {
for (const auto& [prevV, nextV] :
llvm::zip(op.getInputQubits(), op.getOutputQubits())) {
const auto prevQ = cast<TypedValue<QubitType>>(prevV);
const auto nextQ = cast<TypedValue<QubitType>>(nextV);
qubits.remap(prevQ, nextQ);
}
})
.template Case<ResetOp>([&](ResetOp op) {
qubits.remap(op.getQubitIn(), op.getQubitOut());
})
.template Case<MeasureOp>([&](MeasureOp op) {
qubits.remap(op.getQubitIn(), op.getQubitOut());
})
.template Case<SinkOp>(
[&](SinkOp op) { qubits.remove(op.getQubit()); });

if constexpr (Order == WalkOrder::PostOrder) {
if (fn(&curr, qubits).wasInterrupted()) {
return failure();
}
}
}

return success();
}

using ReleasedOps = SmallVector<UnitaryOpInterface, 8>;
using PendingWiresMap =
DenseMap<UnitaryOpInterface, SmallVector<std::size_t, 2>>;
using ReleasedOps = SmallVector<Operation*, 8>;
using PendingWiresMap = DenseMap<Operation*, SmallVector<size_t>>;

struct IsReady {
bool operator()(PendingWiresMap::value_type& kv) const {
return kv.second.size() == kv.first.getNumQubits();
const auto npending = kv.second.size();
return TypeSwitch<Operation*, bool>(kv.first)
.Case<UnitaryOpInterface>(
[&](auto& op) { return op.getNumQubits() == npending; })
.template Case<scf::ForOp>(
[&](auto& op) { return op.getInits().size() == npending; })
.Default([&](Operation* op) {
const auto name = op->getName().getStringRef();
reportFatalInternalError("unknown pending op: " + name);
return false;
});
}
};

Expand Down Expand Up @@ -138,20 +91,28 @@ LogicalResult walkProgramGraph(MutableArrayRef<WireIterator> wires,
PendingWiresMap pending;
pending.reserve(wires.size());

SmallVector<std::size_t> curr(wires.size());
SmallVector<size_t> curr(wires.size());
std::iota(curr.begin(), curr.end(), 0UL);

SmallVector<std::size_t> next;
SmallVector<size_t> next;
next.reserve(wires.size());

while (!curr.empty()) {
for (std::size_t i : curr) {
for (size_t i : curr) {
auto& it = wires[i];
while (Traits::isActive(it)) {

if (it.qubit() != nullptr && isa<BlockArgument>(it.qubit())) {
std::ranges::advance(it, Traits::stride());
continue;
}

assert(it.operation() != nullptr);

const auto res =
TypeSwitch<Operation*, WalkResult>(it.operation())
.template Case<UnitaryOpInterface>([&](UnitaryOpInterface& op) {
// If there are fewer wires than the qubit requires inputs,
.template Case<UnitaryOpInterface>([&](auto& op) {
// If there are fewer wires than the unitary requires inputs,
// it's impossible to release the operation. Hence, fail.
if (op.getNumQubits() > wires.size()) {
return WalkResult::interrupt();
Expand All @@ -173,19 +134,45 @@ LogicalResult walkProgramGraph(MutableArrayRef<WireIterator> wires,

indices.emplace_back(i);

return WalkResult::skip(); // Stop at multi-qubit gate.
return WalkResult::skip(); // Stop at multi-qubit unitary.
})
.template Case<scf::ForOp>([&](scf::ForOp& op) {
// If there are fewer wires than the loop requires inputs,
// it's impossible to release the operation. Hence, fail.
if (op.getInits().size() > wires.size()) {
return WalkResult::interrupt();
}

if (op.getInits().size() == 1) {
std::ranges::advance(it, Traits::stride());
return WalkResult::advance();
}

// Insert the loop to the pending map.
// The caller decides if this op should be released.
const auto [mapIt, inserted] = pending.try_emplace(op);
auto& indices = mapIt->second;

if (inserted) {
indices.reserve(op.getInits().size());
}

indices.emplace_back(i);

return WalkResult::skip(); // Stop at multi-qubit loop.
})
// AllocOp, StaticOp, and qtensor::ExtractOp are only reachable
// on the forward path; backward isActive() halts before
// reaching them (decrementing at a source op is a no-op).
.template Case<AllocOp, StaticOp, qtensor::ExtractOp, ResetOp,
MeasureOp, SinkOp, qtensor::InsertOp>([&](auto) {
.template Case<AllocOp, StaticOp, ResetOp, MeasureOp, SinkOp,
YieldOp, scf::YieldOp, qtensor::ExtractOp,
qtensor::InsertOp>([&](auto) {
std::ranges::advance(it, Traits::stride());
return WalkResult::advance();
})
.Default([&](Operation* op) -> WalkResult {
const auto name = op->getName().getStringRef();
report_fatal_error("unknown op encountered: " + name);
reportFatalInternalError("unknown op encountered: " + name);
});

if (res.wasSkipped()) {
Expand All @@ -212,11 +199,11 @@ LogicalResult walkProgramGraph(MutableArrayRef<WireIterator> wires,
}
}

for (const UnitaryOpInterface& op : released) {
for (Operation* op : released) {
const auto mapIt = pending.find(op);
assert(mapIt != pending.end());

for (std::size_t i : mapIt->second) {
for (size_t i : mapIt->second) {
std::ranges::advance(wires[i], Traits::stride());
next.emplace_back(i);
}
Expand Down
Loading