Skip to content

Commit 17d8c3c

Browse files
simon1hofmannburgholzerpre-commit-ci[bot]
authored
✨ Improve Static Qubit Handling (#1569)
## Description Based on #1585. ## Checklist <!--- This checklist serves as a reminder of a couple of things that ensure your pull request will be merged swiftly. --> - [x] The pull request only contains commits that are focused and relevant to this change. - [x] I have added appropriate tests that cover the new/changed functionality. - [x] I have updated the documentation to reflect these changes. - [ ] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [ ] I have added migration instructions to the upgrade guide (if needed). - [x] The changes follow the project's style guidelines and introduce no new warnings. - [x] The changes are fully tested and pass the CI checks. - [x] I have reviewed my own code changes. **If PR contains AI-assisted content:** - [ ] I have disclosed the use of AI tools in the PR description as per our [AI Usage Guidelines](https://github.com/munich-quantum-toolkit/core/blob/main/docs/ai_usage.md). - [ ] AI-assisted commits include an `Assisted-by: [Model Name] via [Tool Name]` footer. - [ ] I confirm that I have personally reviewed and understood all AI-generated content, and accept full responsibility for it. --------- Signed-off-by: simon1hofmann <119581649+simon1hofmann@users.noreply.github.com> Signed-off-by: burgholzer <burgholzer@me.com> Co-authored-by: Lukas Burgholzer <burgholzer@me.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 8cec5ca commit 17d8c3c

33 files changed

Lines changed: 816 additions & 499 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel
1515
- ✨ Add conversions between Jeff and QCO ([#1479], [#1548], [#1565]) ([**@denialhaag**])
1616
- ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588]) ([**@MatthiasReumann**])
1717
- ✨ Add initial infrastructure for new QC and QCO MLIR dialects
18-
([#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])
18+
([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1569], [#1570], [#1572], [#1573])
1919
([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**])
2020

2121
### Changed
@@ -344,6 +344,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool
344344
[#1572]: https://github.com/munich-quantum-toolkit/core/pull/1572
345345
[#1571]: https://github.com/munich-quantum-toolkit/core/pull/1571
346346
[#1570]: https://github.com/munich-quantum-toolkit/core/pull/1570
347+
[#1569]: https://github.com/munich-quantum-toolkit/core/pull/1569
347348
[#1568]: https://github.com/munich-quantum-toolkit/core/pull/1568
348349
[#1565]: https://github.com/munich-quantum-toolkit/core/pull/1565
349350
[#1564]: https://github.com/munich-quantum-toolkit/core/pull/1564

mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
110110

111111
/**
112112
* @brief Get a static qubit by index
113-
* @param index The qubit index (must be non-negative)
113+
* @param index The qubit index
114114
* @return A qubit reference
115115
*
116116
* @par Example:
@@ -121,7 +121,7 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
121121
* %q0 = qc.static 0 : !qc.qubit
122122
* ```
123123
*/
124-
Value staticQubit(int64_t index);
124+
Value staticQubit(uint64_t index);
125125

126126
/**
127127
* @brief Allocate a qubit register

mlir/include/mlir/Dialect/QC/IR/QCDialect.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ template <size_t T, size_t P> class TargetAndParameterArityTrait {
5252
static size_t getNumQubits() { return T; }
5353
static size_t getNumTargets() { return T; }
5454
static size_t getNumControls() { return 0; }
55+
static ValueRange getControls() { return {}; }
5556

5657
Value getQubit(size_t i) {
5758
if constexpr (T == 0) {
@@ -71,6 +72,9 @@ template <size_t T, size_t P> class TargetAndParameterArityTrait {
7172
}
7273
return this->getOperation()->getOperand(i);
7374
}
75+
ValueRange getTargets() {
76+
return this->getOperation()->getOperands().slice(0, T);
77+
}
7478

7579
static Value getControl([[maybe_unused]] size_t i) {
7680
llvm::reportFatalUsageError("Operation does not have controls");
@@ -84,6 +88,10 @@ template <size_t T, size_t P> class TargetAndParameterArityTrait {
8488
}
8589
return this->getOperation()->getOperand(T + i);
8690
}
91+
92+
ValueRange getParameters() {
93+
return this->getOperation()->getOperands().slice(T, P);
94+
}
8795
};
8896
};
8997

mlir/include/mlir/Dialect/QC/IR/QCInterfaces.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,18 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> {
4444
(ins "size_t":$i)>,
4545
InterfaceMethod<"Returns the i-th control qubit.", "Value", "getControl",
4646
(ins "size_t":$i)>,
47+
InterfaceMethod<"Returns a range of all target qubits.", "ValueRange",
48+
"getTargets", (ins)>,
49+
InterfaceMethod<"Returns a range of all control qubits.", "ValueRange",
50+
"getControls", (ins)>,
4751

4852
// Parameter handling
4953
InterfaceMethod<"Returns the number of parameters.", "size_t",
5054
"getNumParams", (ins)>,
5155
InterfaceMethod<"Returns the i-th parameter.", "Value", "getParameter",
5256
(ins "size_t":$i)>,
57+
InterfaceMethod<"Returns a range of all parameters.", "ValueRange",
58+
"getParameters", (ins)>,
5359

5460
// Convenience methods
5561
InterfaceMethod<"Returns true if the operation has any control qubits, "

mlir/include/mlir/Dialect/QC/IR/QCOps.td

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ def StaticOp : QCOp<"static", [Pure]> {
110110
let arguments = (ins ConfinedAttr<I64Attr, [IntNonNegative]>:$index);
111111
let results = (outs QubitType:$qubit);
112112
let assemblyFormat = "$index attr-dict `:` type($qubit)";
113+
114+
let builders = [OpBuilder<(ins "uint64_t":$index), [{
115+
build($_builder, $_state, QubitType::get($_builder.getContext()), index);
116+
}]>];
113117
}
114118

115119
//===----------------------------------------------------------------------===//
@@ -910,11 +914,14 @@ def BarrierOp : QCOp<"barrier", traits = [UnitaryOpInterface]> {
910914
size_t getNumQubits() { return getNumTargets(); }
911915
size_t getNumTargets() { return getQubits().size(); }
912916
static size_t getNumControls() { return 0; }
917+
static ValueRange getControls() { return {}; }
913918
Value getQubit(size_t i) { return getTarget(i); }
914919
Value getTarget(size_t i);
920+
ValueRange getTargets() { return getQubits(); }
915921
static Value getControl(size_t i) { llvm::reportFatalUsageError("BarrierOp cannot be controlled"); }
916922
static size_t getNumParams() { return 0; }
917923
static Value getParameter(size_t i) { llvm::reportFatalUsageError("BarrierOp does not have parameters"); }
924+
static ValueRange getParameters() { return {}; }
918925
static StringRef getBaseSymbol() { return "barrier"; }
919926
}];
920927
}
@@ -969,9 +976,11 @@ def CtrlOp
969976
size_t getNumControls() { return getControls().size(); }
970977
Value getQubit(size_t i);
971978
Value getTarget(size_t i) { return getBodyUnitary().getTarget(i); }
979+
ValueRange getTargets() { return getBodyUnitary().getTargets(); }
972980
Value getControl(size_t i);
973981
size_t getNumParams() { return getBodyUnitary().getNumParams(); }
974982
Value getParameter(size_t i) { return getBodyUnitary().getParameter(i); }
983+
ValueRange getParameters() { return getBodyUnitary().getParameters(); }
975984
static StringRef getBaseSymbol() { return "ctrl"; }
976985
}];
977986

@@ -1005,13 +1014,16 @@ def InvOp : QCOp<"inv",
10051014
let extraClassDeclaration = [{
10061015
[[nodiscard]] UnitaryOpInterface getBodyUnitary();
10071016
size_t getNumQubits() { return getBodyUnitary().getNumQubits(); }
1008-
size_t getNumTargets() { return getNumQubits(); }
1009-
static size_t getNumControls() { return 0; }
1017+
size_t getNumTargets() { return getBodyUnitary().getNumTargets(); }
1018+
size_t getNumControls() { return getBodyUnitary().getNumControls(); }
10101019
Value getQubit(size_t i) { return getBodyUnitary().getQubit(i); }
1011-
Value getTarget(size_t i) { return getQubit(i); }
1012-
static Value getControl(size_t i) { llvm::reportFatalUsageError("Operation does not have controls"); }
1020+
Value getTarget(size_t i) { return getBodyUnitary().getTarget(i); }
1021+
ValueRange getTargets() { return getBodyUnitary().getTargets(); }
1022+
Value getControl(size_t i) { return getBodyUnitary().getControl(i); }
1023+
ValueRange getControls() { return getBodyUnitary().getControls(); }
10131024
size_t getNumParams() { return getBodyUnitary().getNumParams(); }
10141025
Value getParameter(size_t i) { return getBodyUnitary().getParameter(i); }
1026+
ValueRange getParameters() { return getBodyUnitary().getParameters(); }
10151027
static StringRef getBaseSymbol() { return "inv"; }
10161028
}];
10171029

mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder {
118118

119119
/**
120120
* @brief Get a static qubit by index
121-
* @param index The qubit index (must be non-negative)
121+
* @param index The qubit index
122122
* @return A tracked, valid qubit SSA value
123123
*
124124
* @par Example:
@@ -129,7 +129,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder {
129129
* %q0 = qco.static 0 : !qco.qubit
130130
* ```
131131
*/
132-
Value staticQubit(int64_t index);
132+
Value staticQubit(uint64_t index);
133133

134134
/**
135135
* @brief Allocate a qubit register
@@ -1228,24 +1228,24 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder {
12281228
//===--------------------------------------------------------------------===//
12291229

12301230
/**
1231-
* @brief Explicitly deallocate a qubit
1231+
* @brief Consume a qubit value (end of lifetime)
12321232
*
12331233
* @details
12341234
* Validates and removes the qubit from tracking. Optional; `finalize()`
1235-
* automatically deallocates all remaining qubits.
1235+
* automatically sinks all remaining qubits.
12361236
*
1237-
* @param qubit Qubit to deallocate (must be valid/unconsumed)
1237+
* @param qubit Qubit to sink (must be valid/unconsumed)
12381238
* @return Reference to this builder for method chaining
12391239
*
12401240
* @par Example:
12411241
* ```c++
1242-
* builder.dealloc(q);
1242+
* builder.sink(q);
12431243
* ```
12441244
* ```mlir
1245-
* qco.dealloc %q : !qco.qubit
1245+
* qco.sink %q : !qco.qubit
12461246
* ```
12471247
*/
1248-
QCOProgramBuilder& dealloc(Value qubit);
1248+
QCOProgramBuilder& sink(Value qubit);
12491249

12501250
//===--------------------------------------------------------------------===//
12511251
// SCF operations

mlir/include/mlir/Dialect/QCO/IR/QCOOps.td

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,20 @@ def AllocOp : QCOOp<"alloc", [MemoryEffects<[MemAlloc]>]> {
7777
let hasVerifier = 1;
7878
}
7979

80-
def DeallocOp : QCOOp<"dealloc", [MemoryEffects<[MemFree]>]> {
81-
let summary = "Deallocate a qubit";
80+
def SinkOp : QCOOp<"sink", [MemoryEffects<[MemFree]>]> {
81+
let summary = "Consume a qubit value (end of lifetime)";
8282
let description = [{
83-
Deallocates a qubit, releasing its resources.
83+
Consumes a qubit SSA value and marks the end of its lifetime.
84+
85+
This operation is the canonical "sink" for QCO's linear/value semantics:
86+
every qubit value must be consumed exactly once on all paths.
87+
88+
When converting back to QC (reference semantics), sinks corresponding to
89+
static qubits may be erased.
8490

8591
Example:
8692
```mlir
87-
qco.dealloc %q : !qco.qubit
93+
qco.sink %q : !qco.qubit
8894
```
8995
}];
9096

mlir/include/mlir/Dialect/QCO/Utils/Drivers.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,8 @@ template <typename Fn> void walkUnit(Region& region, Fn&& fn) {
104104
.template Case<MeasureOp>([&](MeasureOp op) {
105105
qubits.remap(op.getQubitIn(), op.getQubitOut());
106106
})
107-
.template Case<DeallocOp>(
108-
[&](DeallocOp op) { qubits.remove(op.getQubit()); });
107+
.template Case<SinkOp>(
108+
[&](SinkOp op) { qubits.remove(op.getQubit()); });
109109
}
110110
}
111111
} // namespace mlir::qco

mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ struct ConvertJeffQubitAllocOpToQCO final
406406
};
407407

408408
/**
409-
* @brief Converts jeff.qubit_free to qco.reset + qco.dealloc
409+
* @brief Converts jeff.qubit_free to qco.reset + qco.sink
410410
*
411411
* @par Example:
412412
* ```mlir
@@ -415,7 +415,7 @@ struct ConvertJeffQubitAllocOpToQCO final
415415
* is converted to
416416
* ```mlir
417417
* %q_out = qco.reset %q_in : !qco.qubit
418-
* qco.dealloc %q_out : !qco.qubit
418+
* qco.sink %q_out : !qco.qubit
419419
* ```
420420
*/
421421
struct ConvertJeffQubitFreeOpToQCO final
@@ -427,21 +427,21 @@ struct ConvertJeffQubitFreeOpToQCO final
427427
ConversionPatternRewriter& rewriter) const override {
428428
auto resetOp =
429429
qco::ResetOp::create(rewriter, op.getLoc(), adaptor.getInQubit());
430-
rewriter.replaceOpWithNewOp<qco::DeallocOp>(op, resetOp.getQubitOut());
430+
rewriter.replaceOpWithNewOp<qco::SinkOp>(op, resetOp.getQubitOut());
431431
return success();
432432
}
433433
};
434434

435435
/**
436-
* @brief Converts jeff.qubit_free_zero to qco.dealloc
436+
* @brief Converts jeff.qubit_free_zero to qco.sink
437437
*
438438
* @par Example:
439439
* ```mlir
440440
* jeff.qubit_free_zero %q : !jeff.qubit
441441
* ```
442442
* is converted to
443443
* ```mlir
444-
* qco.dealloc %q : !qco.qubit
444+
* qco.sink %q : !qco.qubit
445445
* ```
446446
*/
447447
struct ConvertJeffQubitFreeZeroOpToQCO final
@@ -451,13 +451,13 @@ struct ConvertJeffQubitFreeZeroOpToQCO final
451451
LogicalResult
452452
matchAndRewrite(jeff::QubitFreeZeroOp op, OpAdaptor adaptor,
453453
ConversionPatternRewriter& rewriter) const override {
454-
rewriter.replaceOpWithNewOp<qco::DeallocOp>(op, adaptor.getInQubit());
454+
rewriter.replaceOpWithNewOp<qco::SinkOp>(op, adaptor.getInQubit());
455455
return success();
456456
}
457457
};
458458

459459
/**
460-
* @brief Converts jeff.qubit_measure to qco.measure + qco.dealloc
460+
* @brief Converts jeff.qubit_measure to qco.measure + qco.sink
461461
*
462462
* @par Example:
463463
* ```mlir
@@ -466,7 +466,7 @@ struct ConvertJeffQubitFreeZeroOpToQCO final
466466
* is converted to
467467
* ```mlir
468468
* %q_out, %result = qco.measure %q_in : !qco.qubit
469-
* qco.dealloc %q_out : !qco.qubit
469+
* qco.sink %q_out : !qco.qubit
470470
* ```
471471
*/
472472
struct ConvertJeffQubitMeasureOpToQCO final
@@ -479,7 +479,7 @@ struct ConvertJeffQubitMeasureOpToQCO final
479479
auto loc = op.getLoc();
480480
auto measureOp =
481481
qco::MeasureOp::create(rewriter, loc, adaptor.getInQubit());
482-
qco::DeallocOp::create(rewriter, loc, measureOp.getQubitOut());
482+
qco::SinkOp::create(rewriter, loc, measureOp.getQubitOut());
483483
rewriter.replaceOp(op, measureOp.getResult());
484484
return success();
485485
}

mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -269,23 +269,22 @@ struct ConvertQCOAllocOpToJeff final
269269
};
270270

271271
/**
272-
* @brief Converts qco.dealloc to jeff.qubit_free_zero
272+
* @brief Converts qco.sink to jeff.qubit_free_zero
273273
*
274274
* @par Example:
275275
* ```mlir
276-
* qco.dealloc %q : !qco.qubit
276+
* qco.sink %q : !qco.qubit
277277
* ```
278278
* is converted to
279279
* ```mlir
280280
* jeff.qubit_free_zero %q : !jeff.qubit
281281
* ```
282282
*/
283-
struct ConvertQCODeallocOpToJeff final
284-
: StatefulOpConversionPattern<qco::DeallocOp> {
283+
struct ConvertQCOSinkOpToJeff final : StatefulOpConversionPattern<qco::SinkOp> {
285284
using StatefulOpConversionPattern::StatefulOpConversionPattern;
286285

287286
LogicalResult
288-
matchAndRewrite(qco::DeallocOp op, OpAdaptor adaptor,
287+
matchAndRewrite(qco::SinkOp op, OpAdaptor adaptor,
289288
ConversionPatternRewriter& rewriter) const override {
290289
rewriter.replaceOpWithNewOp<jeff::QubitFreeZeroOp>(op, adaptor.getQubit());
291290
return success();
@@ -1364,7 +1363,7 @@ struct QCOToJeff final : impl::QCOToJeffBase<QCOToJeff> {
13641363
// Register operation conversion patterns
13651364
jeff::populateNativeToJeffConversionPatterns(patterns);
13661365
patterns.add<
1367-
ConvertQCOAllocOpToJeff, ConvertQCODeallocOpToJeff,
1366+
ConvertQCOAllocOpToJeff, ConvertQCOSinkOpToJeff,
13681367
ConvertQCOMeasureOpToJeff, ConvertQCOResetOpToJeff,
13691368
ConvertQCOGPhaseOpToJeff,
13701369
ConvertQCOOneTargetZeroParameterToJeff<qco::IdOp, jeff::IOp, false>,

0 commit comments

Comments
 (0)