Skip to content

Commit 398ccd2

Browse files
li-mingbaoLi-ming Baodenialhaagpre-commit-ci[bot]burgholzer
authored
✨ Add SCF conversions between QC and QCO dialects (#1638)
## Description This PR adds support for the conversion of the `scf` operations `scf.while`, `scf.for`, `scf.if` between the `QC` and the `QCO` dialect. This allows the conversion of programs with nonlinear controlflow. This PR is the revamped version of the #1396 PR to integrate the newest changes. Changes compared to the PR and new features: - Conversions support registers and qubits inside the `scf` operations. - The func conversion is not added since this also requires a `qtensor.cast` operation first. - `scf.if` is converted into `qco.if` - `qco.if` also supports tensors of qubits now as input types. - `qco.yield` also supports tensors of qubits now as input types. ## Checklist: <!--- This checklist serves as a reminder of a couple of things that ensure your pull request will be merged swiftly. --> - [ ] The pull request only contains commits that are focused and relevant to this change. - [ ] I have added appropriate tests that cover the new/changed functionality. - [ ] 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). - [ ] The changes follow the project's style guidelines and introduce no new warnings. - [ ] The changes are fully tested and pass the CI checks. - [ ] 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: li-mingbao <74404929+li-mingbao@users.noreply.github.com> Signed-off-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Signed-off-by: Lukas Burgholzer <burgholzer@me.com> Co-authored-by: Li-ming Bao <ge93vof@tum.de> Co-authored-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Lukas Burgholzer <burgholzer@me.com>
1 parent ea5fd11 commit 398ccd2

21 files changed

Lines changed: 2205 additions & 312 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], [#1637], [#1676]) ([**@denialhaag**], [**@burgholzer**])
1616
- ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588], [#1600], [#1664]) ([**@MatthiasReumann**], [**@burgholzer**])
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], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635], [#1673], [#1675])
18+
([#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])
1919
([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**])
2020

2121
### Changed
@@ -368,6 +368,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool
368368
[#1664]: https://github.com/munich-quantum-toolkit/core/pull/1664
369369
[#1662]: https://github.com/munich-quantum-toolkit/core/pull/1662
370370
[#1652]: https://github.com/munich-quantum-toolkit/core/pull/1652
371+
[#1638]: https://github.com/munich-quantum-toolkit/core/pull/1638
371372
[#1637]: https://github.com/munich-quantum-toolkit/core/pull/1637
372373
[#1635]: https://github.com/munich-quantum-toolkit/core/pull/1635
373374
[#1627]: https://github.com/munich-quantum-toolkit/core/pull/1627

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

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,28 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
171171
*/
172172
QubitRegister allocQubitRegister(int64_t size);
173173

174+
/**
175+
* @brief Explicitly loads a qubit from a memref
176+
*
177+
* @details Explicitly loads a qubit from a memref at the given index. This
178+
* builder should only be called in a nested region inside the main function.
179+
* The same index cannot be used to load a value multiple times in the same
180+
* nested region.
181+
*
182+
* @param memref Source memref
183+
* @param index The index from where the qubit is loaded
184+
* @return The loaded qubit
185+
*
186+
* @par Example:
187+
* ```c++
188+
* auto q0 = builder.memrefLoad(memref, index);
189+
* ```
190+
* ```mlir
191+
* %q0 = memref.load %memref[%index] : memref<3x!qc.qubit>
192+
* ```
193+
*/
194+
Value memrefLoad(Value memref, Value index);
195+
174196
/**
175197
* @brief A small structure representing a single classical bit within a
176198
* classical register.
@@ -924,6 +946,112 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
924946
*/
925947
QCProgramBuilder& dealloc(Value qubit);
926948

949+
//===--------------------------------------------------------------------===//
950+
// SCF operations
951+
//===--------------------------------------------------------------------===//
952+
953+
/**
954+
* @brief Construct an scf.for operation
955+
*
956+
* @param lowerbound Lower bound of the loop
957+
* @param upperbound Upper bound of the loop
958+
* @param step Step size of the loop
959+
* @param body Function that builds the body of the for operation
960+
* @return Reference to this builder for method chaining
961+
*
962+
* @par Example:
963+
* ```c++
964+
* builder.scfFor(lb, ub, step, [&](Value iv) {
965+
* auto q0 = builder.memrefLoad(memref, iv);
966+
* builder.h(q0);
967+
* });
968+
* ```
969+
* ```mlir
970+
* scf.for %iv = %lb to %ub step %step {
971+
* %q0 = memref.load %memref[%iv] : memref<3x!qc.qubit>
972+
* qc.h %q0 : !qc.qubit
973+
* }
974+
* ```
975+
*/
976+
QCProgramBuilder& scfFor(const std::variant<int64_t, Value>& lowerbound,
977+
const std::variant<int64_t, Value>& upperbound,
978+
const std::variant<int64_t, Value>& step,
979+
const function_ref<void(Value)>& body);
980+
981+
/**
982+
* @brief Construct an scf.while operation
983+
*
984+
* @param beforeBody Function that builds the before body of the while
985+
* operation
986+
* @param afterBody Function that builds the after body of the while operation
987+
* @return Reference to this builder for method chaining
988+
*
989+
* @par Example:
990+
* ```c++
991+
* builder.scfWhile([&] {
992+
* auto res = builder.measure(q0);
993+
* builder.scfCondition(res);
994+
* }, [&] {
995+
* builder.h(q0);
996+
* });
997+
* ```
998+
* ```mlir
999+
* scf.while : () -> () {
1000+
* %res = qc.measure %q0 : !qc.qubit -> i1
1001+
* scf.condition(%res)
1002+
* } do {
1003+
* qc.h %q0 : !qc.qubit
1004+
* scf.yield
1005+
* }
1006+
* ```
1007+
*/
1008+
QCProgramBuilder& scfWhile(const function_ref<void()>& beforeBody,
1009+
const function_ref<void()>& afterBody);
1010+
1011+
/**
1012+
* @brief Construct an scf.if operation
1013+
*
1014+
* @param condition Condition for the if operation
1015+
* @param thenBody Function that builds the then body of the if operation
1016+
* @param elseBody Function that builds the else body of the if operation
1017+
* @return Reference to this builder for method chaining
1018+
*
1019+
* @par Example:
1020+
* ```c++
1021+
* builder.scfIf(condition, [&] {
1022+
* builder.x(q0);
1023+
* }, [&] {
1024+
* builder.z(q0);
1025+
* });
1026+
* ```
1027+
* ```mlir
1028+
* scf.if %condition {
1029+
* qc.x %q0 : !qc.qubit
1030+
* } else {
1031+
* qc.z %q0 : !qc.qubit
1032+
* }
1033+
* ```
1034+
*/
1035+
QCProgramBuilder& scfIf(const std::variant<bool, Value>& condition,
1036+
const function_ref<void()>& thenBody,
1037+
const function_ref<void()>& elseBody = nullptr);
1038+
1039+
/**
1040+
* @brief Construct an scf.condition operation
1041+
*
1042+
* @param condition Condition for the condition operation
1043+
* @return Reference to this builder for method chaining
1044+
*
1045+
* @par Example:
1046+
* ```c++
1047+
* builder.scfCondition(condition);
1048+
* ```
1049+
* ```mlir
1050+
* scf.condition(%condition)
1051+
* ```
1052+
*/
1053+
QCProgramBuilder& scfCondition(Value condition);
1054+
9271055
//===--------------------------------------------------------------------===//
9281056
// Finalization
9291057
//===--------------------------------------------------------------------===//
@@ -966,6 +1094,12 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
9661094
/// Track allocated MemRefs for automatic deallocation
9671095
DenseSet<Value> allocatedMemrefs;
9681096

1097+
/// Per-region map of memrefs and their loaded indices
1098+
DenseMap<Region*, DenseMap<Value, DenseSet<Value>>> loadedQubits;
1099+
1100+
/// Stack of the nested regions where the insertion point of the builder is
1101+
SmallVector<Region*> regionStack;
1102+
9691103
/// Check if the builder has been finalized
9701104
void checkFinalized() const;
9711105

0 commit comments

Comments
 (0)