Skip to content

Commit 72f997e

Browse files
♻️ Relax condition on modifiers (#1751)
## Description This PR relaxes the condition that modifiers in QC and QCO must have a single unitary operation followed by a `yield` operation. Modifiers may now contain an arbitrary number of operations (unitary as well as, e.g., `arith.constant`s). Fixes #1678 Fixes #1727 ## Checklist - [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. - [x] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [x] ~~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. --------- Signed-off-by: burgholzer <burgholzer@me.com> Co-authored-by: burgholzer <burgholzer@me.com>
1 parent dc61cd3 commit 72f997e

40 files changed

Lines changed: 2120 additions & 1267 deletions

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel
1818
- ✨ Add conversions between `jeff` and QCO ([#1479], [#1548], [#1565], [#1637], [#1676], [#1706]) ([**@denialhaag**], [**@burgholzer**])
1919
- ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588], [#1600], [#1664], [#1709], [#1716], [#1748]) ([**@MatthiasReumann**], [**@burgholzer**])
2020
- ✨ Add initial infrastructure for new QC and QCO MLIR dialects
21-
([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635], [#1638], [#1673], [#1675], [#1700], [#1717], [#1728], [#1730], [#1749], [#1762], [#1765], [#1774])
21+
([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635], [#1638], [#1673], [#1675], [#1700], [#1717], [#1728], [#1730], [#1749], [#1751], [#1762], [#1765], [#1774])
2222
([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**])
2323

2424
### Changed
@@ -405,6 +405,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool
405405
[#1774]: https://github.com/munich-quantum-toolkit/core/pull/1774
406406
[#1765]: https://github.com/munich-quantum-toolkit/core/pull/1765
407407
[#1762]: https://github.com/munich-quantum-toolkit/core/pull/1762
408+
[#1751]: https://github.com/munich-quantum-toolkit/core/pull/1751
408409
[#1749]: https://github.com/munich-quantum-toolkit/core/pull/1749
409410
[#1748]: https://github.com/munich-quantum-toolkit/core/pull/1748
410411
[#1737]: https://github.com/munich-quantum-toolkit/core/pull/1737
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
3+
* Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
4+
* All rights reserved.
5+
*
6+
* SPDX-License-Identifier: MIT
7+
*
8+
* Licensed under the MIT License
9+
*/
10+
11+
#pragma once
12+
13+
#include <mlir/IR/Region.h>
14+
#include <mlir/Support/LLVM.h>
15+
#include <mlir/Support/LogicalResult.h>
16+
#include <mlir/Transforms/DialectConversion.h>
17+
18+
namespace mlir {
19+
20+
/**
21+
* @brief Inlines @p source into @p dest and converts the entry block signature.
22+
*
23+
* @details Moves all blocks of @p source to the end of @p dest and converts the
24+
* argument types of the resulting entry block using @p typeConverter. This is
25+
* the canonical way to migrate a region from one dialect to another during a
26+
* dialect conversion when the block arguments change type.
27+
*
28+
* @param source The region whose blocks are moved out.
29+
* @param dest The region the blocks are moved into.
30+
* @param rewriter The conversion rewriter driving the current pass.
31+
* @param typeConverter The type converter used to convert the entry block
32+
* signature.
33+
* @return Whether converting the entry block signature succeeded.
34+
*/
35+
inline LogicalResult moveRegion(Region& source, Region& dest,
36+
ConversionPatternRewriter& rewriter,
37+
const TypeConverter* typeConverter) {
38+
rewriter.inlineRegionBefore(source, dest, dest.end());
39+
auto* block = &dest.front();
40+
TypeConverter::SignatureConversion sc(block->getNumArguments());
41+
if (failed(
42+
typeConverter->convertSignatureArgs(block->getArgumentTypes(), sc))) {
43+
return failure();
44+
}
45+
rewriter.applySignatureConversion(block, sc);
46+
return success();
47+
}
48+
49+
} // namespace mlir

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

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -406,8 +406,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
406406
* builder.c##OP_NAME(q0, q1); \
407407
* ``` \
408408
* ```mlir \
409-
* qc.ctrl(%q0) { \
410-
* qc.OP_NAME %q1 : !qc.qubit \
409+
* qc.ctrl(%q0) targets(%a0 = %q1) { \
410+
* qc.OP_NAME %a0 : !qc.qubit \
411411
* } : !qc.qubit \
412412
* ``` \
413413
*/ \
@@ -424,8 +424,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
424424
* builder.mc##OP_NAME({q0, q1}, q2); \
425425
* ``` \
426426
* ```mlir \
427-
* qc.ctrl(%q0, %q1) { \
428-
* qc.OP_NAME %q2 : !qc.qubit \
427+
* qc.ctrl(%q0, %q1) targets(%a0 = %q2) { \
428+
* qc.OP_NAME %a0 : !qc.qubit \
429429
* } : !qc.qubit, !qc.qubit \
430430
* ``` \
431431
*/ \
@@ -478,8 +478,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
478478
* builder.c##OP_NAME(PARAM, q0, q1); \
479479
* ``` \
480480
* ```mlir \
481-
* qc.ctrl(%q0) { \
482-
* qc.OP_NAME(%PARAM) %q1 : !qc.qubit \
481+
* qc.ctrl(%q0) targets(%a0 = %q1) { \
482+
* qc.OP_NAME(%PARAM) %a0 : !qc.qubit \
483483
* } : !qc.qubit \
484484
* ``` \
485485
*/ \
@@ -498,8 +498,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
498498
* builder.mc##OP_NAME(PARAM, {q0, q1}, q2); \
499499
* ``` \
500500
* ```mlir \
501-
* qc.ctrl(%q0, %q1) { \
502-
* qc.OP_NAME(%PARAM) %q2 : !qc.qubit \
501+
* qc.ctrl(%q0, %q1) targets(%a0 = %q2) { \
502+
* qc.OP_NAME(%PARAM) %a0 : !qc.qubit \
503503
* } : !qc.qubit, !qc.qubit \
504504
* ``` \
505505
*/ \
@@ -549,8 +549,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
549549
* builder.c##OP_NAME(PARAM1, PARAM2, q0, q1); \
550550
* ``` \
551551
* ```mlir \
552-
* qc.ctrl(%q0) { \
553-
* qc.OP_NAME(%PARAM1, %PARAM2) %q1 : !qc.qubit \
552+
* qc.ctrl(%q0) (%a0 = %q1) { \
553+
* qc.OP_NAME(%PARAM1, %PARAM2) %a0 : !qc.qubit \
554554
* } : !qc.qubit \
555555
* ``` \
556556
*/ \
@@ -571,8 +571,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
571571
* builder.mc##OP_NAME(PARAM1, PARAM2, {q0, q1}, q2); \
572572
* ``` \
573573
* ```mlir \
574-
* qc.ctrl(%q0, %q1) { \
575-
* qc.OP_NAME(%PARAM1, %PARAM2) %q2 : !qc.qubit \
574+
* qc.ctrl(%q0, %q1) targets(%a0 = %q2) { \
575+
* qc.OP_NAME(%PARAM1, %PARAM2) %a0 : !qc.qubit \
576576
* } : !qc.qubit, !qc.qubit \
577577
* ``` \
578578
*/ \
@@ -625,8 +625,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
625625
* builder.c##OP_NAME(PARAM1, PARAM2, PARAM3, q0, q1); \
626626
* ``` \
627627
* ```mlir \
628-
* qc.ctrl(%q0) { \
629-
* qc.OP_NAME(%PARAM1, %PARAM2, %PARAM3) %q1 : !qc.qubit \
628+
* qc.ctrl(%q0) targets(%a0 = %q1) { \
629+
* qc.OP_NAME(%PARAM1, %PARAM2, %PARAM3) %a0 : !qc.qubit \
630630
* } : !qc.qubit \
631631
* ``` \
632632
*/ \
@@ -649,8 +649,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
649649
* builder.mc##OP_NAME(PARAM1, PARAM2, PARAM3, {q0, q1}, q2); \
650650
* ``` \
651651
* ```mlir \
652-
* qc.ctrl(%q0, %q1) { \
653-
* qc.OP_NAME(%PARAM1, %PARAM2, %PARAM3) %q2 : !qc.qubit \
652+
* qc.ctrl(%q0, %q1) targets(%a0 = %q2) { \
653+
* qc.OP_NAME(%PARAM1, %PARAM2, %PARAM3) %a0 : !qc.qubit \
654654
* } : !qc.qubit, !qc.qubit \
655655
* ``` \
656656
*/ \
@@ -695,8 +695,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
695695
* builder.c##OP_NAME(q0, q1, q2); \
696696
* ``` \
697697
* ```mlir \
698-
* qc.ctrl(%q0) { \
699-
* qc.OP_NAME %q1, %q2 : !qc.qubit, !qc.qubit \
698+
* qc.ctrl(%q0) targets(%a0 = %q1, %a1 = %q2) { \
699+
* qc.OP_NAME %a0, %a1 : !qc.qubit, !qc.qubit \
700700
* } : !qc.qubit \
701701
* ``` \
702702
*/ \
@@ -714,8 +714,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
714714
* builder.mc##OP_NAME({q0, q1}, q2, q3); \
715715
* ``` \
716716
* ```mlir \
717-
* qc.ctrl(%q0, %q1) { \
718-
* qc.OP_NAME %q2, %q3 : !qc.qubit, !qc.qubit \
717+
* qc.ctrl(%q0, %q1) targets(%a0 = %q2, %a1 = %q3) { \
718+
* qc.OP_NAME %a0, %a1 : !qc.qubit, !qc.qubit \
719719
* } : !qc.qubit, !qc.qubit \
720720
* ``` \
721721
*/ \
@@ -764,8 +764,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
764764
* builder.c##OP_NAME(PARAM, q0, q1, q2); \
765765
* ``` \
766766
* ```mlir \
767-
* qc.ctrl(%q0) { \
768-
* qc.OP_NAME(%PARAM) %q1, %q2 : !qc.qubit, !qc.qubit \
767+
* qc.ctrl(%q0) targets(%a0 = %q1, %a1 = %q2) { \
768+
* qc.OP_NAME(%PARAM) %a0, %a1 : !qc.qubit, !qc.qubit \
769769
* } : !qc.qubit \
770770
* ``` \
771771
*/ \
@@ -785,8 +785,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
785785
* builder.mc##OP_NAME(PARAM, {q0, q1}, q2, q3); \
786786
* ``` \
787787
* ```mlir \
788-
* qc.ctrl(%q0, %q1) { \
789-
* qc.OP_NAME(%PARAM) %q2, %q3 : !qc.qubit, !qc.qubit \
788+
* qc.ctrl(%q0, %q1) targets(%a0 = %q2, %a1 = %q3) { \
789+
* qc.OP_NAME(%PARAM) %a0, %a1 : !qc.qubit, !qc.qubit \
790790
* } : !qc.qubit, !qc.qubit \
791791
* ``` \
792792
*/ \
@@ -839,8 +839,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
839839
* builder.c##OP_NAME(PARAM1, PARAM2, q0, q1, q2); \
840840
* ``` \
841841
* ```mlir \
842-
* qc.ctrl(%q0) { \
843-
* qc.OP_NAME(%PARAM1, %PARAM2) %q1, %q2 : !qc.qubit, \
842+
* qc.ctrl(%q0) targets(%a0 = %q1, %a1 = %q2) { \
843+
* qc.OP_NAME(%PARAM1, %PARAM2) %a0, %a1 : !qc.qubit, \
844844
* !qc.qubit \
845845
* } : !qc.qubit \
846846
* ``` \
@@ -863,8 +863,8 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
863863
* builder.mc##OP_NAME(PARAM1, PARAM2, {q0, q1}, q2, q3); \
864864
* ``` \
865865
* ```mlir \
866-
* qc.ctrl(%q0, %q1) { \
867-
* qc.OP_NAME(%PARAM1, %PARAM2) %q2, %q3 : !qc.qubit, !qc.qubit \
866+
* qc.ctrl(%q0, %q1) targets(%a0 = %q2, %a1 = %q3) { \
867+
* qc.OP_NAME(%PARAM1, %PARAM2) %a0, %a1 : !qc.qubit, !qc.qubit \
868868
* } : !qc.qubit, !qc.qubit \
869869
* ``` \
870870
*/ \
@@ -909,15 +909,18 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
909909
*
910910
* @par Example:
911911
* ```c++
912-
* builder.ctrl(q0, [&] { builder.x(q1); });
912+
* builder.ctrl(q0, q1, [&](ValueRange targets) {
913+
* builder.x(targets[0]);
914+
* });
913915
* ```
914916
* ```mlir
915-
* qc.ctrl(%q0) {
916-
* qc.x %q1 : !qc.qubit
917+
* qc.ctrl(%q0) targets(%a0 = %q1) {
918+
* qc.x %a0 : !qc.qubit
917919
* } : !qc.qubit
918920
* ```
919921
*/
920-
QCProgramBuilder& ctrl(ValueRange controls, const function_ref<void()>& body);
922+
QCProgramBuilder& ctrl(ValueRange controls, ValueRange targets,
923+
const function_ref<void(ValueRange)>& body);
921924

922925
/**
923926
* @brief Apply an inverse (i.e., adjoint) operation.
@@ -928,15 +931,18 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
928931
*
929932
* @par Example:
930933
* ```c++
931-
* builder.inv([&] { builder.s(q0); });
934+
* builder.inv(q0, [&](ValueRange qubits) {
935+
* builder.h(qubits[0]);
936+
* });
932937
* ```
933938
* ```mlir
934939
* qc.inv {
935940
* qc.s %q0 : !qc.qubit
936941
* }
937942
* ```
938943
*/
939-
QCProgramBuilder& inv(const function_ref<void()>& body);
944+
QCProgramBuilder& inv(ValueRange qubits,
945+
const function_ref<void(ValueRange)>& body);
940946

941947
//===--------------------------------------------------------------------===//
942948
// Deallocation

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ template <size_t T, size_t P> class TargetAndParameterArityTrait {
5151
static size_t getNumQubits() { return T; }
5252
static size_t getNumTargets() { return T; }
5353
static size_t getNumControls() { return 0; }
54-
static ValueRange getControls() { return {}; }
54+
static OperandRange getControls() { return {nullptr, 0}; }
5555

5656
Value getQubit(size_t i) {
5757
if constexpr (T == 0) {
@@ -71,7 +71,8 @@ template <size_t T, size_t P> class TargetAndParameterArityTrait {
7171
}
7272
return this->getOperation()->getOperand(i);
7373
}
74-
ValueRange getTargets() {
74+
OperandRange getQubits() { return getTargets(); }
75+
OperandRange getTargets() {
7576
return this->getOperation()->getOperands().slice(0, T);
7677
}
7778

@@ -88,7 +89,7 @@ template <size_t T, size_t P> class TargetAndParameterArityTrait {
8889
return this->getOperation()->getOperand(T + i);
8990
}
9091

91-
ValueRange getParameters() {
92+
OperandRange getParameters() {
9293
return this->getOperation()->getOperands().slice(T, P);
9394
}
9495
};

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,20 @@ 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",
47+
InterfaceMethod<
48+
"Returns a range of all qubits (targets + controls combined).",
49+
"OperandRange", "getQubits", (ins)>,
50+
InterfaceMethod<"Returns a range of all target qubits.", "OperandRange",
4851
"getTargets", (ins)>,
49-
InterfaceMethod<"Returns a range of all control qubits.", "ValueRange",
52+
InterfaceMethod<"Returns a range of all control qubits.", "OperandRange",
5053
"getControls", (ins)>,
5154

5255
// Parameter handling
5356
InterfaceMethod<"Returns the number of parameters.", "size_t",
5457
"getNumParams", (ins)>,
5558
InterfaceMethod<"Returns the i-th parameter.", "Value", "getParameter",
5659
(ins "size_t":$i)>,
57-
InterfaceMethod<"Returns a range of all parameters.", "ValueRange",
60+
InterfaceMethod<"Returns a range of all parameters.", "OperandRange",
5861
"getParameters", (ins)>,
5962

6063
// Convenience methods

0 commit comments

Comments
 (0)