From 0dc1c2896b97083db1a9df93461b4c167fc050bd Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 8 Jan 2026 19:12:38 +0100 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=94=A5=20Remove=20legacy=20MLIR=20cod?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- mlir/CMakeLists.txt | 1 - mlir/include/mlir/Conversion/CMakeLists.txt | 4 - .../Conversion/MQTOptToMQTRef/CMakeLists.txt | 13 - .../MQTOptToMQTRef/MQTOptToMQTRef.h | 23 - .../MQTOptToMQTRef/MQTOptToMQTRef.td | 29 - .../Conversion/MQTRefToMQTOpt/CMakeLists.txt | 13 - .../MQTRefToMQTOpt/MQTRefToMQTOpt.h | 23 - .../MQTRefToMQTOpt/MQTRefToMQTOpt.td | 29 - .../Conversion/MQTRefToQIR/CMakeLists.txt | 13 - .../mlir/Conversion/MQTRefToQIR/MQTRefToQIR.h | 23 - .../Conversion/MQTRefToQIR/MQTRefToQIR.td | 66 - .../Conversion/QIRToMQTRef/CMakeLists.txt | 13 - .../mlir/Conversion/QIRToMQTRef/QIRToMQTRef.h | 23 - .../Conversion/QIRToMQTRef/QIRToMQTRef.td | 51 - mlir/include/mlir/Dialect/CMakeLists.txt | 2 - .../mlir/Dialect/Common/IR/CommonTraits.h | 139 -- .../mlir/Dialect/Common/IR/CommonTraits.td | 48 - .../mlir/Dialect/Common/IR/StdOps.td.inc | 344 ---- .../mlir/Dialect/MQTOpt/CMakeLists.txt | 10 - .../mlir/Dialect/MQTOpt/IR/CMakeLists.txt | 17 - .../mlir/Dialect/MQTOpt/IR/MQTOptDialect.h | 76 - .../Dialect/MQTOpt/IR/MQTOptInterfaces.td | 228 --- .../mlir/Dialect/MQTOpt/IR/MQTOptOps.td | 225 --- .../mlir/Dialect/MQTOpt/IR/WireIterator.h | 340 ---- .../Dialect/MQTOpt/Transforms/CMakeLists.txt | 12 - .../Transforms/LiftMeasurementsPasses.h | 32 - .../mlir/Dialect/MQTOpt/Transforms/Passes.h | 62 - .../mlir/Dialect/MQTOpt/Transforms/Passes.td | 173 -- .../Transforms/Transpilation/Architecture.h | 121 -- .../MQTOpt/Transforms/Transpilation/Common.h | 136 -- .../Transforms/Transpilation/LayeredUnit.h | 73 - .../MQTOpt/Transforms/Transpilation/Layout.h | 380 ---- .../MQTOpt/Transforms/Transpilation/Router.h | 208 --- .../Transforms/Transpilation/SequentialUnit.h | 46 - .../MQTOpt/Transforms/Transpilation/Stack.h | 87 - .../MQTOpt/Transforms/Transpilation/Unit.h | 52 - .../mlir/Dialect/MQTRef/CMakeLists.txt | 9 - .../mlir/Dialect/MQTRef/IR/CMakeLists.txt | 17 - .../mlir/Dialect/MQTRef/IR/MQTRefDialect.h | 69 - .../Dialect/MQTRef/IR/MQTRefInterfaces.td | 108 -- .../mlir/Dialect/MQTRef/IR/MQTRefOps.td | 200 --- .../Translation/ImportQuantumComputation.h | 22 - mlir/lib/Conversion/CMakeLists.txt | 4 - .../Conversion/MQTOptToMQTRef/CMakeLists.txt | 23 - .../MQTOptToMQTRef/MQTOptToMQTRef.cpp | 368 ---- .../Conversion/MQTRefToMQTOpt/CMakeLists.txt | 23 - .../MQTRefToMQTOpt/MQTRefToMQTOpt.cpp | 483 ----- .../lib/Conversion/MQTRefToQIR/CMakeLists.txt | 23 - .../Conversion/MQTRefToQIR/MQTRefToQIR.cpp | 1036 ----------- .../lib/Conversion/QIRToMQTRef/CMakeLists.txt | 21 - .../Conversion/QIRToMQTRef/QIRToMQTRef.cpp | 661 ------- mlir/lib/Dialect/CMakeLists.txt | 2 - mlir/lib/Dialect/MQTOpt/CMakeLists.txt | 10 - mlir/lib/Dialect/MQTOpt/IR/CMakeLists.txt | 38 - mlir/lib/Dialect/MQTOpt/IR/MQTOptOps.cpp | 235 --- .../Dialect/MQTOpt/Transforms/CMakeLists.txt | 43 - .../Transforms/DeadGateEliminationPattern.cpp | 61 - .../FromQuantumComputationPattern.cpp | 338 ---- .../MQTOpt/Transforms/GateElimination.cpp | 44 - .../Transforms/GateEliminationPattern.cpp | 159 -- .../LiftMeasurementsAboveControlsPattern.cpp | 69 - .../LiftMeasurementsAboveGatesPatterns.cpp | 164 -- .../Transforms/LiftMeasurementsPass.cpp | 48 - .../MQTOpt/Transforms/MQTCoreRoundTrip.cpp | 45 - .../MQTOpt/Transforms/MergeRotationGates.cpp | 45 - .../Transforms/MergeRotationGatesPattern.cpp | 206 --- .../MQTOpt/Transforms/QuantumSinkPass.cpp | 46 - .../Transforms/QuantumSinkPushPattern.cpp | 344 ---- .../Transforms/QuantumSinkShiftPattern.cpp | 168 -- ...ReplaceBasisStateControlsWithIfPattern.cpp | 281 --- .../MQTOpt/Transforms/ReuseQubitsPass.cpp | 45 - .../MQTOpt/Transforms/ReuseQubitsPattern.cpp | 177 -- .../SwapReconstructionAndElision.cpp | 50 - .../SwapReconstructionAndElisionPattern.cpp | 236 --- .../ToQuantumComputationPattern.cpp | 488 ------ .../Transforms/Transpilation/Architecture.cpp | 170 -- .../Transforms/Transpilation/Common.cpp | 141 -- .../Transforms/Transpilation/LayeredUnit.cpp | 325 ---- .../Transpilation/SequentialUnit.cpp | 80 - .../Transpilation/sc/AStarRoutingPass.cpp | 199 --- .../Transpilation/sc/NaiveRoutingPass.cpp | 170 -- .../Transpilation/sc/PlacementPass.cpp | 500 ------ .../sc/RoutingVerificationPass.cpp | 164 -- mlir/lib/Dialect/MQTRef/CMakeLists.txt | 10 - mlir/lib/Dialect/MQTRef/IR/CMakeLists.txt | 38 - mlir/lib/Dialect/MQTRef/IR/MQTRefOps.cpp | 128 -- .../Dialect/MQTRef/Translation/CMakeLists.txt | 28 - .../Translation/ImportQuantumComputation.cpp | 660 ------- mlir/test/CMakeLists.txt | 28 - mlir/test/Conversion/mqtopt-to-mqtref.mlir | 606 ------- mlir/test/Conversion/mqtref-to-mqtopt.mlir | 593 ------- mlir/test/Conversion/mqtref-to-qir.mlir | 915 ---------- mlir/test/Conversion/qir-to-mqtref.mlir | 1070 ------------ .../Dialect/MQTOpt/IR/dialect_features.mlir | 1554 ----------------- .../Transforms/Transpilation/basics.mlir | 376 ---- .../Transforms/Transpilation/grover_5.mlir | 687 -------- .../Transpilation/invalid-arch-option.mlir | 17 - .../Transpilation/missing-arch-option.mlir | 17 - .../Transpilation/routing-placement.mlir | 34 - .../Transpilation/routing-verification.mlir | 41 - .../Dialect/MQTOpt/Transforms/branch-opt.mlir | 213 --- .../MQTOpt/Transforms/gate-elimination.mlir | 374 ---- .../MQTOpt/Transforms/lift-measurements.mlir | 421 ----- .../Transforms/merge-rotation-gates.mlir | 472 ----- ...reuse-qubits-with-measurement-lifting.mlir | 205 --- .../MQTOpt/Transforms/reuse-qubits.mlir | 219 --- .../swap-reconstruction-and-elision.mlir | 354 ---- .../Transforms/to-quantum-computation.mlir | 1060 ----------- .../Dialect/MQTRef/IR/dialect_features.mlir | 1267 -------------- mlir/test/lit.cfg.py | 69 - mlir/test/lit.site.cfg.py.in | 20 - mlir/tools/CMakeLists.txt | 1 - mlir/tools/quantum-opt/CMakeLists.txt | 29 - mlir/tools/quantum-opt/quantum-opt.cpp | 51 - mlir/unittests/CMakeLists.txt | 5 +- mlir/unittests/dialect/CMakeLists.txt | 21 - mlir/unittests/dialect/test_wireiterator.cpp | 352 ---- mlir/unittests/translation/CMakeLists.txt | 34 - .../translation/test_translation.cpp | 1149 ------------ 119 files changed, 1 insertion(+), 24410 deletions(-) delete mode 100644 mlir/include/mlir/Conversion/MQTOptToMQTRef/CMakeLists.txt delete mode 100644 mlir/include/mlir/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.h delete mode 100644 mlir/include/mlir/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.td delete mode 100644 mlir/include/mlir/Conversion/MQTRefToMQTOpt/CMakeLists.txt delete mode 100644 mlir/include/mlir/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.h delete mode 100644 mlir/include/mlir/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.td delete mode 100644 mlir/include/mlir/Conversion/MQTRefToQIR/CMakeLists.txt delete mode 100644 mlir/include/mlir/Conversion/MQTRefToQIR/MQTRefToQIR.h delete mode 100644 mlir/include/mlir/Conversion/MQTRefToQIR/MQTRefToQIR.td delete mode 100644 mlir/include/mlir/Conversion/QIRToMQTRef/CMakeLists.txt delete mode 100644 mlir/include/mlir/Conversion/QIRToMQTRef/QIRToMQTRef.h delete mode 100644 mlir/include/mlir/Conversion/QIRToMQTRef/QIRToMQTRef.td delete mode 100644 mlir/include/mlir/Dialect/Common/IR/CommonTraits.h delete mode 100644 mlir/include/mlir/Dialect/Common/IR/CommonTraits.td delete mode 100644 mlir/include/mlir/Dialect/Common/IR/StdOps.td.inc delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/CMakeLists.txt delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/IR/CMakeLists.txt delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/IR/MQTOptDialect.h delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/IR/MQTOptInterfaces.td delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/IR/MQTOptOps.td delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/IR/WireIterator.h delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/CMakeLists.txt delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/LiftMeasurementsPasses.h delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.h delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Architecture.h delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.h delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Router.h delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/SequentialUnit.h delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Stack.h delete mode 100644 mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Unit.h delete mode 100644 mlir/include/mlir/Dialect/MQTRef/CMakeLists.txt delete mode 100644 mlir/include/mlir/Dialect/MQTRef/IR/CMakeLists.txt delete mode 100644 mlir/include/mlir/Dialect/MQTRef/IR/MQTRefDialect.h delete mode 100644 mlir/include/mlir/Dialect/MQTRef/IR/MQTRefInterfaces.td delete mode 100644 mlir/include/mlir/Dialect/MQTRef/IR/MQTRefOps.td delete mode 100644 mlir/include/mlir/Dialect/MQTRef/Translation/ImportQuantumComputation.h delete mode 100644 mlir/lib/Conversion/MQTOptToMQTRef/CMakeLists.txt delete mode 100644 mlir/lib/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.cpp delete mode 100644 mlir/lib/Conversion/MQTRefToMQTOpt/CMakeLists.txt delete mode 100644 mlir/lib/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.cpp delete mode 100644 mlir/lib/Conversion/MQTRefToQIR/CMakeLists.txt delete mode 100644 mlir/lib/Conversion/MQTRefToQIR/MQTRefToQIR.cpp delete mode 100644 mlir/lib/Conversion/QIRToMQTRef/CMakeLists.txt delete mode 100644 mlir/lib/Conversion/QIRToMQTRef/QIRToMQTRef.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/CMakeLists.txt delete mode 100644 mlir/lib/Dialect/MQTOpt/IR/CMakeLists.txt delete mode 100644 mlir/lib/Dialect/MQTOpt/IR/MQTOptOps.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/FromQuantumComputationPattern.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/GateElimination.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/GateEliminationPattern.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/LiftMeasurementsAboveControlsPattern.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/LiftMeasurementsAboveGatesPatterns.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/LiftMeasurementsPass.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/MergeRotationGates.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/MergeRotationGatesPattern.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/QuantumSinkPass.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/QuantumSinkPushPattern.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/QuantumSinkShiftPattern.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPass.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/SwapReconstructionAndElision.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/SwapReconstructionAndElisionPattern.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/ToQuantumComputationPattern.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/Architecture.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/Common.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/SequentialUnit.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/AStarRoutingPass.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/NaiveRoutingPass.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/PlacementPass.cpp delete mode 100644 mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/RoutingVerificationPass.cpp delete mode 100644 mlir/lib/Dialect/MQTRef/CMakeLists.txt delete mode 100644 mlir/lib/Dialect/MQTRef/IR/CMakeLists.txt delete mode 100644 mlir/lib/Dialect/MQTRef/IR/MQTRefOps.cpp delete mode 100644 mlir/lib/Dialect/MQTRef/Translation/CMakeLists.txt delete mode 100644 mlir/lib/Dialect/MQTRef/Translation/ImportQuantumComputation.cpp delete mode 100644 mlir/test/CMakeLists.txt delete mode 100644 mlir/test/Conversion/mqtopt-to-mqtref.mlir delete mode 100644 mlir/test/Conversion/mqtref-to-mqtopt.mlir delete mode 100644 mlir/test/Conversion/mqtref-to-qir.mlir delete mode 100644 mlir/test/Conversion/qir-to-mqtref.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/IR/dialect_features.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/Transpilation/basics.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/Transpilation/grover_5.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/Transpilation/invalid-arch-option.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/Transpilation/missing-arch-option.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/Transpilation/routing-placement.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/Transpilation/routing-verification.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/branch-opt.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/gate-elimination.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/merge-rotation-gates.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/reuse-qubits-with-measurement-lifting.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/reuse-qubits.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/swap-reconstruction-and-elision.mlir delete mode 100644 mlir/test/Dialect/MQTOpt/Transforms/to-quantum-computation.mlir delete mode 100644 mlir/test/Dialect/MQTRef/IR/dialect_features.mlir delete mode 100644 mlir/test/lit.cfg.py delete mode 100644 mlir/test/lit.site.cfg.py.in delete mode 100644 mlir/tools/quantum-opt/CMakeLists.txt delete mode 100644 mlir/tools/quantum-opt/quantum-opt.cpp delete mode 100644 mlir/unittests/dialect/CMakeLists.txt delete mode 100644 mlir/unittests/dialect/test_wireiterator.cpp delete mode 100644 mlir/unittests/translation/CMakeLists.txt delete mode 100644 mlir/unittests/translation/test_translation.cpp diff --git a/mlir/CMakeLists.txt b/mlir/CMakeLists.txt index b8211a2df6..596ba1d6ab 100644 --- a/mlir/CMakeLists.txt +++ b/mlir/CMakeLists.txt @@ -26,6 +26,5 @@ add_subdirectory(tools) # add test code if(BUILD_MQT_CORE_TESTS) - add_subdirectory(test) add_subdirectory(unittests) endif() diff --git a/mlir/include/mlir/Conversion/CMakeLists.txt b/mlir/include/mlir/Conversion/CMakeLists.txt index 8b2f748403..93f6a8a639 100644 --- a/mlir/include/mlir/Conversion/CMakeLists.txt +++ b/mlir/include/mlir/Conversion/CMakeLists.txt @@ -6,10 +6,6 @@ # # Licensed under the MIT License -add_subdirectory(MQTRefToMQTOpt) -add_subdirectory(MQTOptToMQTRef) -add_subdirectory(MQTRefToQIR) -add_subdirectory(QIRToMQTRef) add_subdirectory(QCOToQC) add_subdirectory(QCToQCO) add_subdirectory(QCToQIR) diff --git a/mlir/include/mlir/Conversion/MQTOptToMQTRef/CMakeLists.txt b/mlir/include/mlir/Conversion/MQTOptToMQTRef/CMakeLists.txt deleted file mode 100644 index 19d852be8f..0000000000 --- a/mlir/include/mlir/Conversion/MQTOptToMQTRef/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -set(LLVM_TARGET_DEFINITIONS MQTOptToMQTRef.td) -mlir_tablegen(MQTOptToMQTRef.h.inc -gen-pass-decls -name MQTOptToMQTRef) -add_public_tablegen_target(MQTOptToMQTRefIncGen) - -add_mlir_doc(MQTOptToMQTRef MLIRMQTOptToMQTRef Conversions/ -gen-pass-doc) diff --git a/mlir/include/mlir/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.h b/mlir/include/mlir/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.h deleted file mode 100644 index c71be38937..0000000000 --- a/mlir/include/mlir/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include // from @llvm-project - -namespace mqt::ir { - -#define GEN_PASS_DECL -#include "mlir/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.h.inc" - -#define GEN_PASS_REGISTRATION -#include "mlir/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.h.inc" - -} // namespace mqt::ir diff --git a/mlir/include/mlir/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.td b/mlir/include/mlir/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.td deleted file mode 100644 index 22bf328393..0000000000 --- a/mlir/include/mlir/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.td +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -include "mlir/Pass/PassBase.td" - -def MQTOptToMQTRef : Pass<"mqtopt-to-mqtref"> { - let summary = "Convert MQT's `Opt` to MQT's `Ref` dialect."; - - let description = [{ - This pass converts all current MQTOpt operations to an equivalent MQTRef operation. - The result of the converted MQTRef alloc operation acts as a qubit register reference and replaces the operand of the operation - that uses the initial MQTOpt qubit register. This reference is then propagated to all subsequent operations that use the state - of the initial MQTOpt register and replaces their operand. The same applies to the result of the extract operation that acts as a qubit - reference and is propagated to all later uses of this qubit. The MQTOpt insert operation is deleted as there is no equivalent - operation in the MQTRef dialect. The use of the insert operation's result in any subsequent operation is replaced by the qubit register reference. - }]; - - // Define dependent dialects - let dependentDialects = [ - "mlir::memref::MemRefDialect", - "::mqt::ir::opt::MQTOptDialect", - "::mqt::ir::ref::MQTRefDialect" - ]; -} diff --git a/mlir/include/mlir/Conversion/MQTRefToMQTOpt/CMakeLists.txt b/mlir/include/mlir/Conversion/MQTRefToMQTOpt/CMakeLists.txt deleted file mode 100644 index 39d0024862..0000000000 --- a/mlir/include/mlir/Conversion/MQTRefToMQTOpt/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -set(LLVM_TARGET_DEFINITIONS MQTRefToMQTOpt.td) -mlir_tablegen(MQTRefToMQTOpt.h.inc -gen-pass-decls -name MQTRefToMQTOpt) -add_public_tablegen_target(MQTRefToMQTOptIncGen) - -add_mlir_doc(MQTRefToMQTOpt MLIRMQTRefToMQTOpt Conversions/ -gen-pass-doc) diff --git a/mlir/include/mlir/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.h b/mlir/include/mlir/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.h deleted file mode 100644 index 2f0efead38..0000000000 --- a/mlir/include/mlir/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include // from @llvm-project - -namespace mqt::ir { - -#define GEN_PASS_DECL -#include "mlir/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.h.inc" - -#define GEN_PASS_REGISTRATION -#include "mlir/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.h.inc" - -} // namespace mqt::ir diff --git a/mlir/include/mlir/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.td b/mlir/include/mlir/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.td deleted file mode 100644 index 28a0e005ab..0000000000 --- a/mlir/include/mlir/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.td +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -include "mlir/Pass/PassBase.td" - -def MQTRefToMQTOpt : Pass<"mqtref-to-mqtopt"> { - let summary = "Convert MQT's `Ref` to MQT's `Opt` dialect."; - - let description = [{ - This pass converts all current MQTRef operations to an equivalent MQTOpt operation. - To convert them to the opt dialect, the conversion pass maintains a map that matches each ref qubit - and ref qubit register to its latest state value when replacing them with an opt operation. These state values - are obtained by updating the map with the return values of the newly created opt operations. The MQTOpt insert - operation is added when matching the dealloc operation of any qubit register. An MQTOpt insert operation is then - created for each qubit that was extracted from the qubit register and placed before the dealloc operation. - }]; - - // Define dependent dialects - let dependentDialects = [ - "mlir::memref::MemRefDialect", - "::mqt::ir::ref::MQTRefDialect", - "::mqt::ir::opt::MQTOptDialect" - ]; -} diff --git a/mlir/include/mlir/Conversion/MQTRefToQIR/CMakeLists.txt b/mlir/include/mlir/Conversion/MQTRefToQIR/CMakeLists.txt deleted file mode 100644 index 69772463df..0000000000 --- a/mlir/include/mlir/Conversion/MQTRefToQIR/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -set(LLVM_TARGET_DEFINITIONS MQTRefToQIR.td) -mlir_tablegen(MQTRefToQIR.h.inc -gen-pass-decls -name MQTRefToQIR) -add_public_tablegen_target(MQTRefToQIRIncGen) - -add_mlir_doc(MQTRefToQIR MLIRMQTRefToQIR Conversions/ -gen-pass-doc) diff --git a/mlir/include/mlir/Conversion/MQTRefToQIR/MQTRefToQIR.h b/mlir/include/mlir/Conversion/MQTRefToQIR/MQTRefToQIR.h deleted file mode 100644 index 457b4782de..0000000000 --- a/mlir/include/mlir/Conversion/MQTRefToQIR/MQTRefToQIR.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include // from @llvm-project - -namespace mqt::ir { - -#define GEN_PASS_DECL -#include "mlir/Conversion/MQTRefToQIR/MQTRefToQIR.h.inc" - -#define GEN_PASS_REGISTRATION -#include "mlir/Conversion/MQTRefToQIR/MQTRefToQIR.h.inc" - -} // namespace mqt::ir diff --git a/mlir/include/mlir/Conversion/MQTRefToQIR/MQTRefToQIR.td b/mlir/include/mlir/Conversion/MQTRefToQIR/MQTRefToQIR.td deleted file mode 100644 index 4d0d933546..0000000000 --- a/mlir/include/mlir/Conversion/MQTRefToQIR/MQTRefToQIR.td +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -include "mlir/Pass/PassBase.td" - -def MQTRefToQIR : Pass<"mqtref-to-qir"> { - let summary = "Lower the `MQTRef` dialect to the LLVM dialect compliant with QIR 2.0 (Base Profile)"; - - let description = [{ - Overview - Converts the MQTRef dialect to the LLVM dialect following the QIR 2.0 Base Profile. Quantum operations are mapped to calls - into the QIR instruction set, while non-quantum operations are lowered using MLIR's standard LLVM conversions. - - Requirements - - Input is a valid module in the MQTRef dialect. - - Static or dynamic qubit addressing is supported. - - The entry function must be marked with the `entry_point` attribute. - - The program must have straight-line control flow (no complex branching). - - Behavior - - Each MQTRef quantum operation is replaced by a call to the corresponding QIR function in the LLVM dialect. - - Required QIR module flags are attached as attributes to the entry function. - - The entry function is split into four blocks to satisfy QIR Base Profile constraints. - - Non-quantum dialects are lowered via MLIR's built-in conversions. - - Block structure (Base Profile) - 0. Initialization block - - Sets up the execution environment and performs required runtime initialization. - 1. Reversible operations block - - Contains only void-returning calls to reversible quantum operations. - 2. Irreversible operations block - - Contains only void-returning calls to operations marked irreversible, e.g.: - `__quantum__qis__mz__body`, `__quantum__rt__qubit_release_array`, - `__quantum__rt__qubit_release`, `__quantum__qis__reset__body`. - 3. Epilogue block - - Records measurement results and returns from the entry function. - - Blocks are connected via unconditional branches in the order listed above. - - Operation mapping - - `mqtref.measure` → call to `__quantum__qis__mz__body` followed by - `__quantum__rt__result_record_output` to record the measurement result. - - Lowering of non-quantum operations - - Dialects lowered via MLIR conversions: `func`, `arith`, `cf` → LLVM dialect. - - Limitations - - Only straight-line control flow is supported. - - Adheres to QIR Base Profile requirements. - - Unsupported operations or control flow patterns may cause the conversion to fail. - - Producing LLVM IR - - After conversion to the LLVM dialect, produce LLVM IR with: - mlir-translate --mlir-to-llvmir input.mlir > output.ll - }]; - - // Define dependent dialects - let dependentDialects = [ - "::mqt::ir::ref::MQTRefDialect", - "mlir::LLVM::LLVMDialect" - ]; -} diff --git a/mlir/include/mlir/Conversion/QIRToMQTRef/CMakeLists.txt b/mlir/include/mlir/Conversion/QIRToMQTRef/CMakeLists.txt deleted file mode 100644 index 53427c37ae..0000000000 --- a/mlir/include/mlir/Conversion/QIRToMQTRef/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -set(LLVM_TARGET_DEFINITIONS QIRToMQTRef.td) -mlir_tablegen(QIRToMQTRef.h.inc -gen-pass-decls -name QIRToMQTRef) -add_public_tablegen_target(QIRToMQTRefIncGen) - -add_mlir_doc(QIRToMQTRef MLIRQIRToMQTRef Conversions/ -gen-pass-doc) diff --git a/mlir/include/mlir/Conversion/QIRToMQTRef/QIRToMQTRef.h b/mlir/include/mlir/Conversion/QIRToMQTRef/QIRToMQTRef.h deleted file mode 100644 index 19927fe5d1..0000000000 --- a/mlir/include/mlir/Conversion/QIRToMQTRef/QIRToMQTRef.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include // from @llvm-project - -namespace mqt::ir { - -#define GEN_PASS_DECL -#include "mlir/Conversion/QIRToMQTRef/QIRToMQTRef.h.inc" - -#define GEN_PASS_REGISTRATION -#include "mlir/Conversion/QIRToMQTRef/QIRToMQTRef.h.inc" - -} // namespace mqt::ir diff --git a/mlir/include/mlir/Conversion/QIRToMQTRef/QIRToMQTRef.td b/mlir/include/mlir/Conversion/QIRToMQTRef/QIRToMQTRef.td deleted file mode 100644 index a02c19ce70..0000000000 --- a/mlir/include/mlir/Conversion/QIRToMQTRef/QIRToMQTRef.td +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -include "mlir/Pass/PassBase.td" - -def QIRToMQTRef : Pass<"qir-to-mqtref"> { - let summary = "Raise LLVM/QIR (Base Profile) to the `MQTRef` dialect by converting QIR calls into `mqtref` ops"; - - let description = [{ - Overview - Converts LLVM dialect modules that use QIR into MQTRef IR by pattern-matching and replacing calls to QIR functions with - equivalent operations from the MQTRef dialect. - - Requirements - - Input is a valid MLIR module in the LLVM dialect that follows QIR Base Profile conventions. - - Static or dynamic qubit addressing is supported. - - The program is expected to have straight-line control flow. - - Behavior - - Replaces `llvm.call` operations targeting QIR functions with corresponding `mqtref` operations. - - The conversion is driven by the QIR function name used by the call. - - Operation mapping - - `__quantum__qis__mz__body` → `mqtref.measure` returning an `i1` measurement result. - - The paired `__quantum__rt__result_record_output` for measurements is removed. - - Non-converted constructs - - `llvm.mlir.constant` and function declarations are currently not converted and remain in the module. - - Limitations - - Only simple, straight-line programs that follow the QIR Base Profile are supported. - - QIR calls without a known mapping in `mqtref` cause the conversion to fail. - - Obtaining the input module - - Import an LLVM IR file as MLIR with: - mlir-translate --import-llvm input.ll -o output.mlir - }]; - - // Define dependent dialects - let dependentDialects = [ - "::mqt::ir::ref::MQTRefDialect", - "mlir::LLVM::LLVMDialect", - "mlir::memref::MemRefDialect", - "mlir::arith::ArithDialect" - ]; -} diff --git a/mlir/include/mlir/Dialect/CMakeLists.txt b/mlir/include/mlir/Dialect/CMakeLists.txt index b18d773031..01edd5218a 100644 --- a/mlir/include/mlir/Dialect/CMakeLists.txt +++ b/mlir/include/mlir/Dialect/CMakeLists.txt @@ -6,7 +6,5 @@ # # Licensed under the MIT License -add_subdirectory(MQTOpt) -add_subdirectory(MQTRef) add_subdirectory(QC) add_subdirectory(QCO) diff --git a/mlir/include/mlir/Dialect/Common/IR/CommonTraits.h b/mlir/include/mlir/Dialect/Common/IR/CommonTraits.h deleted file mode 100644 index f24f7eed06..0000000000 --- a/mlir/include/mlir/Dialect/Common/IR/CommonTraits.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include -#include -#include -#include - -namespace mqt::ir::common { -template class TargetArityTrait { -public: - template - class Impl : public mlir::OpTrait::TraitBase { - public: - [[nodiscard]] static mlir::LogicalResult verifyTrait(mlir::Operation* op) { - auto unitaryOp = mlir::cast(op); - if (const auto size = unitaryOp.getInQubits().size(); size != N) { - return op->emitError() - << "number of input qubits (" << size << ") must be " << N; - } - return mlir::success(); - } - }; -}; - -template class ParameterArityTrait { -public: - template - class Impl : public mlir::OpTrait::TraitBase { - public: - [[nodiscard]] static mlir::LogicalResult verifyTrait(mlir::Operation* op) { - auto paramOp = mlir::cast(op); - const auto& params = paramOp.getParams(); - const auto& staticParams = paramOp.getStaticParams(); - const auto numParams = - params.size() + (staticParams.has_value() ? staticParams->size() : 0); - if (numParams != N) { - return op->emitError() << "operation expects exactly " << N - << " parameters but got " << numParams; - } - const auto& paramsMask = paramOp.getParamsMask(); - if (!params.empty() && staticParams.has_value() && - !paramsMask.has_value()) { - return op->emitError() << "operation has mixed dynamic and static " - "parameters but no parameter mask"; - } - if (paramsMask.has_value() && paramsMask->size() != N) { - return op->emitError() << "operation expects exactly " << N - << " parameters but has a parameter mask with " - << paramsMask->size() << " entries"; - } - if (paramsMask.has_value()) { - const auto trueEntries = static_cast(std::count_if( - paramsMask->begin(), paramsMask->end(), [](bool b) { return b; })); - if ((!staticParams.has_value() || staticParams->empty()) && - trueEntries != 0) { - return op->emitError() << "operation has no static parameter but has " - "a parameter mask with " - << trueEntries << " true entries"; - } - if (const auto size = staticParams->size(); size != trueEntries) { - return op->emitError() - << "operation has " << size - << " static parameter(s) but has a parameter mask with " - << trueEntries << " true entries"; - } - } - return mlir::success(); - } - }; -}; - -template -class NoControlTrait - : public mlir::OpTrait::TraitBase { -public: - [[nodiscard]] static mlir::LogicalResult verifyTrait(mlir::Operation* op) { - if (auto unitaryOp = mlir::cast(op); unitaryOp.isControlled()) { - return op->emitOpError() - << "Gate marked as NoControl should not have control qubits"; - } - return mlir::success(); - } -}; - -template -class UniqueSizeDefinitionTrait - : public mlir::OpTrait::TraitBase { -public: - [[nodiscard]] static mlir::LogicalResult verifyTrait(mlir::Operation* op) { - auto castOp = mlir::cast(op); - const auto hasAttr = op->hasAttr("size_attr"); - const auto hasOperand = castOp.getSize() != nullptr; - if (!(hasAttr ^ hasOperand)) { - return op->emitOpError() - << "exactly one attribute (" - << (hasAttr ? std::to_string( - op->getAttrOfType("size_attr") - .getInt()) - : "undefined") - << ") or operand (" << castOp.getSize() - << ") must be provided for 'size'"; - } - return mlir::success(); - } -}; - -template -class UniqueIndexDefinitionTrait - : public mlir::OpTrait::TraitBase { -public: - [[nodiscard]] static mlir::LogicalResult verifyTrait(mlir::Operation* op) { - auto castOp = mlir::cast(op); - const auto hasAttr = op->hasAttr("index_attr"); - const auto hasOperand = castOp.getIndex() != nullptr; - if (!(hasAttr ^ hasOperand)) { - return op->emitOpError() - << "exactly one attribute (" - << (hasAttr ? std::to_string(op->getAttrOfType( - "index_attr") - .getInt()) - : "undefined") - << ") or operand (" << castOp.getIndex() - << ") must be provided for 'index'"; - } - return mlir::success(); - } -}; - -} // namespace mqt::ir::common diff --git a/mlir/include/mlir/Dialect/Common/IR/CommonTraits.td b/mlir/include/mlir/Dialect/Common/IR/CommonTraits.td deleted file mode 100644 index 2c1f029e34..0000000000 --- a/mlir/include/mlir/Dialect/Common/IR/CommonTraits.td +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -#ifndef MQTCOMMON_TRAITS -#define MQTCOMMON_TRAITS - -include "mlir/IR/OpBase.td" -include "mlir/IR/EnumAttr.td" -include "mlir/IR/DialectBase.td" -include "mlir/Interfaces/SideEffectInterfaces.td" - -class TargetArity - : ParamNativeOpTrait<"TargetArityTrait", !cast(N)> { - let cppNamespace = "::mqt::ir::common"; -} - -def NoTarget : TargetArity<0>; -def OneTarget : TargetArity<1>; -def TwoTarget : TargetArity<2>; - -class ParameterArity - : ParamNativeOpTrait<"ParameterArityTrait", !cast(N)> { - let cppNamespace = "::mqt::ir::common"; -} - -def NoParameter : ParameterArity<0>; -def OneParameter : ParameterArity<1>; -def TwoParameters : ParameterArity<2>; -def ThreeParameters : ParameterArity<3>; - -def NoControl : NativeOpTrait<"NoControlTrait"> { - let cppNamespace = "::mqt::ir::common"; -} - -def UniqueSizeDefinition : NativeOpTrait<"UniqueSizeDefinitionTrait"> { - let cppNamespace = "::mqt::ir::common"; -} - -def UniqueIndexDefinition : NativeOpTrait<"UniqueIndexDefinitionTrait"> { - let cppNamespace = "::mqt::ir::common"; -} - -#endif // MQTCOMMON_TRAITS diff --git a/mlir/include/mlir/Dialect/Common/IR/StdOps.td.inc b/mlir/include/mlir/Dialect/Common/IR/StdOps.td.inc deleted file mode 100644 index 915dace304..0000000000 --- a/mlir/include/mlir/Dialect/Common/IR/StdOps.td.inc +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -#ifndef @DIALECT_NAME_UPPER@_STD_OPS -#define @DIALECT_NAME_UPPER@_STD_OPS - -include "mlir/Dialect/@DIALECT_NAME@/IR/@DIALECT_NAME@Ops.td" - -def GPhaseOp : UnitaryOp<"gphase", [NoTarget, OneParameter]> { - let summary = "GPhase operation"; - - let description = [{ - This class represents a global phase gate. It accepts a parameter indicating - the degree of the rotation angle. The global phase gate does not have - any input and output qubits. It might still be controlled by arbitrarily - many qubits. - }]; -} - -def IOp : UnitaryOp<"i", [OneTarget, NoParameter]> { - let summary = "I operation"; - - let description = [{ - This class represents an identity gate. It takes a qubit and a variadic - list of positive/negative controls as an input. - }]; -} - -def BarrierOp : UnitaryOp<"barrier", [NoParameter, NoControl]> { - let summary = "Barrier operation"; - - let description = [{ - This class represents the barrier operation. It has a variadic number - of input qubits and no control qubits. - }]; -} - -def HOp : UnitaryOp<"h", [OneTarget, NoParameter]> { - let summary = "H operation"; - - let description = [{ - This class represents a Hadamard gate. It takes a qubit and a variadic - list of positive/negative controls as an input. - }]; -} - -def XOp : UnitaryOp<"x", [OneTarget, NoParameter]> { - let summary = "X operation"; - - let description = [{ - This class represents a Pauli-X gate. It takes a qubit and a variadic - list of positive/negative controls as an input. - }]; -} - -def YOp : UnitaryOp<"y", [OneTarget, NoParameter]> { - let summary = "Y operation"; - - let description = [{ - This class represents a Pauli-Y gate. It takes a qubit and a variadic - list of positive/negative controls as an input. - }]; -} - -def ZOp : UnitaryOp<"z", [OneTarget, NoParameter]> { - let summary = "Z operation"; - - let description = [{ - This class represents a Pauli-Z gate. It takes a qubit and a variadic - list of positive/negative controls as an input. - }]; -} - -def SOp : UnitaryOp<"s", [OneTarget, NoParameter]> { - let summary = "S operation"; - - let description = [{ - This class represents an S gate. It takes a qubit as an input and a - variadic list of positive/negative controls. - }]; -} - -def SdgOp : UnitaryOp<"sdg", [OneTarget, NoParameter]> { - let summary = "Sdg operation"; - - let description = [{ - This class represents an inverse S gate. It takes a qubit and a variadic - list of positive/negative controls as an input. - }]; -} - -def TOp : UnitaryOp<"t", [OneTarget, NoParameter]> { - let summary = "T operation"; - - let description = [{ - This class represents a T gate. It takes a qubit and a variadic list of - positive/negative controls as an input. - }]; -} - -def TdgOp : UnitaryOp<"tdg", [OneTarget, NoParameter]> { - let summary = "Tdg operation"; - - let description = [{ - This class represents an inverse T gate. It takes a qubit and a variadic - list of positive/negative controls as an input. - }]; -} - -def VOp : UnitaryOp<"v", [OneTarget, NoParameter]> { - let summary = "V operation"; - - let description = [{ - This class represents a V gate. It takes a qubit and a variadic - list of positive/negative controls as an input. - }]; -} - -def VdgOp : UnitaryOp<"vdg", [OneTarget, NoParameter]> { - let summary = "Vdg operation"; - - let description = [{ - This class represents an inverse V gate. It takes a qubit and a variadic - list of positive/negative controls as an input. - }]; -} - -def UOp : UnitaryOp<"u", [OneTarget, ThreeParameters]> { - let summary = "U operation"; - - let description = [{ - This class represents a U gate. It takes a qubit and a variadic - list of positive/negative controls as an input. Additionally, it accepts - three parameters indicating the degree of the rotation angles. - }]; -} - -def U2Op : UnitaryOp<"u2", [OneTarget, TwoParameters]> { - let summary = "U2 operation"; - - let description = [{ - This class represents a U2 gate. It takes a qubit and a variadic - list of positive/negative controls as an input. Additionally, it accepts - two parameters indicating the degree of the rotation angles. - }]; -} - -def POp : UnitaryOp<"p", [OneTarget, OneParameter]> { - let summary = "P operation"; - - let description = [{ - This class represents a phase gate. It takes a qubit and a variadic - list of positive/negative controls as an input. Additionally, it accepts - a parameter indicating the degree of the rotation angle. - }]; -} - -def SXOp : UnitaryOp<"sx", [OneTarget, NoParameter]> { - let summary = "SX operation"; - - let description = [{ - This class represents an SX gate. It takes a qubit and a variadic - list of positive/negative controls as an input. - }]; -} - -def SXdgOp : UnitaryOp<"sxdg", [OneTarget, NoParameter]> { - let summary = "SXdg operation"; - - let description = [{ - This class represents an inverse SX gate. It takes a qubit and a - variadic list of positive/negative controls as an input. - }]; -} - -def ROp : UnitaryOp<"r", [OneTarget, TwoParameters]> { - let summary = "R operation"; - - let description = [{ - This class represents a R gate. It takes a qubit and a variadic - list of positive/negative controls as an input. Additionally, it accepts - two parameters indicating the degree of the rotation angles. - }]; -} - -def RXOp : UnitaryOp<"rx", [OneTarget, OneParameter]> { - let summary = "RX operation"; - - let description = [{ - This class represents an RX gate. It takes a qubit and a variadic - list of positive/negative controls as input. Additionally, it accepts - a parameter indicating the degree of the rotation angle. - }]; -} - -def RYOp : UnitaryOp<"ry", [OneTarget, OneParameter]> { - let summary = "RY operation"; - - let description = [{ - This class represents an RY gate. It takes a qubit and a variadic - list of positive/negative controls as input. Additionally, it accepts - a parameter indicating the degree of the rotation angle. - }]; -} - -def RZOp : UnitaryOp<"rz", [OneTarget, OneParameter]> { - let summary = "RZ operation"; - - let description = [{ - This class represents an RZ gate. It takes a qubit and a variadic - list of positive/negative controls as input. Additionally, it accepts - a parameter indicating the degree of the rotation angle. - }]; -} - -def SWAPOp : UnitaryOp<"swap", [TwoTarget, NoParameter]> { - let summary = "SWAP operation"; - - let description = [{ - This class represents a SWAP gate. It takes two qubits and a variadic - list of positive/negative controls as input. - }]; -} - -def iSWAPOp : UnitaryOp<"iswap", [TwoTarget, NoParameter]> { - let summary = "iSWAP operation"; - - let description = [{ - This class represents an iSWAP gate. It takes two qubits and a variadic - list of positive/negative controls as input. - }]; -} - -def iSWAPdgOp : UnitaryOp<"iswapdg", [TwoTarget, NoParameter]> { - let summary = "iSWAPdg operation"; - - let description = [{ - This class represents an inverse iSWAP gate. It takes two qubits and a - variadic list of positive/negative controls as input. - }]; -} - -def PeresOp : UnitaryOp<"peres", [TwoTarget, NoParameter]> { - let summary = "Peres operation"; - - let description = [{ - This class represents a Peres gate. It takes two qubits and a variadic - list of positive/negative controls as input. - }]; -} - -def PeresdgOp : UnitaryOp<"peresdg", [TwoTarget, NoParameter]> { - let summary = "Peresdg operation"; - - let description = [{ - This class represents an inverse Peres gate. It takes two qubits and a - variadic list of positive/negative controls as input. - }]; -} - -def DCXOp : UnitaryOp<"dcx", [TwoTarget, NoParameter]> { - let summary = "DCX operation"; - - let description = [{ - This class represents a DCX gate. It takes two qubits and a variadic - list of positive/negative controls as input. - }]; -} - -def ECROp : UnitaryOp<"ecr", [TwoTarget, NoParameter]> { - let summary = "DCX operation"; - - let description = [{ - This class represents an ECR gate. It takes two qubits and a variadic - list of positive/negative controls as input. - }]; -} - -def RXXOp : UnitaryOp<"rxx", [TwoTarget, OneParameter]> { - let summary = "RXX operation"; - - let description = [{ - This class represents an RXX gate. It takes two qubits and a variadic - list of positive/negative controls as input. Additionally, it accepts a - parameter indicating the degree of the rotation angle. - }]; -} - -def RYYOp : UnitaryOp<"ryy", [TwoTarget, OneParameter]> { - let summary = "RYY operation"; - - let description = [{ - This class represents an RYY gate. It takes two qubits and a variadic - list of positive/negative controls as input. Additionally, it accepts a - parameter indicating the degree of the rotation angle. - }]; -} - -def RZZOp : UnitaryOp<"rzz", [TwoTarget, OneParameter]> { - let summary = "RZZ operation"; - - let description = [{ - This class represents an RZZ gate. It takes two qubits and a variadic - list of positive/negative controls as input. Additionally, it accepts a - parameter indicating the degree of the rotation angle. - }]; -} - -def RZXOp : UnitaryOp<"rzx", [TwoTarget, OneParameter]> { - let summary = "RZX operation"; - - let description = [{ - This class represents an RZX gate. It takes two qubits and a variadic - list of positive/negative controls as input. Additionally, it accepts a - parameter indicating the degree of the rotation angle. - }]; -} - -def XXminusYYOp : UnitaryOp<"xx_minus_yy", [TwoTarget, TwoParameters]> { - let summary = "XX-YY operation"; - - let description = [{ - This class represents an XX-YY gate. It takes two qubits and a - variadic list of positive/negative controls as input. Additionally, it - accepts two parameters indicating the degree of the rotation angles. - }]; -} - -def XXplusYYOp : UnitaryOp<"xx_plus_yy", [TwoTarget, TwoParameters]> { - let summary = "XX+YY operation"; - - let description = [{ - This class represents an XX+YY gate. It takes two qubits and a - variadic list of positive/negative controls as input. Additionally, it - accepts two parameters indicating the degree of the rotation angles. - }]; -} - -#endif // @DIALECT_NAME_UPPER@_STD_OPS diff --git a/mlir/include/mlir/Dialect/MQTOpt/CMakeLists.txt b/mlir/include/mlir/Dialect/MQTOpt/CMakeLists.txt deleted file mode 100644 index 3b0a561d0f..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -add_subdirectory(IR) -add_subdirectory(Transforms) diff --git a/mlir/include/mlir/Dialect/MQTOpt/IR/CMakeLists.txt b/mlir/include/mlir/Dialect/MQTOpt/IR/CMakeLists.txt deleted file mode 100644 index b8f39cc434..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/IR/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -set(DIALECT_NAME "MQTOpt") -set(DIALECT_NAME_UPPER "MQTOPT") -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../../Common/IR/StdOps.td.inc - ${CMAKE_CURRENT_BINARY_DIR}/MQTOptStdOps.td @ONLY) - -add_mlir_dialect(MQTOptOps mqtopt) -add_mlir_interface(MQTOptInterfaces) -add_mlir_doc(MQTOptOps MLIRMQTOptDialect Dialects/ -gen-dialect-doc) -add_mlir_doc(MQTOptInterfaces MLIRMQTOptInterfaces Dialects/ -gen-op-interface-docs -dialect=mqtopt) diff --git a/mlir/include/mlir/Dialect/MQTOpt/IR/MQTOptDialect.h b/mlir/include/mlir/Dialect/MQTOpt/IR/MQTOptDialect.h deleted file mode 100644 index 6378a22a16..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/IR/MQTOptDialect.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include - -// Suppress warnings about ambiguous reversed operators in MLIR -// (see https://github.com/llvm/llvm-project/issues/45853) -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wambiguous-reversed-operator" -#endif -#include "mlir/Interfaces/InferTypeOpInterface.h" -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#define DIALECT_NAME_MQTOPT "mqtopt" - -//===----------------------------------------------------------------------===// -// Dialect -//===----------------------------------------------------------------------===// - -#include "mlir/Dialect/MQTOpt/IR/MQTOptOpsDialect.h.inc" // IWYU pragma: export - -//===----------------------------------------------------------------------===// -// Types -//===----------------------------------------------------------------------===// - -#define GET_TYPEDEF_CLASSES -#include "mlir/Dialect/MQTOpt/IR/MQTOptOpsTypes.h.inc" // IWYU pragma: export - -//===----------------------------------------------------------------------===// -// Interfaces -//===----------------------------------------------------------------------===// - -#include "mlir/Dialect/Common/IR/CommonTraits.h" // IWYU pragma: export -#include "mlir/Dialect/MQTOpt/IR/MQTOptInterfaces.h.inc" // IWYU pragma: export - -//===----------------------------------------------------------------------===// -// Operations -//===----------------------------------------------------------------------===// - -#define GET_OP_CLASSES -#include "mlir/Dialect/MQTOpt/IR/MQTOptOps.h.inc" // IWYU pragma: export - -namespace mqt::ir::opt { -mlir::ParseResult -parseOptOutputTypes(mlir::OpAsmParser& parser, - llvm::SmallVectorImpl& out_qubits, - llvm::SmallVectorImpl& pos_ctrl_out_qubits, - llvm::SmallVectorImpl& neg_ctrl_out_qubits); - -void printOptOutputTypes(mlir::OpAsmPrinter& printer, mlir::Operation* op, - mlir::TypeRange out_qubits, - mlir::TypeRange pos_ctrl_out_qubits, - mlir::TypeRange neg_ctrl_out_qubits); - -mlir::ParseResult parseOptParams( - mlir::OpAsmParser& parser, - llvm::SmallVectorImpl& params, - mlir::Attribute& staticParams, mlir::Attribute& paramsMask); - -void printOptParams(mlir::OpAsmPrinter& printer, mlir::Operation* op, - mlir::ValueRange params, - mlir::DenseF64ArrayAttr staticParams, - mlir::DenseBoolArrayAttr paramsMask); -} // namespace mqt::ir::opt diff --git a/mlir/include/mlir/Dialect/MQTOpt/IR/MQTOptInterfaces.td b/mlir/include/mlir/Dialect/MQTOpt/IR/MQTOptInterfaces.td deleted file mode 100644 index 2238c12a1b..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/IR/MQTOptInterfaces.td +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -#ifndef MQTOPT_INTERFACES -#define MQTOPT_INTERFACES - -include "mlir/IR/OpBase.td" - -//===----------------------------------------------------------------------===// -// Interfaces -//===----------------------------------------------------------------------===// - -def UnitaryInterface : OpInterface<"UnitaryInterface"> { - let description = [{ - This interface provides a generic way to interact with unitary - operations in the MQTOpt dialect. Unitary operations are quantum - operations that are reversible and can be represented by a unitary - matrix. The interface provides methods to access the operands and - results of the operation. - }]; - - let cppNamespace = "::mqt::ir::opt"; - - let methods = [ - InterfaceMethod< - /*desc=*/ "Returns all input qubits of the operation excl. control qubits.", - /*returnType=*/ "mlir::OperandRange", - /*methodName=*/ "getInQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return $_op.getInQubits(); - }]>, - InterfaceMethod< - /*desc=*/ "Returns all positively-controlling input qubits of the operation.", - /*returnType=*/ "mlir::OperandRange", - /*methodName=*/ "getPosCtrlInQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return $_op.getPosCtrlInQubits(); - }]>, - InterfaceMethod< - /*desc=*/ "Returns all negatively-controlling input qubits of the operation.", - /*returnType=*/ "mlir::OperandRange", - /*methodName=*/ "getNegCtrlInQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return $_op.getNegCtrlInQubits(); - }]>, - InterfaceMethod< - /*desc=*/ "Returns all output qubits of the operation excl. control qubits.", - /*returnType=*/ "mlir::ResultRange", - /*methodName=*/ "getOutQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return $_op.getOutQubits(); - }]>, - InterfaceMethod< - /*desc=*/ "Returns all positively-controlling output qubits of the operation.", - /*returnType=*/ "mlir::ResultRange", - /*methodName=*/ "getPosCtrlOutQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return $_op.getPosCtrlOutQubits(); - }]>, - InterfaceMethod< - /*desc=*/ "Returns all negatively-controlling output qubits of the operation.", - /*returnType=*/ "mlir::ResultRange", - /*methodName=*/ "getNegCtrlOutQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return $_op.getNegCtrlOutQubits(); - }]>, - InterfaceMethod< - /*desc=*/ "Returns all controlling input qubits of the operation.", - /*returnType=*/ "std::vector", - /*methodName=*/ "getAllCtrlInQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - const auto& posCtrlQubits = $_op.getPosCtrlInQubits(); - const auto& negCtrlQubits = $_op.getNegCtrlInQubits(); - std::vector controls{}; - controls.reserve(posCtrlQubits.size() + negCtrlQubits.size()); - controls.insert(controls.end(), posCtrlQubits.begin(), posCtrlQubits.end()); - controls.insert(controls.end(), negCtrlQubits.begin(), negCtrlQubits.end()); - return controls; - }]>, - InterfaceMethod< - /*desc=*/ "Returns all control output qubits of the operation.", - /*returnType=*/ "std::vector", - /*methodName=*/ "getAllCtrlOutQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - const auto& posCtrlQubits = $_op.getPosCtrlOutQubits(); - const auto& negCtrlQubits = $_op.getNegCtrlOutQubits(); - std::vector controls{}; - controls.reserve(posCtrlQubits.size() + negCtrlQubits.size()); - controls.insert(controls.end(), posCtrlQubits.begin(), posCtrlQubits.end()); - controls.insert(controls.end(), negCtrlQubits.begin(), negCtrlQubits.end()); - return controls; - }]>, - InterfaceMethod< - /*desc=*/ "Returns true if the operation has any control qubits, otherwise false.", - /*returnType=*/ "bool", - /*methodName=*/ "isControlled", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return !$_op.getPosCtrlInQubits().empty() || !$_op.getNegCtrlInQubits().empty(); - }]>, - InterfaceMethod< - /*desc=*/ "Returns all input qubits of the operation incl. all controlling qubits.", - /*returnType=*/ "std::vector", - /*methodName=*/ "getAllInQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - const auto& inQubits = $_op.getInQubits(); - const auto& controls = $_op.getAllCtrlInQubits(); - std::vector operands{}; - operands.reserve(inQubits.size() + controls.size()); - operands.insert(operands.end(), inQubits.begin(), inQubits.end()); - operands.insert(operands.end(), controls.begin(), controls.end()); - return operands; - }]>, - InterfaceMethod< - /*desc=*/ "Returns all output qubits of the operation incl. all controlling qubits.", - /*returnType=*/ "std::vector", - /*methodName=*/ "getAllOutQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - const auto& outQubits = $_op.getOutQubits(); - const auto& controls = $_op.getAllCtrlOutQubits(); - std::vector operands{}; - operands.reserve(outQubits.size() + controls.size()); - operands.insert(operands.end(), outQubits.begin(), outQubits.end()); - operands.insert(operands.end(), controls.begin(), controls.end()); - return operands; - }]>, - InterfaceMethod< - /*desc=*/ "Get params.", - /*returnType=*/ "mlir::ValueRange", - /*methodName=*/ "getParams", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return $_op.getParams(); - }]>, - InterfaceMethod< - /*desc=*/ "Get the corresponding input qubit for a given output qubit.", - /*returnType=*/ "mlir::Value", - /*methodName=*/ "getCorrespondingInput", - /*args=*/ (ins "mlir::Value":$outQubit), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - auto op = $_op; - - auto inQubits = op.getAllInQubits(); - auto outQubits = op.getAllOutQubits(); - - auto it = llvm::find(outQubits, outQubit); - assert(it != outQubits.end() && "output qubit not found in operation"); - size_t index = std::distance(outQubits.begin(), it); - return inQubits[index]; - }]>, - InterfaceMethod< - /*desc=*/ "Get the corresponding output qubit for a given input qubit.", - /*returnType=*/ "mlir::Value", - /*methodName=*/ "getCorrespondingOutput", - /*args=*/ (ins "mlir::Value":$inQubit), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - auto op = $_op; - - auto inQubits = op.getAllInQubits(); - auto outQubits = op.getAllOutQubits(); - - auto it = llvm::find(inQubits, inQubit); - assert(it != inQubits.end() && "input qubit not found in operation"); - size_t index = std::distance(inQubits.begin(), it); - return outQubits[index]; - }]>, - InterfaceMethod< - /*desc=*/ "Returns the name of the gate.", - /*returnType=*/ "llvm::StringRef", - /*methodName=*/ "getIdentifier", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return $_op->getName().getStringRef().split('.').second; - }]> - ]; - - let verify = [{ - auto unitaryOp = mlir::cast($_op); - if (unitaryOp.getInQubits().size() != unitaryOp.getOutQubits().size()) { - return $_op->emitError() << - "number of input qubits (" << unitaryOp.getInQubits().size() << ") " - << "and output qubits (" << unitaryOp.getOutQubits().size() << ") must be the same"; - } else if (unitaryOp.getPosCtrlInQubits().size() != unitaryOp.getPosCtrlOutQubits().size()) { - return $_op->emitError() << - "number of positively-controlling input qubits (" << unitaryOp.getPosCtrlInQubits().size() << ") " - << "and positively-controlling output qubits (" << unitaryOp.getPosCtrlOutQubits().size() - << ") must be the same"; - } else if (unitaryOp.getNegCtrlInQubits().size() != unitaryOp.getNegCtrlOutQubits().size()) { - return $_op->emitError() << - "number of negatively-controlling input qubits (" << unitaryOp.getNegCtrlInQubits().size() << ") " - << "and negatively-controlling output qubits (" << unitaryOp.getNegCtrlOutQubits().size() - << ") must be the same"; - } - return mlir::success(); - }]; -} - -#endif // MQTOPT_INTERFACES diff --git a/mlir/include/mlir/Dialect/MQTOpt/IR/MQTOptOps.td b/mlir/include/mlir/Dialect/MQTOpt/IR/MQTOptOps.td deleted file mode 100644 index 1428bdfe3e..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/IR/MQTOptOps.td +++ /dev/null @@ -1,225 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -#ifndef MQTOPT_OPS -#define MQTOPT_OPS - -include "mlir/IR/BuiltinTypeInterfaces.td" -include "mlir/IR/DialectBase.td" -include "mlir/IR/EnumAttr.td" -include "mlir/IR/OpBase.td" -include "mlir/Interfaces/InferTypeOpInterface.td" -include "mlir/Interfaces/SideEffectInterfaces.td" - -include "mlir/Dialect/Common/IR/CommonTraits.td" -include "mlir/Dialect/MQTOpt/IR/MQTOptInterfaces.td" - -//===----------------------------------------------------------------------===// -// Dialect -//===----------------------------------------------------------------------===// - -def MQTOptDialect : Dialect { - // The dialect name used in the MLIR file to prefix operations. - let name = "mqtopt"; - - let summary = "The MQT optimization (`mqtopt`) dialect."; - - let description = [{ - This dialect is using value semantics for qubits. This means that - variables of type qubit and the elements of qubit registers do not - identify a physical, they rather refer to the state of a qubit. A qubit - state can be assigned only once and can also be used only once. - Otherwise, the no-cloning theorem would be violated. - - The first requirement is enforced by the SSA form of MLIR. The second - requirement is not explicitly enforced. However, when converting the - input dialect mqt that is using reference semantics to the mqtopt dialect, - qubit state values will only be used once by construction. This is also - the reason why the mqtopt dialect is not meant to be used by hand, but - rather as an intermediate representation for optimization passes. - - Due to the value semantics, the mqtopt dialect allows dataflow analysis - and optimizations. Those are very common in classical compiler - optimizations. Hence, the existing classical compiler optimizations can - easier be reused for quantum programs. - - For more information, see the paper ["QIRO: A Static Single Assignment - based Quantum Program Representation for Optimization"](https://doi.org/10.1145/3491247). - - The `mqtopt` dialect supports dynamic as well as static qubit - addressing. - }]; - - // The C++ namespace that the dialect, and all sub-components, get placed - // in. Here, `mlir::` must either appear at the front or not at all. - let cppNamespace = "::mqt::ir::opt"; - - // This dialect defines its own types for qubits and qubit registers. To - // parse and print these, respective hooks must be defined. With this flag - // set to 1, the default hooks are created and used. - let useDefaultTypePrinterParser = 1; -} - -//===----------------------------------------------------------------------===// -// Types -//===----------------------------------------------------------------------===// - -class MQTOptType traits = []> - : TypeDef { - let mnemonic = typeMnemonic; -} - -def QubitType : MQTOptType<"Qubit", "Qubit", [MemRefElementTypeInterface]> { - let summary = "value-semantic qubit"; - - let description = [{ - The value-semantic qubit corresponds to a state. - It can be static or dynamic. - }]; -} - -//===----------------------------------------------------------------------===// -// Operations -//===----------------------------------------------------------------------===// - -// This is the base class for all operations in the MQTOpt dialect. It is a -// template that takes the operation mnemonic and a list of traits. The MQTOptOp -// class is a subclass of the Op class defined in the MLIR core. -class MQTOptOp traits = []> : - Op; - -class GateOp traits = [NoMemoryEffect]> : - MQTOptOp { -} - -class UnitaryOp traits = []> : - GateOp { - let arguments = (ins - OptionalAttr:$static_params, - OptionalAttr:$params_mask, - Variadic:$params, - Variadic:$in_qubits, - Variadic:$pos_ctrl_in_qubits, - Variadic:$neg_ctrl_in_qubits - ); - - let results = (outs - Variadic:$out_qubits, - Variadic:$pos_ctrl_out_qubits, - Variadic:$neg_ctrl_out_qubits - ); - - let assemblyFormat = [{ - `(` custom($params, $static_params, $params_mask) `)` - attr-dict - ( $in_qubits^ )? ( `ctrl` $pos_ctrl_in_qubits^ )? ( `nctrl` $neg_ctrl_in_qubits^ )? - custom(type($out_qubits), type($pos_ctrl_out_qubits), type($neg_ctrl_out_qubits)) - }]; -} - -include "mlir/Dialect/MQTOpt/IR/MQTOptStdOps.td" - -def MeasureOp : GateOp<"measure", []> { - let summary = "A measure operation"; - - let description = [{ - This class represents a measure operation. - It takes a single qubit as input and returns a qubit and a bit. - After the measurement, the returned qubit is in state "0" or "1". - The value of the state is indicated by the returned bit. - - Example: - ```mlir - %q_1, %0 = mqtopt.measure q_0 - ``` - }]; - - let arguments = (ins QubitType:$in_qubit); - let results = (outs - QubitType:$out_qubit, - I1:$out_bit - ); - let assemblyFormat = "$in_qubit attr-dict"; -} - -def ResetOp : GateOp<"reset", []> { - let summary = "A reset operation"; - - let description = [{ - This class represents a reset operation. - It takes a single qubit as input and returns the same qubit after its state was reset to "0". - - Example: - ```mlir - %q_1 = mqtopt.reset %q_0 - ``` - }]; - - let arguments = (ins QubitType:$in_qubit); - let results = (outs QubitType:$out_qubit); - let assemblyFormat = "$in_qubit attr-dict"; -} - -class ResourceOp traits = []> : MQTOptOp; - -def AllocQubitOp : ResourceOp<"allocQubit"> { - let summary = "Allocates a single qubit"; - - let description = [{ - Allocates a single qubit in the "0" state. - The qubit can be used in operations after allocation. - It must be deallocated with the `deallocQubit` operation. - - Example: - ```mlir - %q = mqtopt.allocQubit - ``` - }]; - - let arguments = (ins); - let results = (outs QubitType:$qubit); - let assemblyFormat = "attr-dict"; -} - -def DeallocQubitOp : ResourceOp<"deallocQubit"> { - let summary = "Deallocates a single qubit"; - - let description = [{ - Deallocates a single qubit that was previously allocated with the - `allocQubit` operation. After this operation, the qubit is no longer - valid and cannot be used in further operations. - - Example: - ```mlir - mqtopt.deallocQubit %q - ``` - }]; - - let arguments = (ins QubitType:$qubit); - let results = (outs); - let assemblyFormat = "$qubit attr-dict"; -} - -def QubitOp : ResourceOp<"qubit"> { - let summary = "Retrieve static qubit"; - let description = [{ - The `mqtopt.qubit` operation produces an SSA value from the given index - to a static (hardware) qubit. - - Example: - ```mlir - %q = mqtopt.qubit 0 - ``` - }]; - - let arguments = (ins ConfinedAttr:$index); - let results = (outs QubitType:$qubit); - let assemblyFormat = "$index attr-dict"; -} - -#endif // MQTOPT_OPS diff --git a/mlir/include/mlir/Dialect/MQTOpt/IR/WireIterator.h b/mlir/include/mlir/Dialect/MQTOpt/IR/WireIterator.h deleted file mode 100644 index 90aeea5704..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/IR/WireIterator.h +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -/** - * @brief A bidirectional_iterator traversing the def-use chain of a qubit wire. - * - * The iterator follows the flow of a qubit through a sequence of quantum - * operations in a given region. It respects the semantics of the respective - * quantum operation including control flow constructs (scf::ForOp and - * scf::IfOp). - * - * It treats control flow constructs as a single operation that consumes and - * yields a corresponding number of qubits, without descending into their nested - * regions. - */ -class WireIterator { - /// @returns a view of all input qubits. - [[nodiscard]] static auto getAllInQubits(UnitaryInterface op) { - return llvm::concat(op.getInQubits(), op.getPosCtrlInQubits(), - op.getNegCtrlInQubits()); - } - - /// @returns a view of all output qubits. - [[nodiscard]] static auto getAllOutQubits(UnitaryInterface op) { - return llvm::concat( - op.getOutQubits(), op.getPosCtrlOutQubits(), op.getNegCtrlOutQubits()); - } - - /** - * @brief Find corresponding output from input value for a unitary (Forward). - * - * @note That we don't use the interface method here because - * it creates temporary std::vectors instead of using views. - */ - [[nodiscard]] static mlir::Value findOutput(UnitaryInterface op, - mlir::Value in) { - const auto ins = getAllInQubits(op); - const auto outs = getAllOutQubits(op); - const auto it = llvm::find(ins, in); - assert(it != ins.end() && "input qubit not found in operation"); - const auto index = std::distance(ins.begin(), it); - return *(std::next(outs.begin(), index)); - } - - /** - * @brief Find corresponding input from output value for a unitary (Backward). - * - * @note That we don't use the interface method here because - * it creates temporary std::vectors instead of using views. - */ - [[nodiscard]] static mlir::Value findInput(UnitaryInterface op, - mlir::Value out) { - const auto ins = getAllInQubits(op); - const auto outs = getAllOutQubits(op); - const auto it = llvm::find(outs, out); - assert(it != outs.end() && "output qubit not found in operation"); - const auto index = std::distance(outs.begin(), it); - return *(std::next(ins.begin(), index)); - } - - /** - * @brief Find corresponding result from init argument value (Forward). - */ - [[nodiscard]] static mlir::Value findResult(mlir::scf::ForOp op, - mlir::Value initArg) { - const auto initArgs = op.getInitArgs(); - const auto it = llvm::find(initArgs, initArg); - assert(it != initArgs.end() && "init arg qubit not found in operation"); - const auto index = std::distance(initArgs.begin(), it); - return op->getResult(index); - } - - /** - * @brief Find corresponding init argument from result value (Backward). - */ - [[nodiscard]] static mlir::Value findInitArg(mlir::scf::ForOp op, - mlir::Value res) { - return op.getInitArgs()[cast(res).getResultNumber()]; - } - - /** - * @brief Find corresponding result value from input qubit value (Forward). - * - * @details Recursively traverses the IR "downwards" until the respective - * yield is found. Requires that each branch takes and returns the same - * (possibly modified) qubits. Hence, we can just traverse the then-branch. - */ - [[nodiscard]] static mlir::Value findResult(mlir::scf::IfOp op, - mlir::Value q) { - /// Use the branch with fewer ops. - /// Note: LLVM doesn't guarantee that range_size is in O(1). - /// Might effect performance. - const auto szThen = llvm::range_size(op.getThenRegion().getOps()); - const auto szElse = llvm::range_size(op.getElseRegion().getOps()); - mlir::Region& region = - szElse >= szThen ? op.getThenRegion() : op.getElseRegion(); - - WireIterator it(q, ®ion); - - /// Assumptions: - /// First, there must be a yield. - /// Second, yield is a sentinel. - /// Then: Advance until the yield before the sentinel. - - it = std::prev(std::ranges::next(it, std::default_sentinel)); - assert(isa(*it) && "expected yield op"); - auto yield = cast(*it); - - /// Get the corresponding result. - - const auto results = yield.getResults(); - const auto yieldIt = llvm::find(results, it.q); - assert(yieldIt != results.end() && "yielded qubit not found in operation"); - const auto index = std::distance(results.begin(), yieldIt); - return op->getResult(index); - } - - /** - * @brief Find the first value outside the branch region for a given result - * value (Backward). - * - * @details Recursively traverses the IR "upwards" until a value outside the - * branch region is found. If the iterator's operation does not change during - * backward traversal, it indicates that the def-use chain starts within the - * branch region and does not extend into the parent region. - */ - [[nodiscard]] static mlir::Value findValue(mlir::scf::IfOp op, - mlir::Value q) { - const auto num = cast(q).getResultNumber(); - mlir::Operation* term = op.thenBlock()->getTerminator(); - mlir::scf::YieldOp yield = llvm::cast(term); - mlir::Value v = yield.getResults()[num]; - assert(v != nullptr && "expected yielded value"); - - mlir::Operation* prev{}; - WireIterator it(v, &op.getThenRegion()); - while (it.qubit().getParentRegion() != op->getParentRegion()) { - /// Since the definingOp of q might be a nullptr (BlockArgument), don't - /// immediately dereference the iterator here. - mlir::Operation* curr = it.qubit().getDefiningOp(); - if (curr == prev || curr == nullptr) { - break; - } - prev = *it; - --it; - } - - return it.qubit(); - } - - /** - * @brief Return the first user of a value in a given region. - * @param v The value. - * @param region The targeted region. - * @return A pointer to the user, or nullptr if none exists. - */ - [[nodiscard]] static mlir::Operation* getUserInRegion(mlir::Value v, - mlir::Region* region) { - for (mlir::Operation* user : v.getUsers()) { - if (user->getParentRegion() == region) { - return user; - } - } - return nullptr; - } - -public: - using iterator_category = std::bidirectional_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = mlir::Operation*; - - explicit WireIterator() = default; - explicit WireIterator(mlir::Value q, mlir::Region* region) - : currOp(q.getDefiningOp()), q(q), region(region) {} - - [[nodiscard]] mlir::Operation* operator*() const { - assert(!sentinel && "Dereferencing sentinel iterator"); - assert(currOp && "Dereferencing null operation"); - return currOp; - } - - [[nodiscard]] mlir::Value qubit() const { return q; } - - WireIterator& operator++() { - advanceForward(); - return *this; - } - - WireIterator operator++(int) { - auto tmp = *this; - ++*this; - return tmp; - } - - WireIterator& operator--() { - advanceBackward(); - return *this; - } - - WireIterator operator--(int) { - auto tmp = *this; - --*this; - return tmp; - } - - bool operator==(const WireIterator& other) const { - return other.q == q && other.currOp == currOp && other.sentinel == sentinel; - } - - bool operator==([[maybe_unused]] std::default_sentinel_t s) const { - return sentinel; - } - -private: - void advanceForward() { - /// If we are already at the sentinel, there is nothing to do. - if (sentinel) { - return; - } - - /// Find output from input qubit. - /// If there is no output qubit, set `sentinel` to true. - if (q.getDefiningOp() != currOp) { - mlir::TypeSwitch(currOp) - .Case( - [&](UnitaryInterface op) { q = findOutput(op, q); }) - .Case([&](ResetOp op) { q = op.getOutQubit(); }) - .Case([&](MeasureOp op) { q = op.getOutQubit(); }) - .Case( - [&](mlir::scf::ForOp op) { q = findResult(op, q); }) - .Case( - [&](mlir::scf::IfOp op) { q = findResult(op, q); }) - .Case( - [&](auto) { sentinel = true; }) - .Default([&](mlir::Operation* op) { - report_fatal_error("unknown op in def-use chain: " + - op->getName().getStringRef()); - }); - } - - /// Find the next operation. - /// If it is a sentinel there are no more ops. - if (sentinel) { - return; - } - - /// If there are no more uses, set `sentinel` to true. - if (q.use_empty()) { - sentinel = true; - return; - } - - /// Otherwise, search the user in the targeted region. - currOp = getUserInRegion(q, getRegion()); - if (currOp == nullptr) { - /// Since !q.use_empty: must be a branching op. - currOp = q.getUsers().begin()->getParentOp(); - /// For now, just check if it's a scf::IfOp. - /// Theoretically this could also be an scf::IndexSwitch, etc. - assert(isa(currOp)); - } - } - - void advanceBackward() { - /// If we are at the sentinel and move backwards, "revive" the - /// qubit value and operation. - if (sentinel) { - sentinel = false; - return; - } - - /// Get the operation that produces the qubit value. - currOp = q.getDefiningOp(); - - /// If q is a BlockArgument (no defining op), hold. - if (currOp == nullptr) { - return; - } - - /// Find input from output qubit. - /// If there is no input qubit, hold. - mlir::TypeSwitch(currOp) - .Case( - [&](UnitaryInterface op) { q = findInput(op, q); }) - .Case([&](auto op) { q = op.getInQubit(); }) - .Case([&](DeallocQubitOp op) { q = op.getQubit(); }) - .Case( - [&](mlir::scf::ForOp op) { q = findInitArg(op, q); }) - .Case( - [&](mlir::scf::IfOp op) { q = findValue(op, q); }) - .Case([&](auto) { /* hold (no-op) */ }) - .Default([&](mlir::Operation* op) { - report_fatal_error("unknown op in def-use chain: " + - op->getName().getStringRef()); - }); - } - - /** - * @brief Return the active region this iterator uses. - * @return A pointer to the region. - */ - [[nodiscard]] mlir::Region* getRegion() { - return region != nullptr ? region : q.getParentRegion(); - } - - mlir::Operation* currOp{}; - mlir::Value q; - mlir::Region* region{}; - bool sentinel{false}; -}; - -static_assert(std::bidirectional_iterator); -static_assert(std::sentinel_for, - "std::default_sentinel_t must be a sentinel for WireIterator."); -} // namespace mqt::ir::opt diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/CMakeLists.txt b/mlir/include/mlir/Dialect/MQTOpt/Transforms/CMakeLists.txt deleted file mode 100644 index e09bd1b4f3..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -set(LLVM_TARGET_DEFINITIONS Passes.td) -mlir_tablegen(Passes.h.inc -gen-pass-decls -name MQTOpt) -add_public_tablegen_target(MLIRMQTOptTransformsIncGen) -add_mlir_doc(Passes MLIRMQTOptPasses Passes/ -gen-pass-doc) diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/LiftMeasurementsPasses.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/LiftMeasurementsPasses.h deleted file mode 100644 index 78a28849eb..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/LiftMeasurementsPasses.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -namespace mlir { - -class PatternRewriter; - -} // namespace mlir - -namespace mqt::ir::opt { - -class UnitaryInterface; -class MeasureOp; - -/** - * @brief Moves a measurement before the given gate. - * @param gate The UnitaryInterface gate to swap with the measurement. - * @param measurement The MeasureOp measurement to swap with the gate. - * @param rewriter The pattern rewriter to use for the swap operation. - */ -void swapGateWithMeasurement(UnitaryInterface gate, MeasureOp measurement, - mlir::PatternRewriter& rewriter); -} // namespace mqt::ir::opt diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.h deleted file mode 100644 index 49ec232de1..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" - -#include -#include -#include - -namespace qc { -class QuantumComputation; -} - -namespace mlir { - -class RewritePatternSet; - -} // namespace mlir - -namespace mqt::ir::opt { - -enum class PlacementStrategy : std::uint8_t { Random, Identity }; - -#define GEN_PASS_DECL -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" // IWYU pragma: export - -void populateGateEliminationPatterns(mlir::RewritePatternSet& patterns); -void populateMergeRotationGatesPatterns(mlir::RewritePatternSet& patterns); -void populateSwapReconstructionAndElisionPatterns( - mlir::RewritePatternSet& patterns); -void populateQuantumSinkShiftPatterns(mlir::RewritePatternSet& patterns); -void populateQuantumSinkPushPatterns(mlir::RewritePatternSet& patterns); -void populateLiftMeasurementsAboveControlsPatterns( - mlir::RewritePatternSet& patterns); -void populateReplaceBasisStateControlsWithIfPatterns( - mlir::RewritePatternSet& patterns); -void populateLiftMeasurementsAboveGatesPatterns( - mlir::RewritePatternSet& patterns); -void populateDeadGateEliminationPatterns(mlir::RewritePatternSet& patterns); -void populateReuseQubitsPatterns(mlir::RewritePatternSet& patterns); -void populateToQuantumComputationPatterns(mlir::RewritePatternSet& patterns, - qc::QuantumComputation& circuit); -void populateFromQuantumComputationPatterns(mlir::RewritePatternSet& patterns, - qc::QuantumComputation& circuit); - -//===----------------------------------------------------------------------===// -// Registration -//===----------------------------------------------------------------------===// - -/// Generate the code for registering passes. -#define GEN_PASS_REGISTRATION -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" // IWYU pragma: export -} // namespace mqt::ir::opt diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td deleted file mode 100644 index 18a13e8f1d..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -#ifndef MQTO_PASSES -#define MQTO_PASSES - -include "mlir/Pass/PassBase.td" - -//===----------------------------------------------------------------------===// -// Round-Trip Passes -//===----------------------------------------------------------------------===// - -def MQTCoreRoundTrip : Pass<"mqt-core-round-trip", "mlir::ModuleOp"> { - let summary = "This pass performs a round trip to MQT Core's QuantumComputation and back"; - let description = [{ - This pass starts by extracting all quantum-computation-relevant operations and then translates them into an MQT Core `QuantumComputation`, - deleting all quantum operations in the process. - Then, the quantum computation is read in a second pattern and used to generate new MLIR code from it. - - This pass makes the following assumptions: - - Each module consists of a single function that uses `mqtopt` operations with just a single qubit register. - - All qubits are measured exactly once and the boolean measurement results are returned from the function in order. - - The first returned value is the `AllocOp` that constructs a qubit register. - - Unitary operations may also use any number of (positive) controls. - }]; -} - -//===----------------------------------------------------------------------===// -// Optimization Passes -//===----------------------------------------------------------------------===// - -def GateElimination : Pass<"gate-elimination", "mlir::ModuleOp"> { - let summary = "This pass searches for consecutive applications of gates and their inverses and cancels them."; - let description = [{ - This pass searches for applications of gates that are their own inverses. Walking down their def-use chain, - it then checks if the same gate is applied once again. In that case, the two gates are cancelled. - Additionally, all occurrences of the identity gate are removed. - }]; -} - -def MergeRotationGates : Pass<"merge-rotation-gates", "mlir::ModuleOp"> { - let summary = "This pass searches for consecutive applications of rotation gates that can be merged."; - let description = [{ - Consecutive applications of gphase, `p`, `rx`, `ry`, `rz`, `rxx`, `ryy`, `rzz`, and `rzx` are merged into one by adding their angles. - The merged gate is currently not removed if the angles add up to zero. - - This pass currently does not affect `xx_minus_yy`, `xx_plus_yy`, `u`, and `u2`. - }]; -} - -def SwapReconstructionAndElision : Pass<"swap-reconstruction-and-elision", "mlir::ModuleOp"> { - let summary = "Reconstruct SWAPs from eligible CNOT pairs and eliminate SWAPs by applying the implied qubit permutation."; - let description = [{ - Eliminates all uncontrolled `swap` operations by: - 1. Reconstructing explicit `swap` ops from eligible CNOT patterns (canonical 3–CNOT form or two alternating CNOT pairs). - 2. Replacing remaining `swap` ops by applying the induced qubit permutation to subsequent uses. - - Notes: - - No connectivity or coupling constraints are preserved. - - Controlled `swap` operations (if ever introduced) are not touched. - - Example (reconstruction): - ``` - ┌───┐ ┌───┐ - ──■──┤ X ├ ──■──┤ X ├──■────■── ──╳────■── - ┌─┴─┐└─┬─┘ => ┌─┴─┐└─┬─┘┌─┴─┐┌─┴─┐ => | ┌─┴─┐ - ┤ X ├──■── ┤ X ├──■──┤ X ├┤ X ├ ──╳──┤ X ├ - └───┘ └───┘ └───┘└───┘ └───┘ - ``` - - Example (elision): - ``` - ┌───┐ ┌───┐ - q0 ──╳────■── q0 q0 ──┤ X ├──╳── q0 q0 ──┤ X ├─ q1 - | ┌─┴─┐ => └─┬─┘ | => └─┬─┘ - q1 ──╳──┤ X ├ q1 q1 ────■────╳── q1 q1 ────■─── q0 - └───┘ - ``` - }]; -} - -def QuantumSinkPass : Pass<"quantum-sink", "mlir::ModuleOp"> { - let summary = "This pass attempts to push down operations into branches for possible optimizations."; - let description = [{ - This pass searches for branch instructions and attempts to push instructions from previous branches into them. - }]; -} - -def LiftMeasurementsPass : Pass<"lift-measurements", "mlir::ModuleOp"> { - let dependentDialects = [ "mlir::arith::ArithDialect", "mlir::scf::SCFDialect", "mqt::ir::opt::MQTOptDialect" ]; - let summary = "This pass attempts to lift measurements as much as possible by replacing quantum operations with classical operations where possible."; - let description = [{ - This pass consists of several patterns that attempt to push different structures of quantum gates below measurements. - }]; -} - -def ReuseQubitsPass : Pass<"reuse-qubits", "mlir::ModuleOp"> { - let dependentDialects = [ "mlir::scf::SCFDialect", "mqt::ir::opt::MQTOptDialect" ]; - let summary = "Reduce the number of required qubits by reusing existing ones that are no longer used."; - let description = [{ - This pass performs qubit reuse, searching for qubits that are first referenced after the final reference to a different qubit. - In these cases, rather than using a new qubit, the old qubit can be reset and reused. - }]; -} - -//===----------------------------------------------------------------------===// -// Transpilation Passes For Superconducting Devices -//===----------------------------------------------------------------------===// - -def PlacementPassSC : Pass<"placement-sc", "mlir::ModuleOp"> { - let summary = "This pass maps program qubits to hardware qubits on superconducting quantum devices using initial placement strategies."; - let options = [ - Option<"strategy", "strategy", "PlacementStrategy", "PlacementStrategy::Random", - "The initial placement strategy to use.", [{llvm::cl::values( - clEnumValN(PlacementStrategy::Random, "random", "Random placement"), - clEnumValN(PlacementStrategy::Identity, "identity", "Identity placement"))}]>, - Option<"archName", "arch", "std::string", "", - "The name of the targeted architecture.">, - ]; -} - -def NaiveRoutingPassSC : Pass<"route-naive-sc", "mlir::ModuleOp"> { - let summary = "This pass ensures that all two-qubit gates are executable on the target architecture."; - let description = [{ - Simple pre-order traversal of the IR that routes any non-executable gates by inserting SWAPs along the shortest path. - }]; - let options = [ - Option<"archName", "arch", "std::string", "", - "The name of the targeted architecture.">, - ]; - let statistics = [ - Statistic<"numSwaps", "num-additional-swaps", "The number of additional SWAPs"> - ]; -} - -def AStarRoutingPassSC : Pass<"route-astar-sc", "mlir::ModuleOp"> { - let summary = "This pass ensures that all two-qubit gates are executable on the target architecture."; - let description = [{ - Routes the program by dividing the circuit into layers of parallel two-qubit gates and iteratively searches and - inserts SWAPs for each layer using A*-search. - }]; - let options = [ - Option<"archName", "arch", "std::string", "", - "The name of the targeted architecture.">, - Option<"nlookahead", "nlookahead", "std::size_t", "1", - "astar option: Number of lookahead steps (heuristic horizon)">, - Option<"alpha", "alpha", "float", "1.0F", - "astar option: The alpha factor in the cost function">, - Option<"lambda", "lambda", "float", "0.5F", - "astar option: The lambda factor in the cost function"> - ]; - let statistics = [ - Statistic<"numSwaps", "num-additional-swaps", "The number of additional SWAPs"> - ]; -} - -def RoutingVerificationSCPass : Pass<"verify-routing-sc", "mlir::ModuleOp"> { - let summary = "This pass verifies that all two-qubit gates are executable on the target architecture."; - let description = [{ - This pass ensures that all two-qubit gates are executable on the target's architecture. - }]; - let options = [ - Option<"archName", "arch", "std::string", "", - "The name of the targeted architecture."> - ]; -} - -#endif // MQTO_PASSES diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Architecture.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Architecture.h deleted file mode 100644 index 537b2c2544..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Architecture.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -/** - * @brief A quantum accelerator's architecture. - * @details Computes all-shortest paths at construction. - */ -class Architecture { -public: - using CouplingSet = mlir::DenseSet>; - using NeighbourVector = mlir::SmallVector>; - - explicit Architecture(std::string name, std::size_t nqubits, - CouplingSet couplingSet) - : name_(std::move(name)), nqubits_(nqubits), - couplingSet_(std::move(couplingSet)), neighbours_(nqubits), - dist_(nqubits, llvm::SmallVector(nqubits, UINT64_MAX)), - prev_(nqubits, llvm::SmallVector(nqubits, UINT64_MAX)) { - floydWarshallWithPathReconstruction(); - collectNeighbours(); - } - - /** - * @brief Return the architecture's name. - */ - [[nodiscard]] constexpr std::string_view name() const { return name_; } - - /** - * @brief Return the architecture's number of qubits. - */ - [[nodiscard]] constexpr std::size_t nqubits() const { return nqubits_; } - - /** - * @brief Return true if @p u and @p v are adjacent. - */ - [[nodiscard]] bool areAdjacent(uint32_t u, uint32_t v) const { - return couplingSet_.contains({u, v}); - } - - /** - * @brief Collect the shortest SWAP sequence to make @p u and @p v adjacent. - * @returns The SWAP sequence from the destination (v) to source (u) qubit. - */ - [[nodiscard]] llvm::SmallVector> - shortestSWAPsBetween(uint32_t u, uint32_t v) const; - - /** - * @brief Return the length of the shortest path between @p u and @p v. - */ - [[nodiscard]] std::size_t distanceBetween(uint32_t u, uint32_t v) const; - - /** - * @brief Collect all neighbours of @p u. - */ - [[nodiscard]] llvm::SmallVector neighboursOf(uint32_t u) const; - - /** - * @brief Validate if a two-qubit op is executable on the architecture for a - * given layout. - */ - [[nodiscard]] bool isExecutable(UnitaryInterface op, - const Layout& layout) const; - -private: - using Matrix = llvm::SmallVector>; - - /** - * @brief Find all shortest paths in the coupling map between two qubits. - * @details Vertices are the qubits. Edges connected two qubits. Has a time - * and memory complexity of O(nqubits^3) and O(nqubits^2), respectively. - * @link Adapted from https://en.wikipedia.org/wiki/Floyd–Warshall_algorithm - */ - void floydWarshallWithPathReconstruction(); - - /** - * @brief Collect the neighbours of all qubits. - * @details Has a time complexity of O(nqubits) - */ - void collectNeighbours(); - - std::string name_; - std::size_t nqubits_; - CouplingSet couplingSet_; - NeighbourVector neighbours_; - - Matrix dist_; - Matrix prev_; -}; - -/** - * @brief Get architecture by its name. - */ -std::unique_ptr getArchitecture(llvm::StringRef name); - -}; // namespace mqt::ir::opt diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h deleted file mode 100644 index ae537e76fe..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -/** - * @brief A pair of SSA Values. - */ -using ValuePair = std::pair; - -/** - * @brief Represents a pair of qubit indices. - */ -using QubitIndexPair = std::pair; - -/** - * @brief Return true if the function contains "entry_point" in the passthrough - * attribute. - */ -[[nodiscard]] bool isEntryPoint(mlir::func::FuncOp op); - -/** - * @brief Check if a unitary acts on two qubits. - * @param u A unitary. - * @returns True iff the qubit gate acts on two qubits. - */ -[[nodiscard]] bool isTwoQubitGate(UnitaryInterface op); - -/** - * @brief Return input qubit pair for a two-qubit unitary. - * @param op A two-qubit unitary. - * @return Pair of SSA values consisting of the first and second in-qubits. - */ -[[nodiscard]] ValuePair getIns(UnitaryInterface op); - -/** - * @brief Return output qubit pair for a two-qubit unitary. - * @param op A two-qubit unitary. - * @return Pair of SSA values consisting of the first and second out-qubits. - */ -[[nodiscard]] ValuePair getOuts(UnitaryInterface op); - -/** - * @brief Return the first user of a value in a given region. - * @param v The value. - * @param region The targeted region. - * @return A pointer to the user, or nullptr if non exists. - */ -[[nodiscard]] mlir::Operation* getUserInRegion(mlir::Value v, - mlir::Region* region); - -/** - * @brief Create and return SWAPOp for two qubits. - * - * Expects the rewriter to be set to the correct position. - * - * @param location The Location to attach to the created op. - * @param in0 First input qubit SSA value. - * @param in1 Second input qubit SSA value. - * @param rewriter A PatternRewriter. - * @return The created SWAPOp. - */ -[[nodiscard]] SWAPOp createSwap(mlir::Location location, mlir::Value in0, - mlir::Value in1, - mlir::PatternRewriter& rewriter); - -/** - * @brief Replace all uses of a value within a region and its nested regions, - * except for a specific operation. - * - * @param oldValue The value to replace. - * @param newValue The new value to use. - * @param region The region in which to perform replacements. - * @param exceptOp Operation to exclude from replacements. - * @param rewriter The pattern rewriter. - */ -void replaceAllUsesInRegionAndChildrenExcept(mlir::Value oldValue, - mlir::Value newValue, - mlir::Region* region, - mlir::Operation* exceptOp, - mlir::PatternRewriter& rewriter); - -/** - * @brief Insert SWAP ops at the rewriter's insertion point. - * - * @param loc The location of the inserted SWAP ops. - * @param swaps A range of hardware indices for the SWAPs. - * @param layout The current layout. - * @param rewriter The pattern rewriter. - */ -template - requires std::same_as, QubitIndexPair> -void insertSWAPs(mlir::Location loc, Range&& swaps, Layout& layout, - mlir::PatternRewriter& rewriter) { - for (const auto [hw0, hw1] : std::forward(swaps)) { - const mlir::Value in0 = layout.lookupHardwareValue(hw0); - const mlir::Value in1 = layout.lookupHardwareValue(hw1); - - auto swap = createSwap(loc, in0, in1, rewriter); - - rewriter.setInsertionPointAfter(swap); - - mlir::Region* region = swap->getParentRegion(); - mlir::Value out0 = swap.getOutQubits()[0]; - mlir::Value out1 = swap.getOutQubits()[1]; - - replaceAllUsesInRegionAndChildrenExcept(in0, out1, region, swap, rewriter); - replaceAllUsesInRegionAndChildrenExcept(in1, out0, region, swap, rewriter); - - layout.remap(swap); - } -} -} // namespace mqt::ir::opt diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.h deleted file mode 100644 index df74971d96..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Unit.h" - -#include -#include -#include -#include - -namespace mqt::ir::opt { - -struct Layer { - /// @brief All (zero, one, two-qubit) ops contained inside this layer. - mlir::SmallVector ops; - /// @brief The program index pairs of all two-qubit ops. - mlir::SmallVector twoQubitProgs; - /// @brief The first op in ops in textual IR order. - mlir::Operation* anchor{}; - - /// @brief Add op to ops and reset anchor if necessary. - void addOp(mlir::Operation* op) { - ops.emplace_back(op); - if (anchor == nullptr || op->isBeforeInBlock(anchor)) { - anchor = op; - } - } - /// @returns true iff. there are no ops in this layer. - [[nodiscard]] bool hasZeroOps() const { return ops.empty(); } - /// @returns true iff. there are no two-qubit ops in this layer. - [[nodiscard]] bool hasZero2QOps() const { return twoQubitProgs.empty(); } -}; - -/// @brief A LayeredUnit traverses a program layer-by-layer. -class LayeredUnit : public Unit { -public: - [[nodiscard]] static LayeredUnit - fromEntryPointFunction(mlir::func::FuncOp func, std::size_t nqubits); - - LayeredUnit(Layout layout, mlir::Region* region); - - [[nodiscard]] const Layer& operator[](std::size_t i) const { - return layers_[i]; - } - [[nodiscard]] std::size_t size() const { return layers_.size(); } - -#ifndef NDEBUG - LLVM_DUMP_METHOD void dump(llvm::raw_ostream& os = llvm::dbgs()) const; -#endif - -private: - friend class Unit; - using Layers = mlir::SmallVector; - using const_iterator = Layers::const_iterator; - - [[nodiscard]] mlir::SmallVector nextImpl(); - [[nodiscard]] const_iterator beginImpl() const { return layers_.begin(); } - [[nodiscard]] const_iterator endImpl() const { return layers_.end(); } - - Layers layers_; -}; -} // namespace mqt::ir::opt diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h deleted file mode 100644 index dfe29824d9..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h +++ /dev/null @@ -1,380 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -/** - * @brief A qubit layout that maps program and hardware indices without storing - * Values. Used for efficient memory usage when Value tracking isn't needed. - * - * Note that we use the terminology "hardware" and "program" qubits here, - * because "virtual" (opposed to physical) and "static" (opposed to dynamic) - * are C++ keywords. - */ -class [[nodiscard]] ThinLayout { -public: - explicit ThinLayout(const std::size_t nqubits) - : programToHardware_(nqubits), hardwareToProgram_(nqubits) {} - - /** - * @brief Insert program:hardware index mapping. - * @param prog The program index. - * @param hw The hardware index. - */ - void add(uint32_t prog, uint32_t hw) { - assert(prog < programToHardware_.size() && - "add: program index out of bounds"); - assert(hw < hardwareToProgram_.size() && - "add: hardware index out of bounds"); - programToHardware_[prog] = hw; - hardwareToProgram_[hw] = prog; - } - - /** - * @brief Look up program index for a hardware index. - * @param hw The hardware index. - * @return The program index of the respective hardware index. - */ - [[nodiscard]] uint32_t getProgramIndex(const uint32_t hw) const { - assert(hw < hardwareToProgram_.size() && - "getProgramIndex: hardware index out of bounds"); - return hardwareToProgram_[hw]; - } - - /** - * @brief Look up hardware index for a program index. - * @param prog The program index. - * @return The hardware index of the respective program index. - */ - [[nodiscard]] uint32_t getHardwareIndex(const uint32_t prog) const { - assert(prog < programToHardware_.size() && - "getHardwareIndex: program index out of bounds"); - return programToHardware_[prog]; - } - - /** - * @brief Convenience function to lookup multiple hardware indices at once. - * @param progs The program indices. - * @return A tuple of hardware indices. - */ - template - requires(sizeof...(ProgIndices) > 0) && - ((std::is_convertible_v) && ...) - [[nodiscard]] auto getHardwareIndices(ProgIndices... progs) const { - return std::tuple{getHardwareIndex(static_cast(progs))...}; - } - - /** - * @brief Convenience function to lookup multiple program indices at once. - * @param hws The hardware indices. - * @return A tuple of program indices. - */ - template - requires(sizeof...(HwIndices) > 0) && - ((std::is_convertible_v) && ...) - [[nodiscard]] auto getProgramIndices(HwIndices... hws) const { - return std::tuple{getProgramIndex(static_cast(hws))...}; - } - - /** - * @brief Swap the mapping to hardware indices of two program indices. - */ - void swap(const uint32_t prog0, const uint32_t prog1) { - const uint32_t hw0 = programToHardware_[prog0]; - const uint32_t hw1 = programToHardware_[prog1]; - - std::swap(programToHardware_[prog0], programToHardware_[prog1]); - std::swap(hardwareToProgram_[hw0], hardwareToProgram_[hw1]); - } - - /** - * @returns the number of qubits handled by the layout. - */ - [[nodiscard]] std::size_t getNumQubits() const { - return programToHardware_.size(); - } - -protected: - /** - * @brief Maps a program qubit index to its hardware index. - */ - mlir::SmallVector programToHardware_; - - /** - * @brief Maps a hardware qubit index to its program index. - */ - mlir::SmallVector hardwareToProgram_; - -private: - friend struct llvm::DenseMapInfo; -}; - -/** - * @brief Enhanced layout that extends ThinLayout with Value tracking - * capabilities. - */ -class [[nodiscard]] Layout : public ThinLayout { -public: - explicit Layout(const std::size_t nqubits) - : ThinLayout(nqubits), qubits_(nqubits) { - valueToMapping_.reserve(nqubits); - } - - /** - * @brief Insert program:hardware:value mapping. - * @param prog The program index. - * @param hw The hardware index. - * @param q The SSA value associated with the indices. - */ - void add(uint32_t prog, uint32_t hw, mlir::Value q) { - ThinLayout::add(prog, hw); - qubits_[hw] = q; - valueToMapping_.try_emplace(q, prog, hw); - } - - /** - * @brief Look up hardware index for a qubit value. - * @param q The SSA Value representing the qubit. - * @return The hardware index where this qubit currently resides. - */ - [[nodiscard]] uint32_t lookupHardwareIndex(const mlir::Value q) const { - const auto it = valueToMapping_.find(q); - assert(it != valueToMapping_.end() && "lookupHardwareIndex: unknown value"); - return it->second.hw; - } - - /** - * @brief Look up qubit value for a hardware index. - * @param hw The hardware index. - * @return The SSA value currently representing the qubit at the hardware - * location. - */ - [[nodiscard]] mlir::Value lookupHardwareValue(const uint32_t hw) const { - assert(hw < qubits_.size() && - "lookupHardwareValue: hardware index out of bounds"); - return qubits_[hw]; - } - - /** - * @brief Look up program index for a qubit value. - * @param q The SSA Value representing the qubit. - * @return The program index where this qubit currently resides. - */ - [[nodiscard]] uint32_t lookupProgramIndex(const mlir::Value q) const { - const auto it = valueToMapping_.find(q); - assert(it != valueToMapping_.end() && "lookupProgramIndex: unknown value"); - return it->second.prog; - } - - /** - * @brief Look up qubit value for a program index. - * @param prog The program index. - * @return The SSA value currently representing the qubit at the program - * location. - */ - [[nodiscard]] mlir::Value lookupProgramValue(const uint32_t prog) const { - assert(prog < this->programToHardware_.size() && - "lookupProgramValue: program index out of bounds"); - return qubits_[this->programToHardware_[prog]]; - } - - /** - * @brief Check whether the layout contains a qubit. - * @param q The SSA Value representing the qubit. - * @return True if the layout contains the qubit, false otherwise. - */ - [[nodiscard]] bool contains(const mlir::Value q) const { - return valueToMapping_.contains(q); - } - - /** - * @brief Replace an old SSA value with a new one. - */ - void remapQubitValue(const mlir::Value in, const mlir::Value out) { - const auto it = valueToMapping_.find(in); - assert(it != valueToMapping_.end() && - "remapQubitValue: unknown input value"); - - const QubitInfo info = it->second; - qubits_[info.hw] = out; - - assert(!valueToMapping_.contains(out) && - "remapQubitValue: output value already mapped"); - - valueToMapping_.try_emplace(out, info); - valueToMapping_.erase(in); - } - - /** - * @brief Swap the locations of two program qubits. This is the effect of a - * SWAP gate. - */ - void swap(const mlir::Value q0, const mlir::Value q1) { - auto it0 = valueToMapping_.find(q0); - auto it1 = valueToMapping_.find(q1); - assert(it0 != valueToMapping_.end() && it1 != valueToMapping_.end() && - "swap: unknown values"); - - const uint32_t prog0 = it0->second.prog; - const uint32_t prog1 = it1->second.prog; - - std::swap(it0->second.prog, it1->second.prog); - - ThinLayout::swap(prog0, prog1); - } - - /** - * @brief Return the current layout. - */ - mlir::ArrayRef getCurrentLayout() const { - return this->programToHardware_; - } - - /** - * @brief Return the SSA values for hardware indices from 0...nqubits. - */ - [[nodiscard]] mlir::ArrayRef getHardwareQubits() const { - return qubits_; - } - - /** - * @brief Remap all input to output qubits for the given unitary op. - * - * If the unitary op is a SWAP, exchange the respective program qubits. - * - * @param op The unitary op. - */ - void remap(UnitaryInterface op) { - if (mlir::isa(op)) { - swap(op.getInQubits()[0], op.getInQubits()[1]); - } - - for (const auto& [in, out] : - llvm::zip_equal(op.getAllInQubits(), op.getAllOutQubits())) { - remapQubitValue(in, out); - } - } - - /** - * @brief Remap input to output qubit for the given reset op. - * - * @param op The reset op. - * @param layout The current layout. - */ - void remap(ResetOp op) { remapQubitValue(op.getInQubit(), op.getOutQubit()); } - - /** - * @brief Remap input to output qubit for the given measure op. - * - * @param op The measure op. - */ - void remap(MeasureOp op) { - remapQubitValue(op.getInQubit(), op.getOutQubit()); - } - - /** - * @brief Remap input qubits to in-loop-body values (iteration args). - * - * @param op The 'scf.for' op. - */ - void remapToLoopBody(mlir::scf::ForOp op) { - const auto nqubits = getNumQubits(); - const auto args = op.getInitArgs().take_front(nqubits); - const auto iterArgs = op.getRegionIterArgs().take_front(nqubits); - for (const auto [arg, iter] : llvm::zip(args, iterArgs)) { - remapQubitValue(arg, iter); - } - } - - /** - * @brief Remap input qubits to out-of-loop values (results). - * - * @param op The 'scf.for' op. - */ - void remapToLoopResults(mlir::scf::ForOp op) { - const auto nqubits = getNumQubits(); - const auto args = op.getInitArgs().take_front(nqubits); - const auto results = op.getResults().take_front(nqubits); - for (const auto [arg, iter] : llvm::zip(args, results)) { - remapQubitValue(arg, iter); - } - } - - /** - * @brief Remap current qubit values to if results. - * - * @param op The 'scf.if' op. - */ - void remapIfResults(mlir::scf::IfOp op) { - const auto nqubits = getNumQubits(); - const auto results = op->getResults().take_front(nqubits); - for (const auto [in, out] : llvm::zip(getHardwareQubits(), results)) { - remapQubitValue(in, out); - } - } - -private: - struct QubitInfo { - uint32_t prog; - uint32_t hw; - }; - - /** - * @brief Maps an SSA value to its `QubitInfo`. - */ - mlir::DenseMap valueToMapping_; - - /** - * @brief Maps hardware qubit indices to SSA values. - */ - mlir::SmallVector qubits_; -}; - -} // namespace mqt::ir::opt - -namespace llvm { -template <> struct DenseMapInfo { - using Layout = mqt::ir::opt::ThinLayout; - using VectorInfo = DenseMapInfo>; - - static Layout getEmptyKey() { - Layout layout(0); - layout.programToHardware_ = VectorInfo::getEmptyKey(); - return layout; - } - - static Layout getTombstoneKey() { - Layout layout(0); - layout.programToHardware_ = VectorInfo::getTombstoneKey(); - return layout; - } - - static unsigned getHashValue(const Layout& layout) { - return VectorInfo::getHashValue(layout.programToHardware_); - } - - static bool isEqual(const Layout& lhs, const Layout& rhs) { - return VectorInfo::isEqual(lhs.programToHardware_, rhs.programToHardware_); - } -}; -} // namespace llvm diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Router.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Router.h deleted file mode 100644 index 5d2e4af912..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Router.h +++ /dev/null @@ -1,208 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Architecture.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -class NaiveRouter { -public: - [[nodiscard]] static mlir::SmallVector - route(QubitIndexPair gate, const ThinLayout& layout, - const Architecture& arch) { - mlir::SmallVector swaps; - const auto hw0 = layout.getHardwareIndex(gate.first); - const auto hw1 = layout.getHardwareIndex(gate.second); - return arch.shortestSWAPsBetween(hw0, hw1); - } -}; - -/// @brief Specifies the weights for different terms in the cost function f. -struct HeuristicWeights { - float alpha; - mlir::SmallVector lambdas; - - HeuristicWeights(const float alpha, const float lambda, - const std::size_t nlookahead) - : alpha(alpha), lambdas(1 + nlookahead) { - lambdas[0] = 1.; - for (std::size_t i = 1; i < lambdas.size(); ++i) { - lambdas[i] = lambdas[i - 1] * lambda; - } - } -}; - -class AStarHeuristicRouter { -public: - explicit AStarHeuristicRouter(HeuristicWeights weights) - : weights_(std::move(weights)) {} - -private: - struct Node { - mlir::SmallVector sequence; - ThinLayout layout; - float f; - - /** - * @brief Construct a root node with the given layout. Initialize the - * sequence with an empty vector and set the cost to zero. - */ - explicit Node(ThinLayout layout) : layout(std::move(layout)), f(0) {} - - /** - * @brief Construct a non-root node from its parent node. Apply the given - * swap to the layout of the parent node and evaluate the cost. - */ - Node(const Node& parent, QubitIndexPair swap, - mlir::SmallVector> window, - const Architecture& arch, const HeuristicWeights& weights) - : sequence(parent.sequence), layout(parent.layout), f(0) { - /// Apply node-specific swap to given layout. - layout.swap(layout.getProgramIndex(swap.first), - layout.getProgramIndex(swap.second)); - - /// Add swap to sequence. - sequence.push_back(swap); - - /// Evaluate cost function. - f = g(weights) + h(window, arch, weights); // NOLINT - } - - /** - * @brief Return true if the current sequence of SWAPs makes all gates - * executable. - */ - [[nodiscard]] bool isGoal(mlir::ArrayRef layer, - const Architecture& arch) const { - return std::ranges::all_of(layer, [&](const QubitIndexPair gate) { - return arch.areAdjacent(layout.getHardwareIndex(gate.first), - layout.getHardwareIndex(gate.second)); - }); - } - - /** - * @returns The depth in the search tree. - */ - [[nodiscard]] std::size_t depth() const { return sequence.size(); } - - [[nodiscard]] bool operator>(const Node& rhs) const { return f > rhs.f; } - - private: - /** - * @brief Calculate the path cost for the A* search algorithm. - * - * The path cost function is the weighted sum of the currently required - * SWAPs. - */ - [[nodiscard]] float g(const HeuristicWeights& weights) const { - return (weights.alpha * static_cast(depth())); - } - - /** - * @brief Calculate the heuristic cost for the A* search algorithm. - * - * Computes the minimal number of SWAPs required to route each gate in each - * layer. For each gate, this is determined by the shortest distance between - * its hardware qubits. Intuitively, this is the number of SWAPs that a - * naive router would insert to route the layers. - */ - [[nodiscard]] float - h(mlir::SmallVector> window, - const Architecture& arch, const HeuristicWeights& weights) const { - float nn{0}; - for (const auto [i, layer] : llvm::enumerate(window)) { - for (const auto [prog0, prog1] : layer) { - const auto [hw0, hw1] = layout.getHardwareIndices(prog0, prog1); - const std::size_t nswaps = arch.distanceBetween(hw0, hw1) - 1; - nn += weights.lambdas[i] * static_cast(nswaps); - } - } - return nn; - } - }; - - using MinQueue = std::priority_queue, std::greater<>>; - -public: - [[nodiscard]] std::optional> - route(mlir::SmallVector> window, - const ThinLayout& layout, const Architecture& arch) const { - Node root(layout); - - /// Early exit. No SWAPs required: - if (root.isGoal(window.front(), arch)) { - return mlir::SmallVector{}; - } - - /// Initialize queue. - MinQueue frontier{}; - frontier.emplace(root); - - /// Iterative searching and expanding. - while (!frontier.empty()) { - Node curr = frontier.top(); - frontier.pop(); - - if (curr.isGoal(window.front(), arch)) { - return curr.sequence; - } - - /// Expand frontier with all neighbouring SWAPs in the current front. - expand(frontier, curr, window, arch); - } - - return std::nullopt; - } - -private: - /// @brief Expand frontier with all neighbouring SWAPs in the current front. - void expand(MinQueue& frontier, const Node& parent, - mlir::SmallVector> window, - const Architecture& arch) const { - llvm::SmallDenseSet expansionSet{}; - - /// Currently: Don't revert last SWAP. - /// TODO: Idea? Don't revert "front" (independent) SWAPs? - if (!parent.sequence.empty()) { - expansionSet.insert(parent.sequence.back()); - } - - for (const QubitIndexPair gate : window.front()) { - for (const auto prog : {gate.first, gate.second}) { - const auto hw0 = parent.layout.getHardwareIndex(prog); - for (const auto hw1 : arch.neighboursOf(hw0)) { - /// Ensure consistent hashing/comparison. - const QubitIndexPair swap = std::minmax(hw0, hw1); - if (!expansionSet.insert(swap).second) { - continue; - } - - frontier.emplace(parent, swap, window, arch, weights_); - } - } - } - } - - HeuristicWeights weights_; -}; - -} // namespace mqt::ir::opt diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/SequentialUnit.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/SequentialUnit.h deleted file mode 100644 index 654390b1a9..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/SequentialUnit.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Unit.h" - -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -/// @brief A SequentialUnit traverses a program sequentially. -class SequentialUnit : public Unit { -public: - [[nodiscard]] static SequentialUnit - fromEntryPointFunction(mlir::func::FuncOp func, std::size_t nqubits); - - SequentialUnit(Layout layout, mlir::Region* region, - mlir::Region::OpIterator start); - - SequentialUnit(Layout layout, mlir::Region* region) - : SequentialUnit(std::move(layout), region, region->op_begin()) {} - -private: - friend class Unit; - - [[nodiscard]] mlir::SmallVector nextImpl(); - [[nodiscard]] mlir::Region::OpIterator beginImpl() const { return start_; } - [[nodiscard]] mlir::Region::OpIterator endImpl() const { return end_; } - - mlir::Region::OpIterator start_; - mlir::Region::OpIterator end_; -}; -} // namespace mqt::ir::opt diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Stack.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Stack.h deleted file mode 100644 index 2817d3f988..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Stack.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include -#include -#include -#include - -namespace mqt::ir::opt { -/** - * @brief A custom stack implementation to handle multiple nested regions in a - * quantum-classical program. - */ -template class [[nodiscard]] LayoutStack { -public: - /** - * @brief Returns the top of the stack. - */ - [[nodiscard]] Item& top() { - assert(!stack_.empty() && "top: empty state stack"); - return stack_.back(); - } - - /** - * @brief Returns the item at the specified depth from the top of the stack. - */ - [[nodiscard]] Item& getItemAtDepth(std::size_t depth) { - assert(depth < stack_.size() && "getItemAtDepth: depth out of bounds"); - return stack_[stack_.size() - 1 - depth]; - } - - /** - * @brief Pushes a new item on to the stack. - */ - void push(Item item) { stack_.emplace_back(std::move(item)); } - - /** - * @brief Constructs a new item in-place at the top of the stack. - */ - template void emplace(Args&&... args) { - stack_.emplace_back(std::forward(args)...); - } - - /** - * @brief Duplicates the top item. - */ - void duplicateTop() { - assert(!stack_.empty() && "duplicateTop: empty state stack"); - stack_.emplace_back(stack_.back()); - } - - /** - * @brief Pops the top off the stack. - */ - void pop() { - assert(!stack_.empty() && "pop: empty item stack"); - stack_.pop_back(); - } - - /** - * @brief Returns the number of items in the stack. - */ - [[nodiscard]] std::size_t size() const { return stack_.size(); } - - /** - * @brief Returns whether the stack is empty. - */ - [[nodiscard]] bool empty() const { return stack_.empty(); } - - /** - * @brief Remove all items from the stack. - */ - void clear() { stack_.clear(); } - -private: - std::deque stack_; -}; -} // namespace mqt::ir::opt diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Unit.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Unit.h deleted file mode 100644 index 2887d6fbfa..0000000000 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Unit.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h" - -#include - -namespace mqt::ir::opt { - -/// @brief A Unit divides a quantum-classical program into routable sections. -template class Unit { -public: - /// @brief Compute and return subsequent units. - [[nodiscard]] mlir::SmallVector next() { - return static_cast(this)->nextImpl(); - } - - /// @returns an iterator pointing at the first element of the unit. - [[nodiscard]] auto begin() const { - return static_cast(this)->beginImpl(); - } - - /// @returns an iterator pointing at the past-the-end position. - [[nodiscard]] auto end() const { - return static_cast(this)->endImpl(); - } - - /// @returns the managed layout. - [[nodiscard]] Layout& layout() { return layout_; } - -protected: - Unit(Layout layout, mlir::Region* region) - : layout_(std::move(layout)), region_(region) {} - - /// @brief The layout this unit manages. - Layout layout_; - /// @brief The region this unit belongs to. - mlir::Region* region_; - /// @brief Pointer to the next dividing operation. - mlir::Operation* divider_{}; -}; - -} // namespace mqt::ir::opt diff --git a/mlir/include/mlir/Dialect/MQTRef/CMakeLists.txt b/mlir/include/mlir/Dialect/MQTRef/CMakeLists.txt deleted file mode 100644 index b181a84fed..0000000000 --- a/mlir/include/mlir/Dialect/MQTRef/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -add_subdirectory(IR) diff --git a/mlir/include/mlir/Dialect/MQTRef/IR/CMakeLists.txt b/mlir/include/mlir/Dialect/MQTRef/IR/CMakeLists.txt deleted file mode 100644 index b503ccd3e7..0000000000 --- a/mlir/include/mlir/Dialect/MQTRef/IR/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -set(DIALECT_NAME "MQTRef") -set(DIALECT_NAME_UPPER "MQTREF") -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/../../Common/IR/StdOps.td.inc - ${CMAKE_CURRENT_BINARY_DIR}/MQTRefStdOps.td @ONLY) - -add_mlir_dialect(MQTRefOps mqtref) -add_mlir_interface(MQTRefInterfaces) -add_mlir_doc(MQTRefOps MLIRMQTRefDialect Dialects/ -gen-dialect-doc) -add_mlir_doc(MQTRefInterfaces MLIRMQTRefInterfaces Dialects/ -gen-op-interface-docs -dialect=mqtref) diff --git a/mlir/include/mlir/Dialect/MQTRef/IR/MQTRefDialect.h b/mlir/include/mlir/Dialect/MQTRef/IR/MQTRefDialect.h deleted file mode 100644 index 0db2657290..0000000000 --- a/mlir/include/mlir/Dialect/MQTRef/IR/MQTRefDialect.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include -#include -#include - -// Suppress warnings about ambiguous reversed operators in MLIR -// (see https://github.com/llvm/llvm-project/issues/45853) -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wambiguous-reversed-operator" -#endif -#include "mlir/Interfaces/InferTypeOpInterface.h" -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -#include - -#define DIALECT_NAME_MQTREF "mqtref" - -//===----------------------------------------------------------------------===// -// Dialect -//===----------------------------------------------------------------------===// - -#include "mlir/Dialect/MQTRef/IR/MQTRefOpsDialect.h.inc" // IWYU pragma: export - -//===----------------------------------------------------------------------===// -// Types -//===----------------------------------------------------------------------===// - -#define GET_TYPEDEF_CLASSES -#include "mlir/Dialect/MQTRef/IR/MQTRefOpsTypes.h.inc" // IWYU pragma: export - -//===----------------------------------------------------------------------===// -// Interfaces -//===----------------------------------------------------------------------===// - -#include "mlir/Dialect/Common/IR/CommonTraits.h" // IWYU pragma: export -#include "mlir/Dialect/MQTRef/IR/MQTRefInterfaces.h.inc" // IWYU pragma: export - -//===----------------------------------------------------------------------===// -// Operations -//===----------------------------------------------------------------------===// - -#define GET_OP_CLASSES -#include "mlir/Dialect/MQTRef/IR/MQTRefOps.h.inc" // IWYU pragma: export - -namespace mqt::ir::ref { -mlir::ParseResult parseRefParams( - mlir::OpAsmParser& parser, - llvm::SmallVectorImpl& params, - mlir::Attribute& staticParams, mlir::Attribute& paramsMask); - -void printRefParams(mlir::OpAsmPrinter& printer, mlir::Operation* op, - mlir::ValueRange params, - mlir::DenseF64ArrayAttr staticParams, - mlir::DenseBoolArrayAttr paramsMask); -} // namespace mqt::ir::ref diff --git a/mlir/include/mlir/Dialect/MQTRef/IR/MQTRefInterfaces.td b/mlir/include/mlir/Dialect/MQTRef/IR/MQTRefInterfaces.td deleted file mode 100644 index d9ea8d4384..0000000000 --- a/mlir/include/mlir/Dialect/MQTRef/IR/MQTRefInterfaces.td +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -#ifndef MQTREF_INTERFACES -#define MQTREF_INTERFACES - -include "mlir/IR/OpBase.td" - -//===----------------------------------------------------------------------===// -// Interfaces -//===----------------------------------------------------------------------===// - -def UnitaryInterface : OpInterface<"UnitaryInterface"> { - let description = [{ - This interface provides a generic way to interact with unitary - operations in the MQTRef dialect. Unitary operations are quantum - operations that are reversible and can be represented by a unitary - matrix. The interface provides methods to access the operands and - results of the operation. - }]; - - let cppNamespace = "::mqt::ir::ref"; - - let methods = [ - InterfaceMethod< - /*desc=*/ "Returns all qubits of the operation excl. controlling qubits.", - /*returnType=*/ "mlir::OperandRange", - /*methodName=*/ "getInQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return $_op.getInQubits(); - }]>, - InterfaceMethod< - /*desc=*/ "Returns all positively-controlling qubits of the operation.", - /*returnType=*/ "mlir::OperandRange", - /*methodName=*/ "getPosCtrlInQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return $_op.getPosCtrlInQubits(); - }]>, - InterfaceMethod< - /*desc=*/ "Returns all negatively-controlling qubits of the operation.", - /*returnType=*/ "mlir::OperandRange", - /*methodName=*/ "getNegCtrlInQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return $_op.getNegCtrlInQubits(); - }]>, - InterfaceMethod< - /*desc=*/ "Returns all controlling qubits of the operation.", - /*returnType=*/ "std::vector", - /*methodName=*/ "getCtrlInQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - const auto& posCtrlInQubits = $_op.getPosCtrlInQubits(); - const auto& negCtrlInQubits = $_op.getNegCtrlInQubits(); - std::vector controls{}; - controls.reserve(posCtrlInQubits.size() + negCtrlInQubits.size()); - controls.insert(controls.end(), posCtrlInQubits.begin(), posCtrlInQubits.end()); - controls.insert(controls.end(), negCtrlInQubits.begin(), negCtrlInQubits.end()); - return controls; - }]>, - InterfaceMethod< - /*desc=*/ "Returns true if the operation has any control qubits, otherwise false.", - /*returnType=*/ "bool", - /*methodName=*/ "isControlled", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return !$_op.getPosCtrlInQubits().empty() || !$_op.getNegCtrlInQubits().empty(); - }]>, - InterfaceMethod< - /*desc=*/ "Returns all input qubits of the operation incl. all controlling qubits.", - /*returnType=*/ "std::vector", - /*methodName=*/ "getAllInQubits", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - const auto& inQubits = $_op.getInQubits(); - const auto& controls = $_op.getCtrlInQubits(); - std::vector operands{}; - operands.reserve(inQubits.size() + controls.size()); - operands.insert(operands.end(), inQubits.begin(), inQubits.end()); - operands.insert(operands.end(), controls.begin(), controls.end()); - return operands; - }]>, - InterfaceMethod< - /*desc=*/ "Returns the name of the gate.", - /*returnType=*/ "llvm::StringRef", - /*methodName=*/ "getIdentifier", - /*args=*/ (ins), - /*methodBody=*/ [{}], - /*defaultImpl=*/ [{ - return $_op->getName().getStringRef().split('.').second; - }]> - ]; -} - -#endif // MQTREF_INTERFACES diff --git a/mlir/include/mlir/Dialect/MQTRef/IR/MQTRefOps.td b/mlir/include/mlir/Dialect/MQTRef/IR/MQTRefOps.td deleted file mode 100644 index 41b5ee8013..0000000000 --- a/mlir/include/mlir/Dialect/MQTRef/IR/MQTRefOps.td +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -#ifndef MQTREF_OPS -#define MQTREF_OPS - -include "mlir/IR/BuiltinTypeInterfaces.td" -include "mlir/IR/DialectBase.td" -include "mlir/IR/EnumAttr.td" -include "mlir/IR/OpBase.td" -include "mlir/Interfaces/InferTypeOpInterface.td" -include "mlir/Interfaces/SideEffectInterfaces.td" - -include "mlir/Dialect/Common/IR/CommonTraits.td" -include "mlir/Dialect/MQTRef/IR/MQTRefInterfaces.td" - -//===----------------------------------------------------------------------===// -// Dialect -//===----------------------------------------------------------------------===// - -def MQTRefDialect : Dialect { - // The dialect name used in the MLIR file to prefix operations. - let name = "mqtref"; - - let summary = "The MQT reference semantics (`mqtref`) dialect."; - - let description = [{ - This dialect uses memory semantics to represent and work with qubits. - Similar to many existing quantum programming languages, operations can - just be applied to qubits without needing to track state updates. - - This dialect is designed as a "compatibility" dialect that simplifies - translations from and to existing languages such as QASM or QIR. - Its optimization capabilities are more limited compared to the - `mqtopt` dialect, which is designed for optimization. - - The `mqtref` dialect supports dynamic as well as static qubit - addressing. - }]; - - // The C++ namespace that the dialect, and all sub-components, get placed - // in. Here, `mlir::` must either appear at the front or not at all. - let cppNamespace = "::mqt::ir::ref"; - - // This dialect defines its own types for qubits and qubit registers. To - // parse and print these, respective hooks must be defined. With this flag - // set to 1, the default hooks are created and used. - let useDefaultTypePrinterParser = 1; -} - -//===----------------------------------------------------------------------===// -// Types -//===----------------------------------------------------------------------===// - -class MQTRefType traits = []> - : TypeDef { - let mnemonic = typeMnemonic; -} - -def QubitType : MQTRefType<"Qubit", "Qubit", [MemRefElementTypeInterface]> { - let summary = "qubit reference"; -} - -//===----------------------------------------------------------------------===// -// Operations -//===----------------------------------------------------------------------===// - -// This is the base class for all operations in the MQTRef dialect. It is a -// template that takes the operation mnemonic and a list of traits. The MQTRefOp -// class is a subclass of the Op class defined in the MLIR core. -class MQTRefOp traits = []> : - Op; - -class GateOp traits = []> : - MQTRefOp { -} - -class UnitaryOp traits = []> : - GateOp { - let arguments = (ins - OptionalAttr:$static_params, - OptionalAttr:$params_mask, - Variadic:$params, - Variadic:$in_qubits, - Variadic:$pos_ctrl_in_qubits, - Variadic:$neg_ctrl_in_qubits - ); - - let results = (outs); - - let assemblyFormat = [{ - `(` custom($params, $static_params, $params_mask) `)` - attr-dict - $in_qubits ( `ctrl` $pos_ctrl_in_qubits^ )? ( `nctrl` $neg_ctrl_in_qubits^ )? - }]; -} - -include "mlir/Dialect/MQTRef/IR/MQTRefStdOps.td" - -def MeasureOp : GateOp<"measure", []> { - let summary = "A measure operation"; - - let description = [{ - This class represents a measure operation. - It takes a single qubit as input and returns a bit. - After the measurement, the referenced qubit is in state "0" or "1". - The value of the state is indicated by the returned bit. - - Example: - ```mlir - %1 = mqtref.measure %0 - ``` - }]; - - let arguments = (ins QubitType:$in_qubit); - let results = (outs I1:$out_bit); - let assemblyFormat = "$in_qubit attr-dict"; -} - -def ResetOp : GateOp<"reset", []> { - let summary = "A reset operation"; - - let description = [{ - This class represents a reset operation. - It takes a single qubit as input and and resets its state to "0". - - Example: - ```mlir - mqtref.reset %0 - ``` - }]; - - let arguments = (ins QubitType:$in_qubit); - let results = (outs); - let assemblyFormat = "$in_qubit attr-dict"; -} - -class ResourceOp traits = []> : MQTRefOp; - -def AllocQubitOp : ResourceOp<"allocQubit"> { - let summary = "Allocates a single qubit"; - - let description = [{ - Allocates a single qubit in the "0" state. - The qubit can be used in operations after allocation. - It must be deallocated with the `deallocQubit` operation. - - Example: - ```mlir - %q = mqtref.allocQubit - ``` - }]; - - let arguments = (ins); - let results = (outs QubitType:$qubit); - let assemblyFormat = "attr-dict"; -} - -def DeallocQubitOp : ResourceOp<"deallocQubit"> { - let summary = "Deallocates a single qubit"; - - let description = [{ - Deallocates a single qubit that was previously allocated with the - `allocQubit` operation. After this operation, the qubit is no longer - valid and cannot be used in further operations. - - Example: - ```mlir - mqtref.deallocQubit %q - ``` - }]; - - let arguments = (ins QubitType:$qubit); - let results = (outs); - let assemblyFormat = "$qubit attr-dict"; -} - -def QubitOp : ResourceOp<"qubit"> { - let summary = "Assign static qubit reference"; - let description = [{ - The `mqtref.qubit` operation produces an SSA value from the given index - to a static (hardware) qubit. - - Example: - ```mlir - %q = mqtref.qubit 0 - ``` - }]; - - let arguments = (ins ConfinedAttr:$index); - let results = (outs QubitType:$qubit); - let assemblyFormat = "$index attr-dict"; -} - -#endif // MQTREF_OPS diff --git a/mlir/include/mlir/Dialect/MQTRef/Translation/ImportQuantumComputation.h b/mlir/include/mlir/Dialect/MQTRef/Translation/ImportQuantumComputation.h deleted file mode 100644 index 98dc3de142..0000000000 --- a/mlir/include/mlir/Dialect/MQTRef/Translation/ImportQuantumComputation.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#pragma once - -#include -#include - -namespace qc { -class QuantumComputation; -} - -mlir::OwningOpRef -translateQuantumComputationToMLIR(mlir::MLIRContext* context, - const qc::QuantumComputation& qc); diff --git a/mlir/lib/Conversion/CMakeLists.txt b/mlir/lib/Conversion/CMakeLists.txt index 33eb84b886..93f6a8a639 100644 --- a/mlir/lib/Conversion/CMakeLists.txt +++ b/mlir/lib/Conversion/CMakeLists.txt @@ -6,10 +6,6 @@ # # Licensed under the MIT License -add_subdirectory(MQTOptToMQTRef) -add_subdirectory(MQTRefToMQTOpt) -add_subdirectory(MQTRefToQIR) -add_subdirectory(QIRToMQTRef) add_subdirectory(QCOToQC) add_subdirectory(QCToQCO) add_subdirectory(QCToQIR) diff --git a/mlir/lib/Conversion/MQTOptToMQTRef/CMakeLists.txt b/mlir/lib/Conversion/MQTOptToMQTRef/CMakeLists.txt deleted file mode 100644 index ea0c053b6e..0000000000 --- a/mlir/lib/Conversion/MQTOptToMQTRef/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -file(GLOB CONVERSION_SOURCES *.cpp) - -add_mlir_library( - MQTOptToMQTRef - ${CONVERSION_SOURCES} - DEPENDS - MQTOptToMQTRefIncGen - LINK_LIBS - MLIRMQTOpt - MLIRMQTRef - MLIRMemRefDialect - MLIRTransforms - MLIRFuncDialect - MLIRFuncTransforms - DISABLE_INSTALL) diff --git a/mlir/lib/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.cpp b/mlir/lib/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.cpp deleted file mode 100644 index 93c042586b..0000000000 --- a/mlir/lib/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.cpp +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -// macro to add the conversion pattern from any opt gate operation to the same -// gate operation in the ref dialect -#define ADD_CONVERT_PATTERN(gate) \ - patterns \ - .add>( \ - typeConverter, context); - -#include "mlir/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.h" - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTRef/IR/MQTRefDialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir { - -using namespace mlir; - -#define GEN_PASS_DEF_MQTOPTTOMQTREF -#include "mlir/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.h.inc" - -static bool isQubitType(MemRefType type) { - return llvm::isa(type.getElementType()); -} - -static bool isQubitType(memref::AllocOp op) { - return isQubitType(op.getType()); -} - -static bool isQubitType(memref::DeallocOp op) { - const auto& memRef = op.getMemref(); - const auto& memRefType = llvm::cast(memRef.getType()); - return isQubitType(memRefType); -} - -static bool isQubitType(memref::LoadOp op) { - const auto& memRef = op.getMemref(); - const auto& memRefType = llvm::cast(memRef.getType()); - return isQubitType(memRefType); -} - -static bool isQubitType(memref::StoreOp op) { - const auto& memRef = op.getMemref(); - const auto& memRefType = llvm::cast(memRef.getType()); - return isQubitType(memRefType); -} - -class MQTOptToMQTRefTypeConverter final : public TypeConverter { -public: - explicit MQTOptToMQTRefTypeConverter(MLIRContext* ctx) { - // Identity conversion - addConversion([](Type type) { return type; }); - - // QubitType conversion - addConversion([ctx](opt::QubitType /*type*/) -> Type { - return ref::QubitType::get(ctx); - }); - - // MemRefType conversion - addConversion([ctx](MemRefType type) -> Type { - if (isQubitType(type)) { - return MemRefType::get(type.getShape(), ref::QubitType::get(ctx)); - } - return type; - }); - } -}; - -struct ConvertMQTOptMemRefAlloc final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(memref::AllocOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - if (!isQubitType(op)) { - return failure(); - } - - const auto& qubitType = ref::QubitType::get(rewriter.getContext()); - const auto& memRefType = - MemRefType::get(op.getType().getShape(), qubitType); - - rewriter.replaceOpWithNewOp(op, memRefType, - adaptor.getDynamicSizes()); - - return success(); - } -}; - -struct ConvertMQTOptMemRefDealloc final - : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(memref::DeallocOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - rewriter.replaceOpWithNewOp(op, adaptor.getMemref()); - return success(); - } -}; - -struct ConvertMQTOptAllocQubit final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(const opt::AllocQubitOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - rewriter.replaceOpWithNewOp(op); - return success(); - } -}; - -struct ConvertMQTOptDeallocQubit final - : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(const opt::DeallocQubitOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - rewriter.replaceOpWithNewOp(op, adaptor.getQubit()); - return success(); - } -}; - -struct ConvertMQTOptMemRefLoad final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(memref::LoadOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - if (!isQubitType(op)) { - return failure(); - } - - rewriter.replaceOpWithNewOp(op, adaptor.getMemref(), - adaptor.getIndices()); - - return success(); - } -}; - -struct ConvertMQTOptMemRefStore final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(memref::StoreOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - if (!isQubitType(op)) { - return failure(); - } - - rewriter.eraseOp(op); - return success(); - } -}; - -struct ConvertMQTOptMeasure final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::MeasureOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - const auto& refQubit = adaptor.getInQubit(); - - // create new operation - auto measure = rewriter.create( - op.getLoc(), op.getOutBit().getType(), refQubit); - - // replace the results of the old operation with the new results and - // delete old operation - rewriter.replaceOp(op, {refQubit, measure.getOutBit()}); - return success(); - } -}; - -struct ConvertMQTOptReset final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::ResetOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - const auto& refQubit = adaptor.getInQubit(); - - // create new operation - rewriter.create(op.getLoc(), refQubit); - - // replace the results of the old operation with the new results and - // delete old operation - rewriter.replaceOp(op, refQubit); - return success(); - } -}; - -struct ConvertMQTOptQubit final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::QubitOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - const auto& qubitType = ref::QubitType::get(rewriter.getContext()); - rewriter.replaceOpWithNewOp(op, qubitType, op.getIndex()); - return success(); - } -}; - -template -struct ConvertMQTOptGateOp final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(MQTGateOptOp op, typename MQTGateOptOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // get all the input qubits including the ctrl qubits - const auto& refInQubitsValues = adaptor.getInQubits(); - const auto& refPosCtrlQubitsValues = adaptor.getPosCtrlInQubits(); - const auto& refNegCtrlQubitsValues = adaptor.getNegCtrlInQubits(); - - // append them to a single vector - SmallVector refQubitsValues; - refQubitsValues.reserve(refInQubitsValues.size() + - refPosCtrlQubitsValues.size() + - refNegCtrlQubitsValues.size()); - refQubitsValues.append(refInQubitsValues.begin(), refInQubitsValues.end()); - refQubitsValues.append(refPosCtrlQubitsValues.begin(), - refPosCtrlQubitsValues.end()); - refQubitsValues.append(refNegCtrlQubitsValues.begin(), - refNegCtrlQubitsValues.end()); - - // get the static params and paramMask if they exist - auto staticParams = op.getStaticParams() - ? DenseF64ArrayAttr::get(rewriter.getContext(), - *op.getStaticParams()) - : DenseF64ArrayAttr{}; - auto paramMask = op.getParamsMask() - ? DenseBoolArrayAttr::get(rewriter.getContext(), - *op.getParamsMask()) - : DenseBoolArrayAttr{}; - - // create new operation - rewriter.create( - op.getLoc(), staticParams, paramMask, op.getParams(), refInQubitsValues, - refPosCtrlQubitsValues, refNegCtrlQubitsValues); - - // replace the results of the old operation with the new results and - // delete old operation - rewriter.replaceOp(op, refQubitsValues); - - return success(); - } -}; - -struct MQTOptToMQTRef final : impl::MQTOptToMQTRefBase { - using MQTOptToMQTRefBase::MQTOptToMQTRefBase; - void runOnOperation() override { - MLIRContext* context = &getContext(); - auto* module = getOperation(); - - ConversionTarget target(*context); - RewritePatternSet patterns(context); - MQTOptToMQTRefTypeConverter typeConverter(context); - - target.addIllegalDialect(); - target.addLegalDialect(); - - target.addDynamicallyLegalOp( - [&](memref::AllocOp op) { return !isQubitType(op); }); - target.addDynamicallyLegalOp( - [&](memref::DeallocOp op) { return !isQubitType(op); }); - target.addDynamicallyLegalOp( - [&](memref::LoadOp op) { return !isQubitType(op); }); - target.addDynamicallyLegalOp( - [&](memref::StoreOp op) { return !isQubitType(op); }); - - patterns.add( - typeConverter, context); - - ADD_CONVERT_PATTERN(GPhaseOp) - ADD_CONVERT_PATTERN(IOp) - ADD_CONVERT_PATTERN(BarrierOp) - ADD_CONVERT_PATTERN(HOp) - ADD_CONVERT_PATTERN(XOp) - ADD_CONVERT_PATTERN(YOp) - ADD_CONVERT_PATTERN(ZOp) - ADD_CONVERT_PATTERN(SOp) - ADD_CONVERT_PATTERN(SdgOp) - ADD_CONVERT_PATTERN(TOp) - ADD_CONVERT_PATTERN(TdgOp) - ADD_CONVERT_PATTERN(VOp) - ADD_CONVERT_PATTERN(VdgOp) - ADD_CONVERT_PATTERN(UOp) - ADD_CONVERT_PATTERN(U2Op) - ADD_CONVERT_PATTERN(POp) - ADD_CONVERT_PATTERN(SXOp) - ADD_CONVERT_PATTERN(SXdgOp) - ADD_CONVERT_PATTERN(ROp) - ADD_CONVERT_PATTERN(RXOp) - ADD_CONVERT_PATTERN(RYOp) - ADD_CONVERT_PATTERN(RZOp) - ADD_CONVERT_PATTERN(SWAPOp) - ADD_CONVERT_PATTERN(iSWAPOp) - ADD_CONVERT_PATTERN(iSWAPdgOp) - ADD_CONVERT_PATTERN(PeresOp) - ADD_CONVERT_PATTERN(PeresdgOp) - ADD_CONVERT_PATTERN(DCXOp) - ADD_CONVERT_PATTERN(ECROp) - ADD_CONVERT_PATTERN(RXXOp) - ADD_CONVERT_PATTERN(RYYOp) - ADD_CONVERT_PATTERN(RZZOp) - ADD_CONVERT_PATTERN(RZXOp) - ADD_CONVERT_PATTERN(XXminusYYOp) - ADD_CONVERT_PATTERN(XXplusYYOp) - - // conversion of mqtopt types in func.func signatures - populateFunctionOpInterfaceTypeConversionPattern( - patterns, typeConverter); - target.addDynamicallyLegalOp([&](func::FuncOp op) { - return typeConverter.isSignatureLegal(op.getFunctionType()) && - typeConverter.isLegal(&op.getBody()); - }); - - // conversion of mqtopt types in func.return - populateReturnOpTypeConversionPattern(patterns, typeConverter); - target.addDynamicallyLegalOp( - [&](const func::ReturnOp op) { return typeConverter.isLegal(op); }); - - // conversion of mqtopt types in func.call - populateCallOpTypeConversionPattern(patterns, typeConverter); - target.addDynamicallyLegalOp( - [&](const func::CallOp op) { return typeConverter.isLegal(op); }); - - // conversion of mqtopt types in control-flow ops; e.g. cf.br - populateBranchOpInterfaceTypeConversionPattern(patterns, typeConverter); - - if (failed(applyPartialConversion(module, target, std::move(patterns)))) { - signalPassFailure(); - } - }; -}; - -} // namespace mqt::ir diff --git a/mlir/lib/Conversion/MQTRefToMQTOpt/CMakeLists.txt b/mlir/lib/Conversion/MQTRefToMQTOpt/CMakeLists.txt deleted file mode 100644 index daa12685af..0000000000 --- a/mlir/lib/Conversion/MQTRefToMQTOpt/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -file(GLOB CONVERSION_SOURCES *.cpp) - -add_mlir_library( - MQTRefToMQTOpt - ${CONVERSION_SOURCES} - DEPENDS - MQTRefToMQTOptIncGen - LINK_LIBS - MLIRMQTOpt - MLIRMQTRef - MLIRMemRefDialect - MLIRTransforms - MLIRFuncDialect - MLIRFuncTransforms - DISABLE_INSTALL) diff --git a/mlir/lib/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.cpp b/mlir/lib/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.cpp deleted file mode 100644 index 032530e446..0000000000 --- a/mlir/lib/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.cpp +++ /dev/null @@ -1,483 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -// macro to add the conversion pattern from any ref gate operation to the same -// gate operation in the opt dialect -#define ADD_CONVERT_PATTERN(gate) \ - patterns \ - .add>( \ - typeConverter, context, &state); - -#include "mlir/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.h" - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTRef/IR/MQTRefDialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir { - -using namespace mlir; - -#define GEN_PASS_DEF_MQTREFTOMQTOPT -#include "mlir/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.h.inc" - -namespace { - -struct LoweringState { - /// @brief Map each initial ref qubit to its latest opt qubit. - llvm::DenseMap qubitMap; - /// @brief Map each initial ref qubit to its index. - llvm::DenseMap qubitIndexMap; - /// @brief Map each initial ref register to its refQubits. - llvm::DenseMap> qregQubitsMap; -}; - -template -class StatefulOpConversionPattern : public mlir::OpConversionPattern { - using mlir::OpConversionPattern::OpConversionPattern; - -public: - StatefulOpConversionPattern(mlir::TypeConverter& typeConverter, - mlir::MLIRContext* context, LoweringState* state) - : mlir::OpConversionPattern(typeConverter, context), - state_(state) {} - - /// @brief Return the state object as reference. - [[nodiscard]] LoweringState& getState() const { return *state_; } - -private: - LoweringState* state_; -}; - -} // namespace - -static bool isQubitType(MemRefType type) { - return llvm::isa(type.getElementType()); -} - -static bool isQubitType(memref::AllocOp op) { - return isQubitType(op.getType()); -} - -static bool isQubitType(memref::DeallocOp op) { - const auto& memRef = op.getMemref(); - const auto& memRefType = llvm::cast(memRef.getType()); - return isQubitType(memRefType); -} - -static bool isQubitType(memref::LoadOp op) { - const auto& memRef = op.getMemref(); - const auto& memRefType = llvm::cast(memRef.getType()); - return isQubitType(memRefType); -} - -class MQTRefToMQTOptTypeConverter final : public TypeConverter { -public: - explicit MQTRefToMQTOptTypeConverter(MLIRContext* ctx) { - // Identity conversion - addConversion([](Type type) { return type; }); - - // QubitType conversion - addConversion([ctx](ref::QubitType /*type*/) -> Type { - return opt::QubitType::get(ctx); - }); - - // MemRefType conversion - addConversion([ctx](MemRefType type) -> Type { - if (isQubitType(type)) { - return MemRefType::get(type.getShape(), opt::QubitType::get(ctx)); - } - return type; - }); - } -}; - -struct ConvertMQTRefMemRefAlloc final - : StatefulOpConversionPattern { - using StatefulOpConversionPattern< - memref::AllocOp>::StatefulOpConversionPattern; - - LogicalResult - matchAndRewrite(memref::AllocOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - if (!isQubitType(op)) { - return failure(); - } - - const auto& qubitType = opt::QubitType::get(rewriter.getContext()); - auto memRefType = MemRefType::get(op.getType().getShape(), qubitType); - - rewriter.replaceOpWithNewOp(op, memRefType, - adaptor.getDynamicSizes()); - - return success(); - } -}; - -struct ConvertMQTRefMemRefDealloc final - : StatefulOpConversionPattern { - using StatefulOpConversionPattern< - memref::DeallocOp>::StatefulOpConversionPattern; - - LogicalResult - matchAndRewrite(memref::DeallocOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - if (!isQubitType(op)) { - return failure(); - } - - auto refMemRef = op.getMemref(); - const auto& optMemRef = adaptor.getMemref(); - - // iterate over all the qubits that were extracted from the register - for (const auto& refQubit : getState().qregQubitsMap[refMemRef]) { - const auto& optQubit = getState().qubitMap[refQubit]; - auto index = getState().qubitIndexMap[refQubit]; - - auto storeOp = rewriter.create( - op.getLoc(), optQubit, optMemRef, ValueRange{index}); - - // move it before the current dealloc operation - storeOp->moveBefore(op); - - // erase the refQubit entry from the maps - getState().qubitMap.erase(refQubit); - getState().qubitIndexMap.erase(refQubit); - } - - // erase the register from the map - getState().qregQubitsMap.erase(refMemRef); - - rewriter.replaceOpWithNewOp(op, optMemRef); - - return success(); - } -}; - -struct ConvertMQTRefAllocQubit final - : StatefulOpConversionPattern { - using StatefulOpConversionPattern::StatefulOpConversionPattern; - - LogicalResult - matchAndRewrite(ref::AllocQubitOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - const auto& refQubit = op.getQubit(); - auto optOp = rewriter.replaceOpWithNewOp(op); - const auto& optQubit = optOp.getQubit(); - getState().qubitMap.try_emplace(refQubit, optQubit); - return success(); - } -}; - -struct ConvertMQTRefDeallocQubit final - : StatefulOpConversionPattern { - using StatefulOpConversionPattern::StatefulOpConversionPattern; - - LogicalResult - matchAndRewrite(ref::DeallocQubitOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - const auto& refQubit = op.getQubit(); - const auto& optQubit = getState().qubitMap[refQubit]; - rewriter.replaceOpWithNewOp(op, optQubit); - getState().qubitMap.erase(refQubit); - return success(); - } -}; - -struct ConvertMQTRefMemRefLoad final - : StatefulOpConversionPattern { - using StatefulOpConversionPattern< - memref::LoadOp>::StatefulOpConversionPattern; - - LogicalResult - matchAndRewrite(memref::LoadOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - if (!isQubitType(op)) { - return failure(); - } - - // create new operation - auto optLoadOp = rewriter.replaceOpWithNewOp( - op, adaptor.getMemref(), adaptor.getIndices()); - - const auto& refMemRef = op.getMemref(); - const auto& refQubit = op.getResult(); - const auto& optQubit = optLoadOp.getResult(); - - // put the pair of the ref qubit and the latest opt qubit in the map - getState().qubitMap.try_emplace(refQubit, optQubit); - - // add entry to qubitIndexMap - getState().qubitIndexMap.try_emplace(refQubit, - adaptor.getIndices().front()); - - // append the entry to the qregQubitsMap to store which qubits that were - // extracted from the register - getState().qregQubitsMap[refMemRef].emplace_back(refQubit); - - return success(); - } -}; - -struct ConvertMQTRefMeasure final - : StatefulOpConversionPattern { - using StatefulOpConversionPattern< - ref::MeasureOp>::StatefulOpConversionPattern; - - LogicalResult - matchAndRewrite(ref::MeasureOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - - // prepare result type - const auto& qubitType = opt::QubitType::get(rewriter.getContext()); - - const auto& refQubit = op.getInQubit(); - - // get the latest opt qubit from the map and add them to the vector - const Value optQubit = getState().qubitMap[refQubit]; - - // create new operation - auto optOp = rewriter.create( - op.getLoc(), qubitType, op.getOutBit().getType(), optQubit); - - auto outOptQubit = optOp.getOutQubit(); - auto newBit = optOp.getOutBit(); - - getState().qubitMap[refQubit] = outOptQubit; - - // replace the old operation results with the new bits and delete - // old operation - rewriter.replaceOp(op, newBit); - - return success(); - } -}; - -struct ConvertMQTRefReset final : StatefulOpConversionPattern { - using StatefulOpConversionPattern::StatefulOpConversionPattern; - - LogicalResult - matchAndRewrite(ref::ResetOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - - // prepare result type - const auto& qubitType = opt::QubitType::get(rewriter.getContext()); - - const auto& refQubit = op.getInQubit(); - - // get the latest opt qubit from the map and add them to the vector - const Value optQubit = getState().qubitMap[refQubit]; - - // create new operation - auto optOp = - rewriter.create(op.getLoc(), qubitType, optQubit); - - auto outOptQubit = optOp.getOutQubit(); - - getState().qubitMap[refQubit] = outOptQubit; - - // delete the old operation - rewriter.eraseOp(op); - - return success(); - } -}; - -struct ConvertMQTRefQubit final : StatefulOpConversionPattern { - using StatefulOpConversionPattern::StatefulOpConversionPattern; - - LogicalResult - matchAndRewrite(ref::QubitOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - // prepare result type - const auto& qubitType = opt::QubitType::get(rewriter.getContext()); - - // create new operation - auto optOp = - rewriter.create(op.getLoc(), qubitType, op.getIndex()); - - // collect ref and opt SSA value - const auto& refQubit = op.getQubit(); - const auto& optQubit = optOp.getQubit(); - - // map ref to opt - getState().qubitMap[refQubit] = optQubit; - - // replace the old operation result with the new result and delete - // old operation - rewriter.replaceOp(op, optQubit); - - return success(); - } -}; - -template -struct ConvertMQTRefGateOp final : StatefulOpConversionPattern { - using StatefulOpConversionPattern::StatefulOpConversionPattern; - - LogicalResult - matchAndRewrite(MQTGateRefOp op, typename MQTGateRefOp::Adaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - // Map ref qubits to opt qubits - auto mapQubits = [&](const auto& refQubits) { - std::vector optQubits; - for (const auto& refQubit : refQubits) { - optQubits.emplace_back(this->getState().qubitMap[refQubit]); - } - return optQubits; - }; - - auto optInQubits = mapQubits(op.getInQubits()); - auto optPosCtrlQubitsValues = mapQubits(op.getPosCtrlInQubits()); - auto optNegCtrlQubitsValues = mapQubits(op.getNegCtrlInQubits()); - - // Get optional attributes - auto staticParams = op.getStaticParams() - ? DenseF64ArrayAttr::get(rewriter.getContext(), - *op.getStaticParams()) - : DenseF64ArrayAttr{}; - auto paramMask = op.getParamsMask() - ? DenseBoolArrayAttr::get(rewriter.getContext(), - *op.getParamsMask()) - : DenseBoolArrayAttr{}; - - // Create new operation - auto optOp = rewriter.create( - op.getLoc(), ValueRange(optInQubits).getTypes(), - ValueRange(optPosCtrlQubitsValues).getTypes(), - ValueRange(optNegCtrlQubitsValues).getTypes(), staticParams, paramMask, - op.getParams(), optInQubits, optPosCtrlQubitsValues, - optNegCtrlQubitsValues); - - // Update qubit map - const auto& optResults = optOp.getAllOutQubits(); - for (size_t i = 0; i < op.getAllInQubits().size(); i++) { - this->getState().qubitMap[op.getAllInQubits()[i]] = optResults[i]; - } - - rewriter.eraseOp(op); - return success(); - } -}; - -struct MQTRefToMQTOpt final : impl::MQTRefToMQTOptBase { - using MQTRefToMQTOptBase::MQTRefToMQTOptBase; - - void runOnOperation() override { - MLIRContext* context = &getContext(); - auto* module = getOperation(); - - LoweringState state; - - ConversionTarget target(*context); - RewritePatternSet patterns(context); - MQTRefToMQTOptTypeConverter typeConverter(context); - - target.addIllegalDialect(); - target.addLegalDialect(); - - target.addDynamicallyLegalOp( - [&](memref::AllocOp op) { return !isQubitType(op); }); - target.addDynamicallyLegalOp( - [&](memref::DeallocOp op) { return !isQubitType(op); }); - target.addDynamicallyLegalOp( - [&](memref::LoadOp op) { return !isQubitType(op); }); - target.addLegalOp(); - - patterns.add(typeConverter, context, &state); - patterns.add(typeConverter, context, &state); - patterns.add(typeConverter, context, &state); - patterns.add(typeConverter, context, &state); - patterns.add(typeConverter, context, &state); - patterns.add(typeConverter, context, &state); - patterns.add(typeConverter, context, &state); - patterns.add(typeConverter, context, &state); - - ADD_CONVERT_PATTERN(GPhaseOp) - ADD_CONVERT_PATTERN(IOp) - ADD_CONVERT_PATTERN(BarrierOp) - ADD_CONVERT_PATTERN(HOp) - ADD_CONVERT_PATTERN(XOp) - ADD_CONVERT_PATTERN(YOp) - ADD_CONVERT_PATTERN(ZOp) - ADD_CONVERT_PATTERN(SOp) - ADD_CONVERT_PATTERN(SdgOp) - ADD_CONVERT_PATTERN(TOp) - ADD_CONVERT_PATTERN(TdgOp) - ADD_CONVERT_PATTERN(VOp) - ADD_CONVERT_PATTERN(VdgOp) - ADD_CONVERT_PATTERN(UOp) - ADD_CONVERT_PATTERN(U2Op) - ADD_CONVERT_PATTERN(POp) - ADD_CONVERT_PATTERN(SXOp) - ADD_CONVERT_PATTERN(SXdgOp) - ADD_CONVERT_PATTERN(ROp) - ADD_CONVERT_PATTERN(RXOp) - ADD_CONVERT_PATTERN(RYOp) - ADD_CONVERT_PATTERN(RZOp) - ADD_CONVERT_PATTERN(SWAPOp) - ADD_CONVERT_PATTERN(iSWAPOp) - ADD_CONVERT_PATTERN(iSWAPdgOp) - ADD_CONVERT_PATTERN(PeresOp) - ADD_CONVERT_PATTERN(PeresdgOp) - ADD_CONVERT_PATTERN(DCXOp) - ADD_CONVERT_PATTERN(ECROp) - ADD_CONVERT_PATTERN(RXXOp) - ADD_CONVERT_PATTERN(RYYOp) - ADD_CONVERT_PATTERN(RZZOp) - ADD_CONVERT_PATTERN(RZXOp) - ADD_CONVERT_PATTERN(XXminusYYOp) - ADD_CONVERT_PATTERN(XXplusYYOp) - - // conversion of mqtopt types in func.func signatures - // does not work for now as signature needs to be changed - populateFunctionOpInterfaceTypeConversionPattern( - patterns, typeConverter); - target.addDynamicallyLegalOp([&](func::FuncOp op) { - return typeConverter.isSignatureLegal(op.getFunctionType()) && - typeConverter.isLegal(&op.getBody()); - }); - // conversion of mqtref types in func.return - populateReturnOpTypeConversionPattern(patterns, typeConverter); - target.addDynamicallyLegalOp( - [&](const func::ReturnOp op) { return typeConverter.isLegal(op); }); - - // conversion of mqtref types in func.call - populateCallOpTypeConversionPattern(patterns, typeConverter); - target.addDynamicallyLegalOp( - [&](const func::CallOp op) { return typeConverter.isLegal(op); }); - - // conversion of mqtref types in control-flow ops; e.g. cf.br - populateBranchOpInterfaceTypeConversionPattern(patterns, typeConverter); - if (failed(applyPartialConversion(module, target, std::move(patterns)))) { - signalPassFailure(); - } - }; -}; - -} // namespace mqt::ir diff --git a/mlir/lib/Conversion/MQTRefToQIR/CMakeLists.txt b/mlir/lib/Conversion/MQTRefToQIR/CMakeLists.txt deleted file mode 100644 index bdc768c36b..0000000000 --- a/mlir/lib/Conversion/MQTRefToQIR/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -file(GLOB CONVERSION_SOURCES *.cpp) - -add_mlir_library( - MQTRefToQIR - ${CONVERSION_SOURCES} - DEPENDS - MQTRefToQIRIncGen - LINK_LIBS - MLIRMQTRef - MLIRMemRefDialect - MLIRTransforms - MLIRLLVMDialect - MLIRFuncToLLVM - MLIRReconcileUnrealizedCasts - DISABLE_INSTALL) diff --git a/mlir/lib/Conversion/MQTRefToQIR/MQTRefToQIR.cpp b/mlir/lib/Conversion/MQTRefToQIR/MQTRefToQIR.cpp deleted file mode 100644 index d2797c0e7a..0000000000 --- a/mlir/lib/Conversion/MQTRefToQIR/MQTRefToQIR.cpp +++ /dev/null @@ -1,1036 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -// macro to add the conversion pattern from any ref gate operation to a llvm -// call operation that adheres to the QIR specification -#define ADD_CONVERT_PATTERN(gate) \ - mqtPatterns.add>(typeConverter, ctx); - -#include "mlir/Conversion/MQTRefToQIR/MQTRefToQIR.h" - -#include "mlir/Dialect/MQTRef/IR/MQTRefDialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir { - -using namespace mlir; - -#define GEN_PASS_DEF_MQTREFTOQIR -#include "mlir/Conversion/MQTRefToQIR/MQTRefToQIR.h.inc" - -/** - * @brief Look up the function declaration with a given name. If it does not - exist create one and return it. - * - * @param rewriter The PatternRewriter to use. - * @param op The operation that is matched. - * @param fnName The name of the function. - * @param fnType The type signature of the function. - * @return The LLVM funcOp declaration with the requested name and signature. - */ -static LLVM::LLVMFuncOp getFunctionDeclaration(PatternRewriter& rewriter, - Operation* op, StringRef fnName, - Type fnType) { - // check if the function already exists - auto* fnDecl = - SymbolTable::lookupNearestSymbolFrom(op, rewriter.getStringAttr(fnName)); - - if (fnDecl == nullptr) { - // if not create the declaration at the end of the module - const PatternRewriter::InsertionGuard insertGuard(rewriter); - auto module = op->getParentOfType(); - rewriter.setInsertionPointToEnd(module.getBody()); - - fnDecl = rewriter.create(op->getLoc(), fnName, fnType); - - // add the irreversible attribute to irreversible quantum operations - if (fnName == "__quantum__qis__mz__body" || - fnName == "__quantum__rt__qubit_release_array" || - fnName == "__quantum__rt__qubit_release" || - fnName == "__quantum__qis__reset__body") { - fnDecl->setAttr("passthrough", - rewriter.getStrArrayAttr({"irreversible"})); - } - } - - return static_cast(fnDecl); -} - -namespace { - -struct LoweringState { - // map a given index to a pointer value, to reuse the value instead of - // creating a new one every time - DenseMap ptrMap; - // map a given index to an address to record the classical output - DenseMap outputMap; - // Index for the next measure operation - size_t index{}; - // number of stored results in the module - size_t numResults{}; - // number of qubits in the module - size_t numQubits{}; - // boolean to check if the module uses dynamically addressed qubits - bool useDynamicQubit{}; - // boolean to check if the module uses dynamically addressed results - bool useDynamicResult{}; -}; - -template -class StatefulOpConversionPattern : public OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - -public: - StatefulOpConversionPattern(TypeConverter& typeConverter, MLIRContext* ctx, - LoweringState* state) - : OpConversionPattern(typeConverter, ctx), state_(state) {} - - /// @brief Return the state object as reference. - [[nodiscard]] LoweringState& getState() const { return *state_; } - -private: - LoweringState* state_; -}; - -} // namespace - -static bool isQubitType(MemRefType type) { - return llvm::isa(type.getElementType()); -} - -static bool isQubitType(memref::AllocOp op) { - return isQubitType(op.getType()); -} - -static bool isQubitType(memref::DeallocOp op) { - const auto& memRef = op.getMemref(); - const auto& memRefType = llvm::cast(memRef.getType()); - return isQubitType(memRefType); -} - -static bool isQubitType(memref::LoadOp op) { - const auto& memRef = op.getMemref(); - const auto& memRefType = llvm::cast(memRef.getType()); - return isQubitType(memRefType); -} - -struct MQTRefToQIRTypeConverter final : LLVMTypeConverter { - explicit MQTRefToQIRTypeConverter(MLIRContext* ctx) : LLVMTypeConverter(ctx) { - // QubitType conversion - addConversion([ctx](ref::QubitType /*type*/) { - return LLVM::LLVMPointerType::get(ctx); - }); - - // MemRefType conversion - addConversion([ctx](MemRefType type) -> Type { - if (isQubitType(type)) { - return LLVM::LLVMPointerType::get(ctx); - } - return type; - }); - } -}; - -struct ConvertMemRefAllocQIR final - : StatefulOpConversionPattern { - using StatefulOpConversionPattern::StatefulOpConversionPattern; - - static constexpr StringLiteral FN_NAME_ALLOCATE_ARRAY = - "__quantum__rt__qubit_allocate_array"; - - LogicalResult - matchAndRewrite(memref::AllocOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - if (!isQubitType(op)) { - return failure(); - } - - auto* ctx = getContext(); - - // create signature of the new function - const auto qirSignature = LLVM::LLVMFunctionType::get( - LLVM::LLVMPointerType::get(ctx), IntegerType::get(ctx, 64)); - - // get the function declaration - const auto fnDecl = getFunctionDeclaration( - rewriter, op, FN_NAME_ALLOCATE_ARRAY, qirSignature); - - // get size - int64_t size = 0; - mlir::Value sizeValue; - if (op.getType().hasStaticShape()) { - size = op.getType().getShape().front(); - sizeValue = rewriter.create( - op.getLoc(), rewriter.getI64IntegerAttr(size)); - } else { - auto opSizeValue = op.getDynamicSizes().front(); - auto constantOp = opSizeValue.getDefiningOp(); - if (!constantOp) { - llvm::errs() << "Dynamic size could not be resolved. Size is not a " - "ConstantOp.\n"; - return failure(); - } - auto integerAttr = dyn_cast(constantOp.getValue()); - if (!integerAttr) { - llvm::errs() << "Dynamic size could not be resolved. Size cannot be " - "cast to IntegerAttr.\n"; - return failure(); - } - size = integerAttr.getInt(); - sizeValue = adaptor.getDynamicSizes().front(); - } - getState().numQubits += size; - - // replace the old operation with new callOp - rewriter.replaceOpWithNewOp(op, fnDecl, - ValueRange{sizeValue}); - - getState().useDynamicQubit = true; - - return success(); - } -}; - -struct ConvertMQTRefQubitQIR final : StatefulOpConversionPattern { - using StatefulOpConversionPattern::StatefulOpConversionPattern; - - LogicalResult - matchAndRewrite(ref::QubitOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - auto* ctx = getContext(); - const auto index = adaptor.getIndex(); - - // get a pointer to qubit - if (getState().ptrMap.contains(index)) { - // check if the pointer already exist, if yes reuse them - rewriter.replaceOp(op, getState().ptrMap.at(index)); - } else { - // otherwise create a constant operation and a intToPtr operation - const auto constantOp = rewriter.create( - op.getLoc(), rewriter.getI64IntegerAttr(static_cast(index))); - const auto intToPtrOp = rewriter.replaceOpWithNewOp( - op, LLVM::LLVMPointerType::get(ctx), constantOp->getResult(0)); - - // store them in the map to reuse them later - getState().ptrMap.try_emplace(index, intToPtrOp->getResult(0)); - } - - // increase the number of required qubits - getState().numQubits++; - return success(); - } -}; - -struct ConvertMQTRefAllocQubitQIR final - : StatefulOpConversionPattern { - using StatefulOpConversionPattern::StatefulOpConversionPattern; - - static constexpr StringLiteral FN_NAME_ALLOCATE = - "__quantum__rt__qubit_allocate"; - - LogicalResult - matchAndRewrite(const ref::AllocQubitOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - auto* ctx = getContext(); - - // create signature of the new function - const auto qirSignature = - LLVM::LLVMFunctionType::get(LLVM::LLVMPointerType::get(ctx), {}); - - // get the function declaration - const auto fnDecl = - getFunctionDeclaration(rewriter, op, FN_NAME_ALLOCATE, qirSignature); - - // replace the old operation with new callOp - rewriter.replaceOpWithNewOp(op, fnDecl, ValueRange{}); - - // increase the number of required qubits - getState().numQubits++; - - getState().useDynamicQubit = true; - - return success(); - } -}; - -struct ConvertMQTRefDeallocQubitQIR final - : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - static constexpr StringLiteral FN_NAME_QUBIT_RELEASE = - "__quantum__rt__qubit_release"; - - LogicalResult - matchAndRewrite(const ref::DeallocQubitOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - auto* ctx = getContext(); - - // create signature of the new function - const auto qirSignature = LLVM::LLVMFunctionType::get( - LLVM::LLVMVoidType::get(ctx), LLVM::LLVMPointerType::get(ctx)); - - // get the function declaration - const auto fnDecl = getFunctionDeclaration( - rewriter, op, FN_NAME_QUBIT_RELEASE, qirSignature); - - // replace the old operation with new callOp - rewriter.replaceOpWithNewOp(op, fnDecl, - adaptor.getOperands()); - return success(); - } -}; - -struct ConvertMemRefDeallocQIR final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - static constexpr StringLiteral FN_NAME_QUBIT_RELEASE_ARRAY = - "__quantum__rt__qubit_release_array"; - - LogicalResult - matchAndRewrite(memref::DeallocOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - if (!isQubitType(op)) { - return failure(); - } - - auto* ctx = getContext(); - - // create signature of the new function - const auto qirSignature = LLVM::LLVMFunctionType::get( - LLVM::LLVMVoidType::get(ctx), LLVM::LLVMPointerType::get(ctx)); - - // get the function declaration - const auto fnDecl = getFunctionDeclaration( - rewriter, op, FN_NAME_QUBIT_RELEASE_ARRAY, qirSignature); - - // replace the old operation with new callOp - rewriter.replaceOpWithNewOp(op, fnDecl, - adaptor.getOperands()); - return success(); - } -}; - -struct ConvertMQTRefResetQIR final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - static constexpr StringLiteral FN_NAME_RESET = "__quantum__qis__reset__body"; - - LogicalResult - matchAndRewrite(ref::ResetOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - auto* ctx = getContext(); - - // create signature of the new function - const auto qirSignature = LLVM::LLVMFunctionType::get( - LLVM::LLVMVoidType::get(ctx), LLVM::LLVMPointerType::get(ctx)); - - // get the function declaration - const auto fnDecl = - getFunctionDeclaration(rewriter, op, FN_NAME_RESET, qirSignature); - - // replace the old operation with new callOp - rewriter.replaceOpWithNewOp(op, fnDecl, - adaptor.getOperands()); - return success(); - } -}; - -struct ConvertMemRefLoadQIR final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - static constexpr StringLiteral FN_NAME_ARRAY_GET_ELEMENT_PTR = - "__quantum__rt__array_get_element_ptr_1d"; - - LogicalResult - matchAndRewrite(memref::LoadOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - if (!isQubitType(op)) { - return failure(); - } - - auto* ctx = getContext(); - - // create signature of the new function - const auto qirSignature = LLVM::LLVMFunctionType::get( - LLVM::LLVMPointerType::get(ctx), - {LLVM::LLVMPointerType::get(ctx), IntegerType::get(ctx, 64)}); - - // get the function declaration - const auto fnDecl = getFunctionDeclaration( - rewriter, op, FN_NAME_ARRAY_GET_ELEMENT_PTR, qirSignature); - - // get memref from adaptor - const auto memref = adaptor.getMemref(); - - // create the new callOp - const auto elemPtr = - rewriter - .create( - op.getLoc(), fnDecl, - ValueRange{memref, adaptor.getIndices().front()}) - .getResult(); - - // replace the old operation with a loadOp - rewriter.replaceOpWithNewOp( - op, LLVM::LLVMPointerType::get(ctx), elemPtr); - - return success(); - } -}; - -template -struct ConvertMQTRefGateOpQIR final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - static constexpr StringLiteral FN_NAME_X_GATE = "__quantum__qis__x__body"; - - LogicalResult - matchAndRewrite(MQTRefGateOp op, typename MQTRefGateOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - auto* ctx = rewriter.getContext(); - - // get all the values - const auto& params = adaptor.getParams(); - const auto& inQubits = adaptor.getInQubits(); - const auto& posCtrlQubits = adaptor.getPosCtrlInQubits(); - const auto& negCtrlQubits = adaptor.getNegCtrlInQubits(); - auto staticParams = op.getStaticParams() - ? DenseF64ArrayAttr::get(rewriter.getContext(), - *op.getStaticParams()) - : DenseF64ArrayAttr{}; - auto paramMask = op.getParamsMask() - ? DenseBoolArrayAttr::get(rewriter.getContext(), - *op.getParamsMask()) - : DenseBoolArrayAttr{}; - - SmallVector newParams; - - // check for static parameters - if (staticParams) { - // set the insertionpoint to the beginning of the first block - auto funcOp = op->template getParentOfType(); - auto& firstBlock = *(funcOp.getBlocks().begin()); - rewriter.setInsertionPointToStart(&firstBlock); - - // create constant operations for every static parameter - auto createF64Const = [&](double v) { - auto fAttr = rewriter.getF64FloatAttr(v); - return rewriter.create(op.getLoc(), fAttr); - }; - auto staticParamValues = llvm::to_vector_of( - llvm::map_range(staticParams.asArrayRef(), - [&](double v) { return createF64Const(v); })); - - // reset the insertionpoint after creating the constants - rewriter.setInsertionPoint(op); - - // merge parameters with static parameters - if (!paramMask) { - // assuming every value is true when the paramMask does not exist but - // staticParameters do - newParams = staticParamValues; - } else { - newParams.reserve(paramMask.size()); - auto staticParamIndex = 0; - auto paramIndex = 0; - // merge the parameter values depending on the paramMask - for (auto isStatic : paramMask.asArrayRef()) { - newParams.push_back(isStatic ? staticParamValues[staticParamIndex++] - : params[paramIndex++]); - } - } - } else { - newParams = params; - } - - // concatenate all the values - SmallVector operands; - operands.reserve(newParams.size() + inQubits.size() + posCtrlQubits.size() + - negCtrlQubits.size()); - - operands.append(posCtrlQubits.begin(), posCtrlQubits.end()); - operands.append(negCtrlQubits.begin(), negCtrlQubits.end()); - operands.append(inQubits.begin(), inQubits.end()); - operands.append(newParams.begin(), newParams.end()); - - // check for negative controlled qubits - // for any negative controlled qubits place an X operation before and - // after the matched gate operation - if (negCtrlQubits.size() > 0) { - // create signature of the X gate operation - const auto notGateSignature = LLVM::LLVMFunctionType::get( - LLVM::LLVMVoidType::get(ctx), LLVM::LLVMPointerType::get(ctx)); - - // get the function declaration - const auto notGateDecl = getFunctionDeclaration( - rewriter, op, FN_NAME_X_GATE, notGateSignature); - - // place a NOT operation before and after the operation for each negative - // controlled qubit - for (const auto negCtrlQubit : negCtrlQubits) { - rewriter.setInsertionPoint(op); - rewriter.create(op.getLoc(), notGateDecl, negCtrlQubit); - rewriter.setInsertionPointAfter(op); - rewriter.create(op.getLoc(), notGateDecl, negCtrlQubit); - } - // reset the insertionpoint - rewriter.setInsertionPoint(op); - } - - // get the name of the gate - const StringRef name = op.getIdentifier(); - - // add leading c's to the function name depending on the number of control - // qubits - const auto ctrQubitsCount = posCtrlQubits.size() + negCtrlQubits.size(); - std::string fnName(ctrQubitsCount, 'c'); - - // check if it is a u gate - if (name == "u") { - fnName = ("__quantum__qis__" + fnName + "u3__body"); - } else { - fnName = ("__quantum__qis__" + fnName + name + "__body").str(); - } - - // create the signature of the function - const auto qirSignature = LLVM::LLVMFunctionType::get( - LLVM::LLVMVoidType::get(ctx), - llvm::to_vector(ValueRange(operands).getTypes())); - - // get the function declaration - const auto fnDecl = - getFunctionDeclaration(rewriter, op, fnName, qirSignature); - - // replace the old operation with a callOp - rewriter.replaceOpWithNewOp(op, fnDecl, operands); - - return success(); - } -}; - -struct ConvertMQTRefMeasureQIR final - : StatefulOpConversionPattern { - using StatefulOpConversionPattern::StatefulOpConversionPattern; - - static constexpr StringLiteral FN_NAME_MEASURE = "__quantum__qis__mz__body"; - static constexpr StringLiteral FN_NAME_RECORD_OUTPUT = - "__quantum__rt__result_record_output"; - - /** - * @brief returns the next addressOfOp for a global constant to store the - * results of the measure operation. - * - * @param op The current measure operation that is converted. - * @param rewriter The PatternRewriter to use. - * @param state The LoweringState of the current conversion pass. - * @return The addressOfOp of the next global constant. - */ - static Operation* getAddressOfOp(Operation* op, - ConversionPatternRewriter& rewriter, - LoweringState& state) { - - // create a new globalOp and an addressOfOp - // set the insertionpoint to the beginning of the module - auto module = op->getParentOfType(); - rewriter.setInsertionPointToStart(module.getBody()); - - // check how many digits the next index has for the array allocation - auto num = state.index; - auto digits = 1; - while (num >= 10) { - num /= 10; - ++digits; - } - - // create the necessary names and types for the global operation - // symbol name should be mlir.llvm.nameless_global_0, - // mlir.llvm.nameless_global_1 etc. - const auto symbolName = rewriter.getStringAttr( - "mlir.llvm.nameless_global_" + std::to_string(state.index)); - const auto llvmArrayType = - LLVM::LLVMArrayType::get(rewriter.getIntegerType(8), digits + 2); - // initializer name should be r0\00, r1\00 etc. - const auto stringInitializer = - rewriter.getStringAttr("r" + std::to_string(state.index) + '\0'); - - // create the global operation - auto globalOp = rewriter.create( - op->getLoc(), llvmArrayType, - /*isConstant=*/true, LLVM::Linkage::Internal, symbolName, - stringInitializer); - globalOp->setAttr("addr_space", rewriter.getI32IntegerAttr(0)); - globalOp->setAttr("dso_local", rewriter.getUnitAttr()); - - // get the first block of the main function - auto main = op->getParentOfType(); - auto& firstBlock = *(main.getBlocks().begin()); - - // insert the addressOfOp of the newly created global op at the beginning - // of the block - rewriter.setInsertionPointToStart(&firstBlock); - const auto addressOfOp = rewriter.create( - op->getLoc(), LLVM::LLVMPointerType::get(rewriter.getContext()), - symbolName); - - // reset the insertionpoint to the initial operation again - rewriter.setInsertionPoint(op); - - // increment the index of the next addressOfOp - state.index++; - - return addressOfOp; - } - - LogicalResult - matchAndRewrite(ref::MeasureOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - auto* ctx = rewriter.getContext(); - - // match and replace the measure operation to the following operations - // call void @__quantum__qis__mz__body(ptr %qubit , ptr %result) - // call void @__quantum__rt__result_record_output(ptr %result , ptr - // @constant) - LLVM::LLVMFunctionType qirSignature; - LLVM::LLVMFuncOp fnDecl; - auto const ptrType = LLVM::LLVMPointerType::get(ctx); - auto& ptrMap = getState().ptrMap; - auto& outputMap = getState().outputMap; - Value resultValue = nullptr; - // get a pointer to result - if (ptrMap.contains(getState().numResults)) { - // check if the pointer already exist, if yes reuse them - resultValue = ptrMap.at(getState().numResults); - } else { - // otherwise create a constant operation and an intToPtr operation - auto constantOp = rewriter.create( - op.getLoc(), rewriter.getI64IntegerAttr( - static_cast(getState().numResults))); - resultValue = rewriter - .create(op.getLoc(), ptrType, - constantOp->getResult(0)) - .getResult(); - - // store them in the map to reuse them later - ptrMap.try_emplace(getState().numResults, resultValue); - } - // create the measure operation - qirSignature = LLVM::LLVMFunctionType::get(LLVM::LLVMVoidType::get(ctx), - {ptrType, ptrType}); - fnDecl = - getFunctionDeclaration(rewriter, op, FN_NAME_MEASURE, qirSignature); - rewriter.create( - op->getLoc(), fnDecl, ValueRange{adaptor.getInQubit(), resultValue}); - - // create the record output operation - qirSignature = LLVM::LLVMFunctionType::get(LLVM::LLVMVoidType::get(ctx), - {ptrType, ptrType}); - fnDecl = getFunctionDeclaration(rewriter, op, FN_NAME_RECORD_OUTPUT, - qirSignature); - - size_t index = 0; - - // get the index of the register where the result is stored - for (const auto* users : op->getResult(0).getUsers()) { - if (auto storeOp = dyn_cast(users)) { - auto constantOp = - storeOp.getIndices()[0].getDefiningOp(); - if (!constantOp) { - llvm::errs() - << "Index could not be resolved. Index is not a ConstantOp.\n"; - return failure(); - } - - auto integerAttr = dyn_cast(constantOp.getValue()); - if (!integerAttr) { - llvm::errs() << "Index could not be resolved. Index cannot be cast " - "to IntegerAttr.\n"; - return failure(); - } - index = integerAttr.getInt(); - - const auto allocaOp = storeOp.getMemref(); - - // drop the references to update the number of uses immediately - // as eraseOp only updates them after the patterns - storeOp->dropAllReferences(); - rewriter.eraseOp(storeOp); - - // erase the alloca op if all store operations are removed - if (allocaOp.use_empty()) { - rewriter.eraseOp(allocaOp.getDefiningOp()); - } - - // delete the constant op if there are no users left - if (constantOp->use_empty()) { - rewriter.eraseOp(constantOp); - } - break; - } - } - Operation* addressOfOp = nullptr; - // get the pointer to the internal constant - if (outputMap.contains(index)) { - // reuse the address if the index was already used - addressOfOp = outputMap.at(index); - } else { - // otherwise create a new addressofOp - addressOfOp = getAddressOfOp(op, rewriter, getState()); - // store them in the map to reuse them later - outputMap.try_emplace(index, addressOfOp); - // increase the number of required results - getState().numResults++; - } - - // create record result output in the last block of the main function - // the parent of the current operation is only guaranteed to be the main - // function in the base profile and this needs to be adapted for the - // adaptive profile of QIR - auto func = op->getParentOfType(); - rewriter.setInsertionPointToStart(&func.back()); - - rewriter.create( - op.getLoc(), fnDecl, - ValueRange{resultValue, addressOfOp->getResult(0)}); - - // erase the old operation - rewriter.eraseOp(op); - - return success(); - } -}; - -struct MQTRefToQIR final : impl::MQTRefToQIRBase { - using MQTRefToQIRBase::MQTRefToQIRBase; - - static constexpr StringLiteral FN_NAME_INITIALIZE = - "__quantum__rt__initialize"; - - /** - * @brief Finds the main function in the module - * - * @param op The module operation that holds all operations. - * @return The main function. - */ - static LLVM::LLVMFuncOp getMainFunction(Operation* op) { - auto module = dyn_cast(op); - // find the main function - for (auto funcOp : module.getOps()) { - auto passthrough = funcOp->getAttrOfType("passthrough"); - if (!passthrough) { - continue; - } - if (llvm::any_of(passthrough, [](Attribute attr) { - auto strAttr = dyn_cast(attr); - return strAttr && strAttr.getValue() == "entry_point"; - })) { - return funcOp; - } - } - return nullptr; - } - - /** - * @brief Makes sure that the different blocks for the base profile of QIR - * exist - * - * The first block should only contain constant operations for the initialize - * operation. The second block contains the reversible quantum operations. The - * third block contains the irreversible quantum operations like the measure - * operation or the dealloc operation. The final block contains the - * return operation and the record output operation. The blocks are connected - * with an unconditional jump operation to the next block. - * - * @param main The main function of the module. - */ - static void ensureBlocks(LLVM::LLVMFuncOp& main) { - // return if there are more blocks already - if (main.getBlocks().size() > 1) { - return; - } - - // get the existing block - auto* mainBlock = &main.front(); - OpBuilder builder(main.getBody()); - - // create the remaining blocks - auto* entryBlock = builder.createBlock(&main.getBody()); - // move the entryblock before the mainblock - main.getBlocks().splice(Region::iterator(mainBlock), main.getBlocks(), - entryBlock); - Block* irreversibleBlock = builder.createBlock(&main.getBody()); - Block* endBlock = builder.createBlock(&main.getBody()); - - auto& mainBlockOps = mainBlock->getOperations(); - auto& endBlockOps = endBlock->getOperations(); - auto& irreversibleBlockOps = irreversibleBlock->getOperations(); - - for (auto it = mainBlock->begin(); it != mainBlock->end();) { - // make sure that the iterator is valid - auto& op = *it++; - - if (op.getDialect()->getNamespace() == "memref") { - if (auto allocOp = dyn_cast(op)) { - if (isQubitType(allocOp)) { - continue; - } - } else if (auto loadOp = dyn_cast(op)) { - if (isQubitType(loadOp)) { - continue; - } - } else { - irreversibleBlockOps.splice(irreversibleBlock->end(), mainBlockOps, - Block::iterator(op)); - } - } else if (dyn_cast(op) || - dyn_cast(op) || dyn_cast(op)) { - // move irreversible quantum operations to the irreversible block - irreversibleBlockOps.splice(irreversibleBlock->end(), mainBlockOps, - Block::iterator(op)); - } else if (dyn_cast(op)) { - // move the return op to the endblock - endBlockOps.splice(endBlock->end(), mainBlockOps, Block::iterator(op)); - } - } - - // add jump from entryBlock to mainBlock - builder.setInsertionPointToEnd(entryBlock); - builder.create(main->getLoc(), mainBlock); - - // add jump from main to irreversibleBlock - builder.setInsertionPointToEnd(mainBlock); - builder.create(main->getLoc(), irreversibleBlock); - - // add jump from irreversibleBlock to endBlock - builder.setInsertionPointToEnd(irreversibleBlock); - builder.create(main->getLoc(), endBlock); - } - - /** - * @brief Adds the initialize operation to the first block of the main - * function. - * - * @param main The main function of the module. - * @param ctx The context of the module. - */ - static void addInitialize(LLVM::LLVMFuncOp& main, MLIRContext* ctx, - LoweringState* state) { - auto moduleOp = main->getParentOfType(); - - auto& firstBlock = *(main.getBlocks().begin()); - OpBuilder builder(main.getBody()); - - // create the zero op - builder.setInsertionPointToStart(&firstBlock); - auto zeroOperation = builder.create( - main->getLoc(), LLVM::LLVMPointerType::get(ctx)); - - // add the zero operation to the pointerMap - state->ptrMap.try_emplace(0, zeroOperation->getResult(0)); - - // create the initialize operation as the 2nd last operation in the first - // block after all constant operations and before the last jump operation - const auto insertPoint = std::prev(firstBlock.getOperations().end(), 1); - builder.setInsertionPoint(&*insertPoint); - - // get the function declaration of initialize otherwise create one - auto* fnDecl = SymbolTable::lookupNearestSymbolFrom( - main, builder.getStringAttr(FN_NAME_INITIALIZE)); - if (fnDecl == nullptr) { - const PatternRewriter::InsertionGuard insertGuard(builder); - builder.setInsertionPointToEnd(moduleOp.getBody()); - auto fnSignature = LLVM::LLVMFunctionType::get( - LLVM::LLVMVoidType::get(ctx), LLVM::LLVMPointerType::get(ctx)); - fnDecl = builder.create( - main->getLoc(), FN_NAME_INITIALIZE, fnSignature); - } - // create and insert the initialize operation - builder.create(main->getLoc(), - static_cast(fnDecl), - ValueRange{zeroOperation->getResult(0)}); - } - /** - * @brief Sets the necessary attributes to the main function for the QIR base - * profile. The required module flags are also set as attributes. - * - * @param main The main function of the module. - * @param state The lowering state of the conversion pass. - */ - static void setAttributes(LLVM::LLVMFuncOp& main, LoweringState* state) { - OpBuilder builder(main.getBody()); - SmallVector attributes; - attributes.emplace_back(builder.getStringAttr("entry_point")); - attributes.emplace_back( - builder.getStrArrayAttr({"output_labeling_schema", "schema_id"})); - attributes.emplace_back( - builder.getStrArrayAttr({"qir_profiles", "base_profile"})); - attributes.emplace_back(builder.getStrArrayAttr( - {"required_num_qubits", std::to_string(state->numQubits)})); - attributes.emplace_back(builder.getStrArrayAttr( - {"required_num_results", std::to_string(state->numResults)})); - attributes.emplace_back( - builder.getStrArrayAttr({"qir_major_version", "1"})); - attributes.emplace_back( - builder.getStrArrayAttr({"qir_minor_version", "0"})); - attributes.emplace_back( - builder.getStrArrayAttr({"dynamic_qubit_management", - state->useDynamicQubit ? "true" : "false"})); - attributes.emplace_back( - builder.getStrArrayAttr({"dynamic_result_management", - state->useDynamicResult ? "true" : "false"})); - - main->setAttr("passthrough", builder.getArrayAttr(attributes)); - } - - void runOnOperation() override { - MLIRContext* ctx = &getContext(); - auto* moduleOp = getOperation(); - ConversionTarget target(*ctx); - RewritePatternSet funcPatterns(ctx); - RewritePatternSet mqtPatterns(ctx); - RewritePatternSet stdPatterns(ctx); - MQTRefToQIRTypeConverter typeConverter(ctx); - - target.addLegalDialect(); - - // convert func to LLVM - target.addIllegalDialect(); - - populateFuncToLLVMConversionPatterns(typeConverter, funcPatterns); - - if (applyPartialConversion(moduleOp, target, std::move(funcPatterns)) - .failed()) { - signalPassFailure(); - } - - // convert MQTRef to LLVM - auto main = getMainFunction(moduleOp); - ensureBlocks(main); - LoweringState state; - addInitialize(main, ctx, &state); - - target.addIllegalDialect(); - - target.addDynamicallyLegalOp( - [&](memref::AllocOp op) { return !isQubitType(op); }); - target.addDynamicallyLegalOp( - [&](memref::DeallocOp op) { return !isQubitType(op); }); - target.addDynamicallyLegalOp( - [&](memref::LoadOp op) { return !isQubitType(op); }); - - mqtPatterns.add(typeConverter, ctx, &state); - mqtPatterns.add(typeConverter, ctx); - mqtPatterns.add(typeConverter, ctx, &state); - mqtPatterns.add(typeConverter, ctx); - mqtPatterns.add(typeConverter, ctx); - mqtPatterns.add(typeConverter, ctx); - mqtPatterns.add(typeConverter, ctx, &state); - mqtPatterns.add(typeConverter, ctx, &state); - - ADD_CONVERT_PATTERN(GPhaseOp) - ADD_CONVERT_PATTERN(IOp) - ADD_CONVERT_PATTERN(BarrierOp) - ADD_CONVERT_PATTERN(HOp) - ADD_CONVERT_PATTERN(XOp) - ADD_CONVERT_PATTERN(YOp) - ADD_CONVERT_PATTERN(ZOp) - ADD_CONVERT_PATTERN(SOp) - ADD_CONVERT_PATTERN(SdgOp) - ADD_CONVERT_PATTERN(TOp) - ADD_CONVERT_PATTERN(TdgOp) - ADD_CONVERT_PATTERN(VOp) - ADD_CONVERT_PATTERN(VdgOp) - ADD_CONVERT_PATTERN(UOp) - ADD_CONVERT_PATTERN(U2Op) - ADD_CONVERT_PATTERN(POp) - ADD_CONVERT_PATTERN(SXOp) - ADD_CONVERT_PATTERN(SXdgOp) - ADD_CONVERT_PATTERN(ROp) - ADD_CONVERT_PATTERN(RXOp) - ADD_CONVERT_PATTERN(RYOp) - ADD_CONVERT_PATTERN(RZOp) - ADD_CONVERT_PATTERN(SWAPOp) - ADD_CONVERT_PATTERN(iSWAPOp) - ADD_CONVERT_PATTERN(iSWAPdgOp) - ADD_CONVERT_PATTERN(PeresOp) - ADD_CONVERT_PATTERN(PeresdgOp) - ADD_CONVERT_PATTERN(DCXOp) - ADD_CONVERT_PATTERN(ECROp) - ADD_CONVERT_PATTERN(RXXOp) - ADD_CONVERT_PATTERN(RYYOp) - ADD_CONVERT_PATTERN(RZZOp) - ADD_CONVERT_PATTERN(RZXOp) - ADD_CONVERT_PATTERN(XXminusYYOp) - ADD_CONVERT_PATTERN(XXplusYYOp) - - if (applyPartialConversion(moduleOp, target, std::move(mqtPatterns)) - .failed()) { - signalPassFailure(); - } - - setAttributes(main, &state); - - // convert arith and cf to LLVM - target.addIllegalDialect(); - target.addIllegalDialect(); - - cf::populateControlFlowToLLVMConversionPatterns(typeConverter, stdPatterns); - arith::populateArithToLLVMConversionPatterns(typeConverter, stdPatterns); - - if (applyPartialConversion(moduleOp, target, std::move(stdPatterns)) - .failed()) { - signalPassFailure(); - } - - PassManager passManager(ctx); - passManager.addPass(createReconcileUnrealizedCastsPass()); - if (passManager.run(moduleOp).failed()) { - signalPassFailure(); - } - }; -}; - -} // namespace mqt::ir diff --git a/mlir/lib/Conversion/QIRToMQTRef/CMakeLists.txt b/mlir/lib/Conversion/QIRToMQTRef/CMakeLists.txt deleted file mode 100644 index f09afec852..0000000000 --- a/mlir/lib/Conversion/QIRToMQTRef/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -file(GLOB CONVERSION_SOURCES *.cpp) - -add_mlir_library( - QIRToMQTRef - ${CONVERSION_SOURCES} - DEPENDS - QIRToMQTRefIncGen - LINK_LIBS - MLIRMQTRef - MLIRMemRefDialect - MLIRTransforms - MLIRLLVMDialect - DISABLE_INSTALL) diff --git a/mlir/lib/Conversion/QIRToMQTRef/QIRToMQTRef.cpp b/mlir/lib/Conversion/QIRToMQTRef/QIRToMQTRef.cpp deleted file mode 100644 index 825a5abe08..0000000000 --- a/mlir/lib/Conversion/QIRToMQTRef/QIRToMQTRef.cpp +++ /dev/null @@ -1,661 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -// macro to check the gatename and to replace the simple gate -#define ADD_CONVERT_SIMPLE_GATE(gate) \ - if (gateName == #gate) { \ - rewriter.replaceOpWithNewOp(op, DenseF64ArrayAttr{}, \ - DenseBoolArrayAttr{}, ValueRange{}, \ - qubits, ctrlQubits, ValueRange{}); \ - return; \ - } - -// macro to check the gateName and to replace the rotation gate -#define ADD_CONVERT_ROTATION_GATE(gate) \ - if (gateName == #gate) { \ - rewriter.replaceOpWithNewOp( \ - op, DenseF64ArrayAttr{}, DenseBoolArrayAttr{}, rotationDegrees, \ - operands, ctrlQubits, ValueRange{}); \ - return; \ - } - -#include "mlir/Conversion/QIRToMQTRef/QIRToMQTRef.h" - -#include "mlir/Dialect/MQTRef/IR/MQTRefDialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir { - -using namespace mlir; - -#define GEN_PASS_DEF_QIRTOMQTREF -#include "mlir/Conversion/QIRToMQTRef/QIRToMQTRef.h.inc" - -class QIRToMQTRefTypeConverter final : public TypeConverter { -public: - explicit QIRToMQTRefTypeConverter(MLIRContext* /*ctx*/) { - // Identity conversion - addConversion([](Type type) { return type; }); - } -}; - -namespace { - -struct LoweringState { - // map each llvm QIR result to the new mqtref result - DenseMap operandMap; - // memref operation to store the classical results - Operation* allocaOp{}; - // map a llvm result ptr to a classical result bit - DenseMap resultMap; - // next free allocate index - int64_t index{}; -}; - -template -class StatefulOpConversionPattern : public OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - -public: - StatefulOpConversionPattern(TypeConverter& typeConverter, MLIRContext* ctx, - LoweringState* state) - : OpConversionPattern(typeConverter, ctx), state_(state) {} - - /// @brief Return the state object as reference. - [[nodiscard]] LoweringState& getState() const { return *state_; } - - inline static const llvm::StringMap SIMPLE_GATES = { - {"x", "XOp"}, - {"not", "XOp"}, - {"h", "HOp"}, - {"i", "IOp"}, - {"y", "YOp"}, - {"z", "ZOp"}, - {"s", "SOp"}, - {"sdg", "SdgOp"}, - {"t", "TOp"}, - {"tdg", "TdgOp"}, - {"v", "VOp"}, - {"vdg", "VdgOp"}, - {"sx", "SXOp"}, - {"sxdg", "SXdgOp"}, - {"swap", "SWAPOp"}, - {"iswap", "iSWAPOp"}, - {"iswapdg", "iSWAPdgOp"}, - {"peres", "PeresOp"}, - {"peresdg", "PeresdgOp"}, - {"dcx", "DCXOp"}, - {"ecr", "ECROp"}}; - - inline static const llvm::StringMap SINGLE_ROTATION_GATES = { - {"p", "POp"}, {"rx", "RXOp"}, {"ry", "RYOp"}, - {"rz", "RZOp"}, {"rxx", "RXXOp"}, {"ryy", "RYYOp"}, - {"rzz", "RZZOp"}, {"rzx", "RZXOp"}, {"u1", "POp"}}; - - inline static const llvm::StringMap DOUBLE_ROTATION_GATES = { - {"r", "ROp"}, - {"u2", "U2Op"}, - {"xx_minus_yy", "XXminusYYOp"}, - {"xx_plus_yy", "XXplusYYOp"}}; - -private: - LoweringState* state_; -}; -} // namespace - -struct ConvertQIRLoad final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(LLVM::LoadOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // erase the operation and use the operands as results - rewriter.replaceOp(op, adaptor.getOperands().front()); - return success(); - } -}; - -struct ConvertQIRZero final : StatefulOpConversionPattern { - using StatefulOpConversionPattern::StatefulOpConversionPattern; - - LogicalResult - matchAndRewrite(const LLVM::ZeroOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - auto isQubitPtr = false; - - // check if the zero op is used in any functions other than for result - // recording and the initialize operation - for (auto* userOp : op->getResult(0).getUsers()) { - if (auto callOp = dyn_cast(userOp)) { - const auto fnName = callOp.getCallee(); - if (fnName != "__quantum__rt__result_record_output" && - fnName != "__quantum__qis__mz__body" && - fnName != "__quantum__rt__initialize") { - isQubitPtr = true; - break; - } - if (fnName == "__quantum__qis__mz__body" && - op->getResult(0) == callOp->getOperand(0)) { - isQubitPtr = true; - break; - } - } - } - - // if yes the IntToPtr op should be converted to a static qubit - if (isQubitPtr) { - // replace the zero operation with a static qubit with index 0 - const auto newOp = rewriter.replaceOpWithNewOp( - op, ref::QubitType::get(rewriter.getContext()), 0); - - // update the operand map - getState().operandMap.try_emplace(op->getResult(0), - newOp->getOpResult(0)); - } - // otherwise delete the operation - else { - rewriter.eraseOp(op); - } - - return success(); - } -}; - -struct ConvertQIRIntToPtr final - : StatefulOpConversionPattern { - using StatefulOpConversionPattern::StatefulOpConversionPattern; - - LogicalResult - matchAndRewrite(LLVM::IntToPtrOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - auto constantOp = op->getOperand(0).getDefiningOp(); - auto isQubitPtr = false; - - // check if the ptr is used in any functions other than for result - // recording - for (auto* userOp : op->getResult(0).getUsers()) { - if (auto callOp = dyn_cast(userOp)) { - const auto fnName = callOp.getCallee(); - if (fnName != "__quantum__rt__result_record_output" && - fnName != "__quantum__qis__mz__body") { - isQubitPtr = true; - break; - } - if (fnName == "__quantum__qis__mz__body" && - op->getResult(0) == callOp->getOperand(0)) { - isQubitPtr = true; - break; - } - } - } - - // if yes the IntToPtr op should be converted to a static qubit - if (isQubitPtr) { - // get the int value of the operation - const auto value = dyn_cast(constantOp.getValue()).getInt(); - - // replace the int to ptr operation with static qubit - const auto newOp = rewriter.replaceOpWithNewOp( - op, ref::QubitType::get(rewriter.getContext()), value); - - // update the operand map - getState().operandMap.try_emplace(op->getResult(0), - newOp->getOpResult(0)); - } - // otherwise delete the operation - else { - rewriter.eraseOp(op); - } - - // erase the constantOp for the operation if op was the only user - if (constantOp->getResult(0).hasOneUse()) { - rewriter.eraseOp(constantOp); - } - - return success(); - } -}; - -struct ConvertQIRAddressOf final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(LLVM::AddressOfOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - // erase the operation - rewriter.eraseOp(op); - return success(); - } -}; - -struct ConvertQIRGlobal final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(LLVM::GlobalOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - // erase the operation - rewriter.eraseOp(op); - return success(); - } -}; - -struct ConvertQIRCall final : StatefulOpConversionPattern { - using StatefulOpConversionPattern::StatefulOpConversionPattern; - - // constants to trim the function name to get the name of the gate - constexpr static size_t QIS_OPERATION_PREFIX_LENGTH = 16; - constexpr static size_t QIS_OPERATION_SUFFIX_LENGTH = 6; - // constant to trim the global constant name to get the index of the global - // constant - constexpr static size_t GLOBAL_CONSTANT_PREFIX_LENGTH = 26; - - /** - * @brief Replaces the call operation with a matching simple gate operation - * from the mqtref dialect - * - * @param op The call operation that is replaced. - * @param gateName The name of the gate. - * @param qubits The Qubits of the given operation. - * @param ctrlQubits The control Qubits of the given operation. - * @param rewriter The PatternRewriter to use. - */ - static void convertSimpleGate(LLVM::CallOp& op, const StringRef gateName, - const SmallVector& qubits, - const SmallVector& ctrlQubits, - ConversionPatternRewriter& rewriter) { - // match and replace the fitting gate - ADD_CONVERT_SIMPLE_GATE(XOp) - ADD_CONVERT_SIMPLE_GATE(HOp) - ADD_CONVERT_SIMPLE_GATE(IOp) - ADD_CONVERT_SIMPLE_GATE(YOp) - ADD_CONVERT_SIMPLE_GATE(ZOp) - ADD_CONVERT_SIMPLE_GATE(SOp) - ADD_CONVERT_SIMPLE_GATE(SdgOp) - ADD_CONVERT_SIMPLE_GATE(TOp) - ADD_CONVERT_SIMPLE_GATE(TdgOp) - ADD_CONVERT_SIMPLE_GATE(VOp) - ADD_CONVERT_SIMPLE_GATE(VdgOp) - ADD_CONVERT_SIMPLE_GATE(SXOp) - ADD_CONVERT_SIMPLE_GATE(SXdgOp) - ADD_CONVERT_SIMPLE_GATE(SWAPOp) - ADD_CONVERT_SIMPLE_GATE(iSWAPOp) - ADD_CONVERT_SIMPLE_GATE(iSWAPdgOp) - ADD_CONVERT_SIMPLE_GATE(PeresOp) - ADD_CONVERT_SIMPLE_GATE(PeresdgOp) - ADD_CONVERT_SIMPLE_GATE(DCXOp) - ADD_CONVERT_SIMPLE_GATE(ECROp) - } - - /** - * @brief Replaces the call operation with a matching rotation gate operation - * from the mqtref dialect - * - * @param op The call operation that is replaced. - * @param gateName The name of the gate. - * @param operands The operands of the given operation. - * @param ctrlQubits The control Qubits of the given operation. - * @param rotationCount The number of rotation degrees. - * @param rewriter The PatternRewriter to use. - */ - static void convertRotationGate(LLVM::CallOp& op, const StringRef gateName, - SmallVector& operands, - const SmallVector& ctrlQubits, - const size_t rotationCount, - ConversionPatternRewriter& rewriter) { - // extract the degrees from the operand list - SmallVector rotationDegrees; - rotationDegrees.reserve(rotationCount); - rotationDegrees.insert( - rotationDegrees.end(), - std::make_move_iterator(std::prev( - operands.end(), static_cast(rotationCount))), - std::make_move_iterator(operands.end())); - operands.resize(operands.size() - rotationCount); - - // match and replace the fitting gate - ADD_CONVERT_ROTATION_GATE(POp) - ADD_CONVERT_ROTATION_GATE(UOp) - ADD_CONVERT_ROTATION_GATE(U2Op) - ADD_CONVERT_ROTATION_GATE(ROp) - ADD_CONVERT_ROTATION_GATE(RXOp) - ADD_CONVERT_ROTATION_GATE(RYOp) - ADD_CONVERT_ROTATION_GATE(RZOp) - ADD_CONVERT_ROTATION_GATE(RXXOp) - ADD_CONVERT_ROTATION_GATE(RYYOp) - ADD_CONVERT_ROTATION_GATE(RZZOp) - ADD_CONVERT_ROTATION_GATE(RZXOp) - ADD_CONVERT_ROTATION_GATE(XXminusYYOp) - ADD_CONVERT_ROTATION_GATE(XXplusYYOp) - } - - LogicalResult - matchAndRewrite(LLVM::CallOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - auto* ctx = rewriter.getContext(); - - // get the name of the operation and prepare the return types - const auto fnName = op.getCallee(); - const auto qubitType = ref::QubitType::get(ctx); - const auto operands = adaptor.getOperands(); - - // get the new operands from the operandMap - // workaround as we need to convert !llvm.ptr to either !mqtref.Qubit or - // !mqtref.QubitRegister type depending on the operation and this is not - // doable with the typeConverter - SmallVector newOperands; - newOperands.reserve(operands.size()); - for (auto const& val : operands) { - if (getState().operandMap.contains(val)) { - newOperands.emplace_back(getState().operandMap.at(val)); - } else { - newOperands.emplace_back(val); - } - } - - // match initialize operation - if (fnName == "__quantum__rt__initialize") { - op.erase(); - return success(); - } - // match alloc register - if (fnName == "__quantum__rt__qubit_allocate_array") { - const auto memRefType = - MemRefType::get({ShapedType::kDynamic}, qubitType); - const auto sizeInt = adaptor.getOperands().front(); - auto size = rewriter.create( - op.getLoc(), rewriter.getIndexType(), sizeInt); - const auto newOp = rewriter.replaceOpWithNewOp( - op, memRefType, ValueRange{size}); - - // update the operand list - getState().operandMap.try_emplace(op->getResult(0), newOp->getResult(0)); - return success(); - } - // match alloc qubit - if (fnName == "__quantum__rt__qubit_allocate") { - const auto newOp = - rewriter.replaceOpWithNewOp(op, qubitType); - - // update the operand list - getState().operandMap.try_emplace(op->getResult(0), newOp->getResult(0)); - return success(); - } - // match qubit release operation - if (fnName == "__quantum__rt__qubit_release") { - rewriter.replaceOpWithNewOp(op, newOperands.front()); - return success(); - } - // match dealloc register - if (fnName == "__quantum__rt__qubit_release_array") { - rewriter.replaceOpWithNewOp(op, newOperands.front()); - return success(); - } - // match reset operation - if (fnName == "__quantum__qis__reset__body") { - rewriter.replaceOpWithNewOp(op, newOperands.front()); - return success(); - } - // match extract qubit from register - if (fnName == "__quantum__rt__array_get_element_ptr_1d") { - const auto indexInt = newOperands.back(); - auto index = rewriter.create( - op.getLoc(), rewriter.getIndexType(), indexInt); - const auto newOp = rewriter.replaceOpWithNewOp( - op, newOperands.front(), ValueRange{index}); - - // update the operand list - getState().operandMap.try_emplace(op->getResult(0), - newOp->getOpResult(0)); - return success(); - } - // match measure operation - if (fnName == "__quantum__qis__mz__body") { - const auto bitType = IntegerType::get(ctx, 1); - - // create the measure operation - const auto measureOp = rewriter.create( - op.getLoc(), bitType, newOperands.front()); - - // store the result pointer and the measurement result in the map - getState().resultMap.try_emplace(newOperands[1], measureOp->getResult(0)); - - rewriter.eraseOp(op); - return success(); - } - // match record result output operation - if (fnName == "__quantum__rt__result_record_output") { - - if (getState().resultMap.contains(newOperands[0])) { - // get the matching measurement result - auto measureResult = getState().resultMap.at(newOperands[0]); - // get the index where the measurement is stored from the name of the - // global op - auto addressOfOp = op->getOperand(1).getDefiningOp(); - if (op->getOperand(1).getDefiningOp()) { - auto const index = stoi(addressOfOp.getGlobalName() - .substr(GLOBAL_CONSTANT_PREFIX_LENGTH) - .str()); - auto constantIndexOp = - rewriter.create(op.getLoc(), index); - - // create the store operation for the classical result - rewriter.create( - op.getLoc(), measureResult, getState().allocaOp->getResult(0), - ValueRange{constantIndexOp->getResult(0)}); - } - } - rewriter.eraseOp(op); - return success(); - } - - // remove the prefix and the suffix of the gate name - auto gateName(fnName->substr(QIS_OPERATION_PREFIX_LENGTH) - .drop_back(QIS_OPERATION_SUFFIX_LENGTH)); - - // check how many control qubits are used by counting the number of - // leading c's - const size_t ctrlQubitCount = - std::ranges::find_if(gateName, [](char ch) { return ch != 'c'; }) - - gateName.begin(); - - // remove the control qubits from the name - gateName = gateName.substr(ctrlQubitCount); - - // extract the controlqubits from the operand list - SmallVector ctrlQubits; - auto* const firstCtrl = newOperands.begin(); - auto* const lastCtrl = - std::next(firstCtrl, static_cast(ctrlQubitCount)); - ctrlQubits.reserve(ctrlQubitCount); - ctrlQubits.insert(ctrlQubits.end(), std::make_move_iterator(firstCtrl), - std::make_move_iterator(lastCtrl)); - newOperands.erase(firstCtrl, lastCtrl); - - // try to match and replace gate operations - if (gateName == "gphase") { - rewriter.replaceOpWithNewOp( - op, DenseF64ArrayAttr{}, DenseBoolArrayAttr{}, newOperands, - ValueRange{}, ctrlQubits, ValueRange{}); - return success(); - } - if (gateName == "barrier") { - rewriter.replaceOpWithNewOp( - op, DenseF64ArrayAttr{}, DenseBoolArrayAttr{}, ValueRange{}, - newOperands, ctrlQubits, ValueRange{}); - return success(); - } - if (SIMPLE_GATES.contains(gateName.str())) { - convertSimpleGate(op, SIMPLE_GATES.at(gateName.str()), newOperands, - ctrlQubits, rewriter); - return success(); - } - if (SINGLE_ROTATION_GATES.contains(gateName.str())) { - convertRotationGate(op, SINGLE_ROTATION_GATES.at(gateName.str()), - newOperands, ctrlQubits, 1, rewriter); - return success(); - } - if (DOUBLE_ROTATION_GATES.contains(gateName.str())) { - convertRotationGate(op, DOUBLE_ROTATION_GATES.at(gateName.str()), - newOperands, ctrlQubits, 2, rewriter); - return success(); - } - if (gateName == "u3" || gateName == "u") { - convertRotationGate(op, "UOp", newOperands, ctrlQubits, 3, rewriter); - return success(); - } - - return failure(); - } -}; - -struct QIRToMQTRef final : impl::QIRToMQTRefBase { - using QIRToMQTRefBase::QIRToMQTRefBase; - - /** - * @brief Finds the main function in the module - * - * @param op The module operation that holds all operations. - * @return The main function. - */ - static LLVM::LLVMFuncOp getMainFunction(ModuleOp op) { - - // find the main function - for (auto funcOp : op.getOps()) { - auto passthrough = funcOp->getAttrOfType("passthrough"); - if (!passthrough) { - continue; - } - if (llvm::any_of(passthrough, [](Attribute attr) { - auto strAttr = dyn_cast(attr); - return strAttr && strAttr.getValue() == "entry_point"; - })) { - return funcOp; - } - } - return nullptr; - } - /** - * @brief Finds the number of required results from an array of attributes. - * - * @param passthrough The ArrayAttr that holds all attributes - * @return The number of result bits. - */ - static int64_t getRequiredNumResults(ArrayAttr passthrough) { - for (const auto attr : passthrough) { - const auto innerArray = dyn_cast(attr); - if (!innerArray || innerArray.size() < 2) { - continue; - } - - if (const auto key = dyn_cast(innerArray[0])) { - if (key.getValue() == "required_num_results") { - if (const auto value = dyn_cast(innerArray[1])) { - return stoi(value.str()); - } - } - } - } - return 0; - } - - /** - * @brief create the allocaOp to store classical results - * - * @param op The module operation that holds all operations. - * @param state The lowering state of the conversion pass. - */ - static void createAllocaOp(Operation* op, LoweringState* state) { - const auto mod = dyn_cast(op); - - // get the main function - LLVM::LLVMFuncOp main = getMainFunction(mod); - if (!main) { - return; - } - - const auto passthrough = main->getAttrOfType("passthrough"); - - // get the number of results - auto numResults = getRequiredNumResults(passthrough); - // return if no results are needed - if (numResults == 0) { - return; - } - - // create the allocaOp at the beginning of the first block - auto& entryBlock = main.getBody().front(); - OpBuilder builder(&main.getBody()); - builder.setInsertionPointToStart(&entryBlock); - - const auto memrefType = MemRefType::get({numResults}, builder.getI1Type()); - // set the allocaOp in the loweringState - state->allocaOp = - builder.create(op->getLoc(), memrefType); - } - - void runOnOperation() override { - MLIRContext* ctx = &getContext(); - auto* moduleOp = getOperation(); - LoweringState state; - - ConversionTarget target(*ctx); - RewritePatternSet patterns(ctx); - QIRToMQTRefTypeConverter typeConverter(ctx); - target.addLegalDialect(); - target.addLegalDialect(); - target.addLegalDialect(); - createAllocaOp(moduleOp, &state); - target.addIllegalOp(); - target.addIllegalOp(); - target.addIllegalOp(); - target.addIllegalOp(); - target.addIllegalOp(); - target.addIllegalOp(); - patterns.add(typeConverter, ctx, &state); - patterns.add(typeConverter, ctx); - patterns.add(typeConverter, ctx); - patterns.add(typeConverter, ctx); - patterns.add(typeConverter, ctx, &state); - patterns.add(typeConverter, ctx, &state); - - if (failed(applyPartialConversion(moduleOp, target, std::move(patterns)))) { - signalPassFailure(); - } - }; -}; - -} // namespace mqt::ir diff --git a/mlir/lib/Dialect/CMakeLists.txt b/mlir/lib/Dialect/CMakeLists.txt index 25ca2f9c9d..c0d91fa751 100644 --- a/mlir/lib/Dialect/CMakeLists.txt +++ b/mlir/lib/Dialect/CMakeLists.txt @@ -6,8 +6,6 @@ # # Licensed under the MIT License -add_subdirectory(MQTOpt) -add_subdirectory(MQTRef) add_subdirectory(QCO) add_subdirectory(QIR) add_subdirectory(QC) diff --git a/mlir/lib/Dialect/MQTOpt/CMakeLists.txt b/mlir/lib/Dialect/MQTOpt/CMakeLists.txt deleted file mode 100644 index 3b0a561d0f..0000000000 --- a/mlir/lib/Dialect/MQTOpt/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -add_subdirectory(IR) -add_subdirectory(Transforms) diff --git a/mlir/lib/Dialect/MQTOpt/IR/CMakeLists.txt b/mlir/lib/Dialect/MQTOpt/IR/CMakeLists.txt deleted file mode 100644 index 7a6e81b1f6..0000000000 --- a/mlir/lib/Dialect/MQTOpt/IR/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -add_mlir_dialect_library( - MLIRMQTOpt - MQTOptOps.cpp - DEPENDS - MLIRMQTOptOpsIncGen - MLIRMQTOptInterfacesIncGen - LINK_LIBS - MLIRIR - MLIRInferTypeOpInterface - DISABLE_INSTALL) - -# collect header files -file(GLOB_RECURSE IR_HEADERS_SOURCE "${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Dialect/MQTOpt/IR/*.h") -file(GLOB_RECURSE IR_HEADERS_BUILD "${MQT_MLIR_BUILD_INCLUDE_DIR}/mlir/Dialect/MQTOpt/IR/*.inc") - -# add public headers using file sets -target_sources( - MLIRMQTOpt - PUBLIC FILE_SET - HEADERS - BASE_DIRS - ${MQT_MLIR_SOURCE_INCLUDE_DIR} - FILES - ${IR_HEADERS_SOURCE} - FILE_SET - HEADERS - BASE_DIRS - ${MQT_MLIR_BUILD_INCLUDE_DIR} - FILES - ${IR_HEADERS_BUILD}) diff --git a/mlir/lib/Dialect/MQTOpt/IR/MQTOptOps.cpp b/mlir/lib/Dialect/MQTOpt/IR/MQTOptOps.cpp deleted file mode 100644 index 56c37bbd20..0000000000 --- a/mlir/lib/Dialect/MQTOpt/IR/MQTOptOps.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" // IWYU pragma: associated - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// The following headers are needed for some template instantiations. -// IWYU pragma: begin_keep -#include -#include -#include -// IWYU pragma: end_keep - -//===----------------------------------------------------------------------===// -// Dialect -//===----------------------------------------------------------------------===// - -#include "mlir/Dialect/MQTOpt/IR/MQTOptOpsDialect.cpp.inc" - -void mqt::ir::opt::MQTOptDialect::initialize() { - // NOLINTNEXTLINE(clang-analyzer-core.StackAddressEscape) - addTypes< -#define GET_TYPEDEF_LIST -#include "mlir/Dialect/MQTOpt/IR/MQTOptOpsTypes.cpp.inc" - >(); - - addOperations< -#define GET_OP_LIST -#include "mlir/Dialect/MQTOpt/IR/MQTOptOps.cpp.inc" - >(); -} - -//===----------------------------------------------------------------------===// -// Types -//===----------------------------------------------------------------------===// - -#define GET_TYPEDEF_CLASSES -#include "mlir/Dialect/MQTOpt/IR/MQTOptOpsTypes.cpp.inc" - -//===----------------------------------------------------------------------===// -// Interfaces -//===----------------------------------------------------------------------===// - -#include "mlir/Dialect/MQTOpt/IR/MQTOptInterfaces.cpp.inc" - -//===----------------------------------------------------------------------===// -// Operations -//===----------------------------------------------------------------------===// - -#define GET_OP_CLASSES -#include "mlir/Dialect/MQTOpt/IR/MQTOptOps.cpp.inc" - -namespace mqt::ir::opt { - -/** - * @brief Prints the given list of types as a comma-separated list - * - * @param printer The printer to use. - * @param types The types to print. - **/ -static void printCommaSeparated(mlir::OpAsmPrinter& printer, - mlir::TypeRange types) { - llvm::interleaveComma(llvm::make_range(types.begin(), types.end()), - printer.getStream(), - [&printer](mlir::Type t) { printer.printType(t); }); -} - -mlir::ParseResult -parseOptOutputTypes(mlir::OpAsmParser& parser, - llvm::SmallVectorImpl& outQubits, - llvm::SmallVectorImpl& posCtrlOutQubits, - llvm::SmallVectorImpl& negCtrlOutQubits) { - // No ":" delimiter -> no output types. - if (parser.parseOptionalColon().failed()) { - return mlir::success(); - } - - // Also allow `: ()` - if (parser.parseOptionalLParen().succeeded()) { - if (parser.parseRParen().failed()) { - return mlir::failure(); - } - return mlir::success(); - } - - // Parse the type of the target (there is no `parseOptionalTypeList` method so - // we need to do this manually). - mlir::Type target; - if (parser.parseOptionalType(target).has_value()) { - outQubits.push_back(target); - while (parser.parseOptionalComma().succeeded()) { - if (parser.parseType(target).failed()) { - return mlir::failure(); - } - outQubits.push_back(target); - } - } - - // Parse the control and negated control qubits if the corresponding keyword - // exists. - if (parser.parseOptionalKeyword("ctrl").succeeded()) { - if (parser.parseTypeList(posCtrlOutQubits).failed()) { - parser.emitError(parser.getCurrentLocation(), - "expected at least one type after `ctrl` keyword"); - return mlir::failure(); - } - } - - if (parser.parseOptionalKeyword("nctrl").succeeded()) { - if (parser.parseTypeList(negCtrlOutQubits).failed()) { - parser.emitError(parser.getCurrentLocation(), - "expected at least one type after `nctrl` keyword"); - return mlir::failure(); - } - } - - // If no types were parsed, this corresponds to e.g. like `mqtopt.i() %q :` - if (outQubits.empty() && posCtrlOutQubits.empty() && - negCtrlOutQubits.empty()) { - parser.emitError(parser.getCurrentLocation(), - "expected at least one type after `:`"); - return mlir::failure(); - } - - return mlir::success(); -} - -void printOptOutputTypes(mlir::OpAsmPrinter& printer, mlir::Operation* /*op*/, - mlir::TypeRange outQubits, - mlir::TypeRange posCtrlOutQubits, - mlir::TypeRange negCtrlOutQubits) { - if (outQubits.empty() && posCtrlOutQubits.empty() && - negCtrlOutQubits.empty()) { - return; - } - - printer << ": "; - - printCommaSeparated(printer, outQubits); - - if (!posCtrlOutQubits.empty()) { - if (!outQubits.empty()) { - printer << " "; - } - printer << "ctrl "; - printCommaSeparated(printer, posCtrlOutQubits); - } - - if (!negCtrlOutQubits.empty()) { - if (!posCtrlOutQubits.empty() || !outQubits.empty()) { - printer << " "; - } - printer << "nctrl "; - printCommaSeparated(printer, negCtrlOutQubits); - } -} - -mlir::ParseResult parseOptParams( - mlir::OpAsmParser& parser, - llvm::SmallVectorImpl& params, - mlir::Attribute& staticParams, mlir::Attribute& paramsMask) { - if (mlir::OpAsmParser::UnresolvedOperand operand; - parser.parseOptionalOperand(operand).has_value()) { - params.push_back(operand); - while (parser.parseOptionalComma().succeeded()) { - if (parser.parseOperand(operand).failed()) { - return mlir::failure(); - } - params.push_back(operand); - } - } - - if (parser.parseOptionalKeyword("static").succeeded()) { - staticParams = mlir::DenseF64ArrayAttr::parse(parser, mlir::Type{}); - } - - if (parser.parseOptionalKeyword("mask").succeeded()) { - paramsMask = mlir::DenseBoolArrayAttr::parse(parser, mlir::Type{}); - } - - return mlir::success(); -} - -void printOptParams(mlir::OpAsmPrinter& printer, mlir::Operation* /*op*/, - mlir::ValueRange params, - mlir::DenseF64ArrayAttr staticParams, - mlir::DenseBoolArrayAttr paramsMask) { - auto needSpace = false; - if (!params.empty()) { - printer << params; - needSpace = true; - } - - if (staticParams) { - if (needSpace) { - printer << " "; - } - std::string staticStr; - llvm::raw_string_ostream ostream(staticStr); - staticParams.print(ostream); - printer << "static " << ostream.str(); - needSpace = true; - } - - if (paramsMask) { - if (needSpace) { - printer << " "; - } - std::string maskStr; - llvm::raw_string_ostream ostream(maskStr); - paramsMask.print(ostream); - printer << "mask " << ostream.str(); - } -} -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt b/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt deleted file mode 100644 index d503ff5d22..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) -set(LIBRARIES ${dialect_libs} MQT::CoreIR) - -file(GLOB_RECURSE TRANSFORMS_SOURCES *.cpp) - -add_mlir_library( - MLIRMQTOptTransforms - ${TRANSFORMS_SOURCES} - LINK_LIBS - ${LIBRARIES} - DEPENDS - MLIRMQTOptTransformsIncGen - DISABLE_INSTALL) - -# collect header files -file(GLOB_RECURSE TRANSFORMS_HEADERS_SOURCE - ${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Dialect/MQTOpt/Transforms/*.h) -file(GLOB_RECURSE TRANSFORMS_HEADERS_BUILD - ${MQT_MLIR_BUILD_INCLUDE_DIR}/mlir/Dialect/MQTOpt/Transforms/*.inc) - -# add public headers using file sets -target_sources( - MLIRMQTOptTransforms - PUBLIC FILE_SET - HEADERS - BASE_DIRS - ${MQT_MLIR_SOURCE_INCLUDE_DIR} - FILES - ${TRANSFORMS_HEADERS_SOURCE} - FILE_SET - HEADERS - BASE_DIRS - ${MQT_MLIR_BUILD_INCLUDE_DIR} - FILES - ${TRANSFORMS_HEADERS_BUILD}) diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp deleted file mode 100644 index 379883caeb..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/DeadGateEliminationPattern.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -/** - * @brief This pattern eliminates Unitary operations that happen directly before - * de-allocs. - */ -struct DeadGateEliminationPattern final - : mlir::OpInterfaceRewritePattern { - - explicit DeadGateEliminationPattern(mlir::MLIRContext* context) - : OpInterfaceRewritePattern(context) {} - - mlir::LogicalResult - matchAndRewrite(UnitaryInterface op, - mlir::PatternRewriter& rewriter) const override { - for (auto* user : op->getUsers()) { - if (!mlir::isa(user)) { - return mlir::failure(); - } - } - - for (auto outQubit : op.getAllOutQubits()) { - rewriter.replaceAllUsesWith(outQubit, op.getCorrespondingInput(outQubit)); - } - rewriter.eraseOp(op); - - return mlir::success(); - } -}; - -/** - * @brief Populates the given pattern set with the - * `DeadGateEliminationPattern`. - * - * @param patterns The pattern set to populate. - */ -void populateDeadGateEliminationPatterns(mlir::RewritePatternSet& patterns) { - patterns.add(patterns.getContext()); -} - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/FromQuantumComputationPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/FromQuantumComputationPattern.cpp deleted file mode 100644 index 57769d3498..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/FromQuantumComputationPattern.cpp +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "ir/QuantumComputation.hpp" -#include "ir/operations/OpType.hpp" -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { -/// Analysis pattern that creates MLIR instructions from a given -/// qc::QuantumComputation. These instructions replace an existing `AllocOp` -/// that has the `to_replace` attribute set. -struct FromQuantumComputationPattern final - : mlir::OpRewritePattern { - qc::QuantumComputation& circuit; // NOLINT(*-avoid-const-or-ref-data-members) - - explicit FromQuantumComputationPattern(mlir::MLIRContext* context, - qc::QuantumComputation& qc) - : OpRewritePattern(context), circuit(qc) {} - - /** - * @brief Creates a LoadOp for loading a qubit. - * - * @param reg The register to load the qubit from. - * @param index The index of the qubit to load. - * @param rewriter The pattern rewriter to use. - * - * @return The created LoadOp. - */ - static mlir::memref::LoadOp createLoadOp(mlir::Value reg, const size_t index, - mlir::PatternRewriter& rewriter) { - auto indexValue = rewriter.create( - reg.getLoc(), static_cast(index)); - return rewriter.create(reg.getLoc(), reg, - mlir::ValueRange{indexValue}); - } - - /** - * @brief Creates a StoreOp for storing a qubit. - * - * @param qubit The qubit to store into the register. - * @param reg The register into which the qubit will be stored. - * @param index The index at which to store the qubit. - * @param rewriter The pattern rewriter to use. - * - * @return The created StoreOp. - */ - static mlir::memref::StoreOp createStoreOp(mlir::Value qubit, mlir::Value reg, - const size_t index, - mlir::PatternRewriter& rewriter) { - auto indexValue = rewriter.create( - reg.getLoc(), static_cast(index)); - return rewriter.create(reg.getLoc(), qubit, reg, - mlir::ValueRange{indexValue}); - } - -#define CREATE_OP_CASE(opType) \ - case qc::OpType::opType: \ - return rewriter.create( \ - loc, outQubitTypes, controlQubitsPositive.getType(), \ - controlQubitsNegative.getType(), denseParamsAttr, nullptr, \ - mlir::ValueRange{}, inQubits, controlQubitsPositive, \ - controlQubitsNegative); - - /** - * @brief Creates a unitary operation on a given qubit with any number of - * positive or negative controls. - * - * @param loc The location of the operation. - * @param type The type of the unitary operation. - * @param inQubits The qubits to apply the unitary operation to. - * @param controlQubitsPositive The positive control qubits. - * @param controlQubitsNegative The negative control qubits. - * @param rewriter The pattern rewriter to use. - * - * @return The created UnitaryOp. - */ - static UnitaryInterface - createUnitaryOp(const mlir::Location loc, const qc::OpType type, - const llvm::SmallVector& inQubits, - mlir::ValueRange controlQubitsPositive, - mlir::ValueRange controlQubitsNegative, - mlir::PatternRewriter& rewriter, - const std::vector& parameters = {}) { - // Create result types for all output qubits - auto qubitType = QubitType::get(rewriter.getContext()); - const llvm::SmallVector outQubitTypes(inQubits.size(), - qubitType); - - // For all parametric gates, turn parameters vector into DenseF64ArrayAttr - auto denseParamsAttr = - parameters.empty() - ? nullptr - : mlir::DenseF64ArrayAttr::get(rewriter.getContext(), parameters); - - switch (type) { - CREATE_OP_CASE(I) - CREATE_OP_CASE(H) - CREATE_OP_CASE(X) - CREATE_OP_CASE(Y) - CREATE_OP_CASE(Z) - CREATE_OP_CASE(S) - CREATE_OP_CASE(Sdg) - CREATE_OP_CASE(T) - CREATE_OP_CASE(Tdg) - CREATE_OP_CASE(V) - CREATE_OP_CASE(Vdg) - CREATE_OP_CASE(U) - CREATE_OP_CASE(U2) - CREATE_OP_CASE(P) - CREATE_OP_CASE(SX) - CREATE_OP_CASE(SXdg) - CREATE_OP_CASE(RX) - CREATE_OP_CASE(RY) - CREATE_OP_CASE(RZ) - CREATE_OP_CASE(SWAP) - CREATE_OP_CASE(iSWAP) - CREATE_OP_CASE(iSWAPdg) - CREATE_OP_CASE(Peres) - CREATE_OP_CASE(Peresdg) - CREATE_OP_CASE(DCX) - CREATE_OP_CASE(ECR) - CREATE_OP_CASE(RXX) - CREATE_OP_CASE(RYY) - CREATE_OP_CASE(RZZ) - CREATE_OP_CASE(RZX) - CREATE_OP_CASE(XXminusYY) - CREATE_OP_CASE(XXplusYY) - default: - throw std::runtime_error("Unsupported operation type"); - } - } - - /** - * @brief Creates a MeasureOp on a given qubit. - * - * @param loc The location of the operation. - * @param targetQubit The qubit to measure. - * @param rewriter The pattern rewriter to use. - * - * @return The created MeasureOp. - */ - static MeasureOp createMeasureOp(const mlir::Location loc, - mlir::Value targetQubit, - mlir::PatternRewriter& rewriter) { - return rewriter.create( - loc, - mlir::TypeRange{QubitType::get(rewriter.getContext()), - rewriter.getI1Type()}, - targetQubit); - } - - /** - * @brief Updates the inputs of the function's `return` operation. - * - * The previous operation used constant `false` values for the returns of type - * `i1`. After this update, the measurement results are used instead. - * - * @param returnOperation The `return` operation to update. - * @param reg The register to use as the new return value. - * @param measurementValues The values to use as the new return values. - * @param rewriter The pattern rewriter to use. - */ - static void - updateReturnOperation(mlir::Operation* returnOperation, mlir::Value reg, - const std::vector& measurementValues, - mlir::PatternRewriter& rewriter) { - auto* const cloned = rewriter.clone(*returnOperation); - cloned->setOperand(0, reg); - for (size_t i = 0; i < measurementValues.size(); i++) { - cloned->setOperand(i + 1, measurementValues[i]); - } - rewriter.replaceOp(returnOperation, cloned); - } - - mlir::LogicalResult - matchAndRewrite(mlir::memref::AllocOp op, - mlir::PatternRewriter& rewriter) const override { - if (op->hasAttr("to_replace")) { - const std::size_t numQubits = circuit.getNqubits(); - - // Prepare list of measurement results for later use. - std::vector measurementValues(numQubits); - - // Create a new qubit register with the correct number of qubits. - const auto& qubitType = QubitType::get(rewriter.getContext()); - const auto& memRefType = mlir::MemRefType::get( - {static_cast(numQubits)}, qubitType); - auto newAlloc = rewriter.create( - op.getLoc(), memRefType, mlir::ValueRange{}); - newAlloc->setAttr("mqt_core", rewriter.getUnitAttr()); - - // We start by first extracting each qubit from the register. The current - // `Value` representations of each qubit are stored in the - // `currentQubitVariables` vector. - auto reg = newAlloc.getResult(); - std::vector currentQubitVariables(numQubits); - for (size_t i = 0; i < numQubits; i++) { - auto loadOp = createLoadOp(reg, i, rewriter); - currentQubitVariables[i] = loadOp.getResult(); - } - - // Iterate over each operation in the circuit and create the corresponding - // MLIR operations. - for (const auto& o : circuit) { - // Collect the positive and negative control qubits for the operation in - // separate vectors. - std::vector controlQubitIndicesPositive; - std::vector controlQubitIndicesNegative; - std::vector controlQubitsPositive; - std::vector controlQubitsNegative; - for (const auto& control : o->getControls()) { - if (control.type == qc::Control::Type::Pos) { - controlQubitIndicesPositive.emplace_back(control.qubit); - controlQubitsPositive.emplace_back( - currentQubitVariables[control.qubit]); - } else { - controlQubitIndicesNegative.emplace_back(control.qubit); - controlQubitsNegative.emplace_back( - currentQubitVariables[control.qubit]); - } - } - - if (o->isUnitary() && !o->isCompoundOperation()) { - // For unitary operations, we call the `createUnitaryOp` function. We - // then have to update the `currentQubitVariables` vector with the new - // qubit values. - llvm::SmallVector inQubits(o->getTargets().size()); - - for (size_t i = 0; i < o->getTargets().size(); i++) { - inQubits[i] = currentQubitVariables[o->getTargets()[i]]; - } - - UnitaryInterface newUnitaryOp = createUnitaryOp( - op->getLoc(), o->getType(), inQubits, controlQubitsPositive, - controlQubitsNegative, rewriter, o->getParameter()); - - const size_t numTargets = o->getTargets().size(); - auto outs = newUnitaryOp.getAllOutQubits(); - - // targets - for (size_t i = 0; i < numTargets; ++i) { - currentQubitVariables[o->getTargets()[i]] = outs[i]; - } - - // controls - size_t base = numTargets; - for (size_t i = 0; i < controlQubitsPositive.size(); ++i) { - currentQubitVariables[controlQubitIndicesPositive[i]] = - outs[base + i]; - } - - base += controlQubitsPositive.size(); - for (size_t i = 0; i < controlQubitsNegative.size(); ++i) { - currentQubitVariables[controlQubitIndicesNegative[i]] = - outs[base + i]; - } - } else if (o->getType() == qc::OpType::Measure) { - // For measurement operations, we call the `createMeasureOp` function. - // We then update the `currentQubitVariables` and `measurementValues` - // vectors. - MeasureOp newMeasureOp = createMeasureOp( - op->getLoc(), currentQubitVariables[o->getTargets()[0]], - rewriter); - currentQubitVariables[o->getTargets()[0]] = - newMeasureOp.getOutQubit(); - measurementValues[o->getTargets()[0]] = newMeasureOp.getOutBit(); - } else { - llvm::outs() << "ERROR: Unsupported operation type " << o->getType() - << "\n"; - } - } - - // Now insert all the qubits back into the registers they were extracted - // from. - for (size_t i = 0; i < numQubits; i++) { - createStoreOp(currentQubitVariables[i], reg, i, rewriter); - } - - // Finally, the return operation needs to be updated with the measurement - // results and then replace the original `alloc` operation with the - // updated one. - const auto returnIt = - llvm::find_if(op->getUsers(), [](mlir::Operation* user) { - return llvm::isa(user); - }); - - if (returnIt != op->getUsers().end()) { - updateReturnOperation(*returnIt, reg, measurementValues, rewriter); - } - - rewriter.replaceOp(op, newAlloc); - return mlir::success(); - } - return mlir::failure(); - } -}; - -/** - * @brief Populates the given pattern set with the - * `FromQuantumComputationPattern`. - * - * @param patterns The pattern set to populate. - * @param circuit The quantum computation to create MLIR instructions from. - */ -void populateFromQuantumComputationPatterns(mlir::RewritePatternSet& patterns, - qc::QuantumComputation& circuit) { - patterns.add(patterns.getContext(), circuit); -} - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateElimination.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateElimination.cpp deleted file mode 100644 index 134edc36dc..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateElimination.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include - -namespace mqt::ir::opt { - -#define GEN_PASS_DEF_GATEELIMINATION -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" - -/** - * @brief This pass attempts to cancel consecutive self-inverse operations. - */ -struct GateElimination final : impl::GateEliminationBase { - - void runOnOperation() override { - // Get the current operation being operated on. - auto op = getOperation(); - auto* ctx = &getContext(); - - // Define the set of patterns to use. - mlir::RewritePatternSet patterns(ctx); - populateGateEliminationPatterns(patterns); - - // Apply patterns in an iterative and greedy manner. - if (mlir::failed(mlir::applyPatternsGreedily(op, std::move(patterns)))) { - signalPassFailure(); - } - } -}; - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/GateEliminationPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/GateEliminationPattern.cpp deleted file mode 100644 index eb49207deb..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/GateEliminationPattern.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -/** - * @brief This pattern attempts to cancel consecutive self-inverse operations. - */ -struct CancelConsecutiveInversesPattern final - : mlir::OpInterfaceRewritePattern { - - explicit CancelConsecutiveInversesPattern(mlir::MLIRContext* context) - : OpInterfaceRewritePattern(context) {} - - /** - * @brief Checks if two gates are inverse to each other. - * - * @param a The first gate. - * @param b The second gate. - * @return True if the gates are inverse to each other, false otherwise. - */ - [[nodiscard]] static bool areGatesInverse(mlir::Operation& a, - mlir::Operation& b) { - static const std::map INVERSE_PAIRS = { - {"x", "x"}, {"y", "y"}, {"z", "z"}, - {"h", "h"}, {"i", "i"}, {"swap", "swap"}, - {"ecr", "ecr"}, {"t", "tdg"}, {"s", "sdg"}, - {"sx", "sxdg"}, {"v", "vdg"}, {"iswap", "iswapdg"}, - {"peres", "peresdg"}, - }; - - const auto aName = a.getName().stripDialect().str(); - const auto bName = b.getName().stripDialect().str(); - const auto foundA = INVERSE_PAIRS.find(aName); - const auto foundB = INVERSE_PAIRS.find(bName); - - return (foundA != INVERSE_PAIRS.end() && foundA->second == bName) || - (foundB != INVERSE_PAIRS.end() && foundB->second == aName); - } - - /** - * @brief Checks if all users of an operation are the same. - * - * @param users The users to check. - * @return True if all users are the same, false otherwise. - */ - [[nodiscard]] static bool - areUsersUnique(const mlir::ResultRange::user_range& users) { - return llvm::none_of(users, - [&](auto* user) { return user != *users.begin(); }); - } - - mlir::LogicalResult - matchAndRewrite(UnitaryInterface op, - mlir::PatternRewriter& rewriter) const override { - const auto& users = op->getUsers(); - if (!areUsersUnique(users)) { - return mlir::failure(); - } - - auto* userOp = *users.begin(); - if (!areGatesInverse(*op, *userOp)) { - return mlir::failure(); - } - auto user = mlir::dyn_cast(userOp); - if (op.getAllOutQubits() != user.getAllInQubits()) { - return mlir::failure(); - } - if (op.getPosCtrlInQubits().size() != user.getPosCtrlInQubits().size() || - op.getNegCtrlInQubits().size() != user.getNegCtrlInQubits().size()) { - // We only need to check the sizes, because the order of the controls was - // already checked by the previous condition. - return mlir::failure(); - } - - // When iterating over the output qubits, it is important to call - // `getAllOutQubits()` only once, as the output qubits are combined into a - // fresh vector on every call. - const auto& userOutQubits = user.getAllOutQubits(); - // Also get the op's input qubits. - const auto& opInQubits = op.getAllInQubits(); - - // Note: There might be multiple users of an operation. The qubits itself - // can only be used once (linear typing). However, the user may output - // multiple qubits, e.g., a CX gate, that are used by different users. - // Hence, the user may have multiple child users. - const auto& childUsers = user->getUsers(); - - for (const auto& childUser : childUsers) { - for (size_t i = 0; i < childUser->getOperands().size(); i++) { - const auto& operand = childUser->getOperand(i); - const auto found = llvm::find(userOutQubits, operand); - if (found == userOutQubits.end()) { - continue; - } - const auto idx = std::distance(userOutQubits.begin(), found); - rewriter.modifyOpInPlace( - childUser, [&] { childUser->setOperand(i, opInQubits[idx]); }); - } - } - - rewriter.eraseOp(user); - rewriter.eraseOp(op); - return mlir::success(); - } -}; - -/** - * @brief This pattern removes all identity (noop) operations. - */ -struct RemoveIdentitiesPattern final : mlir::OpRewritePattern { - - explicit RemoveIdentitiesPattern(mlir::MLIRContext* context) - : OpRewritePattern(context) {} - - mlir::LogicalResult - matchAndRewrite(IOp op, mlir::PatternRewriter& rewriter) const override { - auto inQubits = op.getAllInQubits(); - - rewriter.replaceAllOpUsesWith(op, inQubits); - - rewriter.eraseOp(op); - return mlir::success(); - } -}; - -/** - * @brief Populates the given pattern set with patterns for gate elimination. - * - * @param patterns The pattern set to populate. - */ -void populateGateEliminationPatterns(mlir::RewritePatternSet& patterns) { - patterns.add(patterns.getContext()); - patterns.add(patterns.getContext()); -} - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/LiftMeasurementsAboveControlsPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/LiftMeasurementsAboveControlsPattern.cpp deleted file mode 100644 index 78cc8a63ac..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/LiftMeasurementsAboveControlsPattern.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/LiftMeasurementsPasses.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -/** - * @brief This pattern is responsible for applying the "deferred measurement - * principle", lifting measurements above controls. - */ -struct LiftMeasurementsAboveControlsPattern final - : mlir::OpRewritePattern { - - explicit LiftMeasurementsAboveControlsPattern(mlir::MLIRContext* context) - : OpRewritePattern(context) {} - - mlir::LogicalResult - matchAndRewrite(MeasureOp op, - mlir::PatternRewriter& rewriter) const override { - const auto qubitVariable = op.getInQubit(); - auto* predecessor = qubitVariable.getDefiningOp(); - auto predecessorUnitary = mlir::dyn_cast(predecessor); - - if (!predecessorUnitary) { - return mlir::failure(); - } - - if (llvm::find(predecessorUnitary.getOutQubits(), qubitVariable) != - predecessorUnitary.getOutQubits().end()) { - // The measured qubit is a target, not a control of the gate. - return mlir::failure(); - } - - swapGateWithMeasurement(predecessorUnitary, op, rewriter); - - return mlir::success(); - } -}; - -/** - * @brief Populates the given pattern set with the - * `LiftMeasurementsAboveControlsPattern`. - * - * @param patterns The pattern set to populate. - */ -void populateLiftMeasurementsAboveControlsPatterns( - mlir::RewritePatternSet& patterns) { - patterns.add(patterns.getContext()); -} - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/LiftMeasurementsAboveGatesPatterns.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/LiftMeasurementsAboveGatesPatterns.cpp deleted file mode 100644 index bbbcd19a25..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/LiftMeasurementsAboveGatesPatterns.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/LiftMeasurementsPasses.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -static const std::unordered_set INVERTING_GATES = {"x", "y"}; -static const std::unordered_set DIAGONAL_GATES = { - "i", "z", "s", "sdg", "t", "tdg", "p", "rz"}; - -void swapGateWithMeasurement(UnitaryInterface gate, MeasureOp measurement, - mlir::PatternRewriter& rewriter) { - auto measurementInput = measurement.getInQubit(); - auto gateInput = gate.getCorrespondingInput(measurementInput); - rewriter.replaceUsesWithIf(measurementInput, gateInput, - [&](mlir::OpOperand& operand) { - // We only replace the single use by the - // measure op - return operand.getOwner() == measurement; - }); - rewriter.replaceUsesWithIf(gateInput, measurement.getOutQubit(), - [&](mlir::OpOperand& operand) { - // We only replace the single use by the - // predecessor - return operand.getOwner() == gate; - }); - rewriter.replaceUsesWithIf(measurement.getOutQubit(), measurementInput, - [&](mlir::OpOperand& operand) { - // All further uses of the measurement output now - // use the gate output - return operand.getOwner() != gate; - }); - rewriter.moveOpBefore(measurement, gate); -} - -/** - * @brief This pattern is responsible for lifting measurements above any phase - * gates. - */ -struct LiftMeasurementsAbovePhaseGatesPattern final - : mlir::OpRewritePattern { - - explicit LiftMeasurementsAbovePhaseGatesPattern(mlir::MLIRContext* context) - : OpRewritePattern(context) {} - - mlir::LogicalResult - matchAndRewrite(MeasureOp op, - mlir::PatternRewriter& rewriter) const override { - const auto qubitVariable = op.getInQubit(); - auto* predecessor = qubitVariable.getDefiningOp(); - - auto predecessorUnitary = mlir::dyn_cast(predecessor); - - if (!predecessorUnitary) { - return mlir::failure(); - } - - const auto name = predecessorUnitary->getName().stripDialect().str(); - if (DIAGONAL_GATES.count(name) == 1) { - swapGateWithMeasurement(predecessorUnitary, op, rewriter); - return mlir::success(); - } - - return mlir::failure(); - } -}; - -/** - * @brief This pattern is responsible for lifting measurements above any - * non-phase gates. - */ -struct LiftMeasurementsAboveInvertingGatesPattern final - : mlir::OpRewritePattern { - - explicit LiftMeasurementsAboveInvertingGatesPattern( - mlir::MLIRContext* context) - : OpRewritePattern(context) {} - - /** - * @brief Checks if the given qubit is not used anymore. - * @param outQubit The output qubit to check. - * @return True if all users are resets/deallocs, false otherwise. - */ - static bool outputQubitRemainsUnused(mlir::Value outQubit) { - return llvm::all_of(outQubit.getUsers(), [](mlir::Operation* user) { - return mlir::isa(user) || mlir::isa(user); - }); - } - - mlir::LogicalResult - matchAndRewrite(MeasureOp op, - mlir::PatternRewriter& rewriter) const override { - if (!outputQubitRemainsUnused(op.getOutQubit())) { - return mlir::failure(); // if the qubit is still used after the - // measurement, we cannot lift it above the gate. - } - const auto qubitVariable = op.getInQubit(); - auto* predecessor = qubitVariable.getDefiningOp(); - const auto name = predecessor->getName().stripDialect().str(); - - auto predecessorUnitary = mlir::dyn_cast(predecessor); - - if (!predecessorUnitary) { - return mlir::failure(); - } - - if (INVERTING_GATES.count(name) == 1 && - predecessorUnitary.getAllInQubits().size() == 1) { - swapGateWithMeasurement(predecessorUnitary, op, rewriter); - rewriter.setInsertionPointAfter(op); - const mlir::Value trueConstant = rewriter.create( - op.getLoc(), rewriter.getBoolAttr(true)); - auto inversion = rewriter.create( - op.getLoc(), op.getOutBit(), trueConstant); - // We need `replaceUsesWithIf` so that we can replace all uses except for - // the one use that defines the inverted bit. - rewriter.replaceUsesWithIf(op.getOutBit(), inversion.getResult(), - [&](mlir::OpOperand& operand) { - return operand.getOwner() != inversion; - }); - return mlir::success(); - } - - return mlir::failure(); - } -}; - -/** - * @brief Populates the given pattern set with the - * `LiftMeasurementsAbovePhaseGatesPattern` and - * `LiftMeasurementsAboveInvertingGatesPattern`. - * - * @param patterns The pattern set to populate. - */ -void populateLiftMeasurementsAboveGatesPatterns( - mlir::RewritePatternSet& patterns) { - patterns.add(patterns.getContext()); - patterns.add( - patterns.getContext()); -} - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/LiftMeasurementsPass.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/LiftMeasurementsPass.cpp deleted file mode 100644 index 8d2c18f51f..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/LiftMeasurementsPass.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include - -namespace mqt::ir::opt { - -#define GEN_PASS_DEF_LIFTMEASUREMENTSPASS -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" - -/** - * @brief This pass attempts to lift measurements above certain operations. - */ -struct LiftMeasurementsPass final - : impl::LiftMeasurementsPassBase { - - void runOnOperation() override { - // Get the current operation being operated on. - auto op = getOperation(); - auto* ctx = &getContext(); - - // Define the set of patterns to use. - mlir::RewritePatternSet patterns(ctx); - populateReplaceBasisStateControlsWithIfPatterns(patterns); - populateLiftMeasurementsAboveControlsPatterns(patterns); - populateLiftMeasurementsAboveGatesPatterns(patterns); - populateDeadGateEliminationPatterns(patterns); - - // Apply patterns in an iterative and greedy manner. - if (mlir::failed(mlir::applyPatternsGreedily(op, std::move(patterns)))) { - signalPassFailure(); - } - } -}; - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp deleted file mode 100644 index 772e59904e..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/MQTCoreRoundTrip.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "ir/QuantumComputation.hpp" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include - -namespace mqt::ir::opt { - -#define GEN_PASS_DEF_MQTCOREROUNDTRIP -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" - -struct MQTCoreRoundTrip final : impl::MQTCoreRoundTripBase { - - qc::QuantumComputation circuit; - - void runOnOperation() override { - // Get the current operation being operated on. - auto op = getOperation(); - auto* ctx = &getContext(); - - // Define the set of patterns to use. - mlir::RewritePatternSet patterns(ctx); - populateToQuantumComputationPatterns(patterns, circuit); - populateFromQuantumComputationPatterns(patterns, circuit); - - // Apply patterns in an iterative and greedy manner. - if (mlir::failed(mlir::applyPatternsGreedily(op, std::move(patterns)))) { - signalPassFailure(); - } - } -}; - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/MergeRotationGates.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/MergeRotationGates.cpp deleted file mode 100644 index 9fe2281703..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/MergeRotationGates.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include - -namespace mqt::ir::opt { - -#define GEN_PASS_DEF_MERGEROTATIONGATES -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" - -/** - * @brief This pattern attempts to merge consecutive rotation gates. - */ -struct MergeRotationGates final - : impl::MergeRotationGatesBase { - - void runOnOperation() override { - // Get the current operation being operated on. - auto op = getOperation(); - auto* ctx = &getContext(); - - // Define the set of patterns to use. - mlir::RewritePatternSet patterns(ctx); - populateMergeRotationGatesPatterns(patterns); - - // Apply patterns in an iterative and greedy manner. - if (mlir::failed(mlir::applyPatternsGreedily(op, std::move(patterns)))) { - signalPassFailure(); - } - } -}; - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/MergeRotationGatesPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/MergeRotationGatesPattern.cpp deleted file mode 100644 index ed755ec8d4..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/MergeRotationGatesPattern.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" -#include "mlir/IR/BuiltinAttributes.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -static const std::unordered_set MERGEABLE_GATES = { - "gphase", "p", "rx", "ry", "rz", "rxx", "ryy", "rzz", "rzx"}; - -/** - * @brief This pattern attempts to merge consecutive rotation gates. - */ -struct MergeRotationGatesPattern final - : mlir::OpInterfaceRewritePattern { - - explicit MergeRotationGatesPattern(mlir::MLIRContext* context) - : OpInterfaceRewritePattern(context) {} - - /** - * @brief Checks if two gates can be merged. - * - * @param a The first gate. - * @param b The second gate. - * @return True if the gates can be merged, false otherwise. - */ - [[nodiscard]] static bool areGatesMergeable(mlir::Operation& a, - mlir::Operation& b) { - const auto aName = a.getName().stripDialect().str(); - const auto bName = b.getName().stripDialect().str(); - - return ((aName == bName) && (MERGEABLE_GATES.count(aName) == 1)); - } - - /** - * @brief Checks if all users of an operation are the same. - * - * @param users The users to check. - * @return True if all users are the same, false otherwise. - */ - [[nodiscard]] static bool - areUsersUnique(const mlir::ResultRange::user_range& users) { - return llvm::none_of(users, - [&](auto* user) { return user != *users.begin(); }); - } - - mlir::LogicalResult - matchAndRewrite(UnitaryInterface op, - mlir::PatternRewriter& rewriter) const override { - const auto& users = op->getUsers(); - if (users.empty()) { - return mlir::failure(); - } - if (!areUsersUnique(users)) { - return mlir::failure(); - } - auto* user = *users.begin(); - if (!areGatesMergeable(*op, *user)) { - return mlir::failure(); - } - auto unitaryUser = mlir::dyn_cast(user); - if (op.getAllOutQubits() != unitaryUser.getAllInQubits()) { - return mlir::failure(); - } - if (op.getPosCtrlInQubits().size() != - unitaryUser.getPosCtrlInQubits().size() || - op.getNegCtrlInQubits().size() != - unitaryUser.getNegCtrlInQubits().size()) { - // We only need to check the sizes, because the order of the controls was - // already checked by the previous condition. - return mlir::failure(); - } - rewriteAdditiveAngle(op, rewriter); - return mlir::success(); - } - - /** - * @brief Creates a new rotation gate. - * - * The new rotation gate is created by adding the angles of two compatible - * rotation gates. - * - * @tparam OpType The type of the operation to create. - * @param op The first instance of the rotation gate. - * @param user The second instance of the rotation gate. - * @param rewriter The pattern rewriter. - * @return A new rotation gate. - */ - template - static UnitaryInterface - createOpAdditiveAngle(UnitaryInterface op, UnitaryInterface user, - mlir::PatternRewriter& rewriter) { - auto loc = user->getLoc(); - - auto userInQubits = user.getInQubits(); - auto userPosCtrlInQubits = user.getPosCtrlInQubits(); - auto userNegCtrlInQubits = user.getNegCtrlInQubits(); - - auto opParam = op.getParams()[0]; - auto userParam = user.getParams()[0]; - auto add = rewriter.create(loc, opParam, userParam); - const llvm::SmallVector newParamsVec{add.getResult()}; - const mlir::ValueRange newParams(newParamsVec); - - return rewriter.create( - loc, userInQubits.getType(), userPosCtrlInQubits.getType(), - userNegCtrlInQubits.getType(), mlir::DenseF64ArrayAttr{}, - mlir::DenseBoolArrayAttr{}, newParams, userInQubits, - userPosCtrlInQubits, userNegCtrlInQubits); - } - - /** - * @brief Merges two consecutive rotation gates into a single gate. - * - * The function supports gphase, p, rx, ry, rz, rxx, ryy, rzz, and rzx. - * The gates are merged by adding their angles. - * The merged gate is not removed if the angles add up to zero. - * - * @param op The first instance of the rotation gate. - * @param rewriter The pattern rewriter. - */ - void static rewriteAdditiveAngle(UnitaryInterface op, - mlir::PatternRewriter& rewriter) { - auto const type = op->getName().stripDialect().str(); - - auto user = mlir::dyn_cast(*op->getUsers().begin()); - - UnitaryInterface newUser; - if (type == "gphase") { - newUser = createOpAdditiveAngle(op, user, rewriter); - } else if (type == "p") { - newUser = createOpAdditiveAngle(op, user, rewriter); - } else if (type == "rx") { - newUser = createOpAdditiveAngle(op, user, rewriter); - } else if (type == "ry") { - newUser = createOpAdditiveAngle(op, user, rewriter); - } else if (type == "rz") { - newUser = createOpAdditiveAngle(op, user, rewriter); - } else if (type == "rxx") { - newUser = createOpAdditiveAngle(op, user, rewriter); - } else if (type == "ryy") { - newUser = createOpAdditiveAngle(op, user, rewriter); - } else if (type == "rzz") { - newUser = createOpAdditiveAngle(op, user, rewriter); - } else if (type == "rzx") { - newUser = createOpAdditiveAngle(op, user, rewriter); - } else { - llvm_unreachable("Unsupported operation type"); - } - - // Prepare erasure of op - const auto& opAllInQubits = op.getAllInQubits(); - const auto& newUserAllInQubits = newUser.getAllInQubits(); - for (size_t i = 0; i < newUser->getOperands().size(); i++) { - const auto& operand = newUser->getOperand(i); - const auto found = llvm::find(newUserAllInQubits, operand); - if (found == newUserAllInQubits.end()) { - continue; - } - const auto idx = std::distance(newUserAllInQubits.begin(), found); - rewriter.modifyOpInPlace( - newUser, [&] { newUser->setOperand(i, opAllInQubits[idx]); }); - } - - // Replace user with newUser - rewriter.replaceOp(user, newUser); - - // Erase op - rewriter.eraseOp(op); - } -}; - -/** - * @brief Populates the given pattern set with the `MergeRotationGatesPattern`. - * - * @param patterns The pattern set to populate. - */ -void populateMergeRotationGatesPatterns(mlir::RewritePatternSet& patterns) { - patterns.add(patterns.getContext()); -} - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/QuantumSinkPass.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/QuantumSinkPass.cpp deleted file mode 100644 index c6a0176444..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/QuantumSinkPass.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include - -namespace mqt::ir::opt { - -#define GEN_PASS_DEF_QUANTUMSINKPASS -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" - -/** - * @brief This pass attempts to sink quantum operations into the block where - * their results are used. - */ -struct QuantumSinkPass final : impl::QuantumSinkPassBase { - - void runOnOperation() override { - // Get the current operation being operated on. - auto op = getOperation(); - auto* ctx = &getContext(); - - // Define the set of patterns to use. - mlir::RewritePatternSet patterns(ctx); - populateQuantumSinkShiftPatterns(patterns); - populateQuantumSinkPushPatterns(patterns); - - // Apply patterns in an iterative and greedy manner. - if (mlir::failed(mlir::applyPatternsGreedily(op, std::move(patterns)))) { - signalPassFailure(); - } - } -}; - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/QuantumSinkPushPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/QuantumSinkPushPattern.cpp deleted file mode 100644 index a2e0d60a7b..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/QuantumSinkPushPattern.cpp +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -/** - * @brief This pattern is responsible to push Unitary operations through branch - * operations into new blocks. - * - * This can be done in some circumstances, if a branch operation uses the result - * of a unitary operation as a block argument. - */ -struct QuantumSinkPushPattern final - : mlir::OpInterfaceRewritePattern { - - explicit QuantumSinkPushPattern(mlir::MLIRContext* context) - : OpInterfaceRewritePattern(context) {} - - /** - * @brief Returns the block distance between a block and an operation that - * precedes it. - * - * @param toCheck The block to check. - * @param op The operation to find the distance to. - * @return The distance between the block and the operation or -1 if the - * operation is not in a predecessor block. - */ - int getDepth(mlir::Block& toCheck, const UnitaryInterface& op) const { - auto* originalBlock = op->getBlock(); - if (&toCheck == originalBlock) { - return 0; - } - int depth = -1; - for (auto* pred : toCheck.getPredecessors()) { - const auto result = getDepth(*pred, op); - if (result == -1) { - continue; - } - if (depth == -1) { - depth = result + 1; - } else { - depth = std::min(depth, result + 1); - } - } - return depth; - } - - /** - * @brief Out of a set of users, returns the closest one. - * - * In this case, "closest" denotes the operation that is in the closest - * successor block of the original operation. - * - * @param users The set of users to consider. - * @param op The operation to find the closest user for. - * @return The closest user operation. - */ - [[nodiscard]] mlir::Operation* - getNext(const mlir::ResultRange::user_range& users, - const UnitaryInterface& op) const { - mlir::Operation* next = nullptr; // NOLINT(misc-const-correctness) - int minDepth = 0; - for (auto* user : users) { - const auto depth = getDepth(*user->getBlock(), op); - if (depth == -1) { - continue; - } - if (next == nullptr || depth < minDepth) { - next = user; - minDepth = depth; - } - } - return next; - } - - /** - * @brief Returns the next branch operation that uses the given operation. - * - * In this case, "next" denotes the operation that is in the closest successor - * block of the original operation. - * - * @param op The operation to find the next branch operation for. - * @return The next branch operation that uses the given operation. - */ - [[nodiscard]] mlir::Operation* - getNextBranchOpUser(const UnitaryInterface& op) const { - auto allUsers = op->getUsers(); - std::vector output; - llvm::copy_if(allUsers, std::back_inserter(output), [&](auto* user) { - return mlir::isa(user) || - mlir::isa(user); - }); - auto* nextBranch = getNext(allUsers, op); - return nextBranch; - } - - /** - * @brief Replaces all uses of a given operation with the results of a clone, - * but only in the current block and all successor blocks. - * - * This differs from MLIR's `Operation::replaceAllUsesWith` in that it does - * not replace uses in parallel blocks. - * - * @param original The original operation to replace. - * @param clone The clone to replace with. - * @param block The block to start replacing in. - * @param visited A set of blocks that have already been visited to prevent - * endless loops. - */ - void - replaceAllChildUsesWith(mlir::Operation& original, mlir::Operation& clone, - mlir::Block& block, - std::unordered_set& visited) const { - if (visited.contains(&block)) { - return; - } - visited.insert(&block); - - for (auto& op : block.getOperations()) { - if (&op == &original) { - continue; - } - for (size_t i = 0; i < original.getResults().size(); i++) { - op.replaceUsesOfWith(original.getResult(i), clone.getResult(i)); - } - } - - for (auto* successor : block.getSuccessors()) { - replaceAllChildUsesWith(original, clone, *successor, visited); - } - } - - /** - * @brief Pushes the given Unitary operation through a branch operation into - * the given block. - * - * @param op The Unitary operation to push. - * @param block The block to push the operation into. - * @param blockArgs The arguments used to call the block. - * @param rewriter The pattern rewriter to use. - * @return The new arguments to call the block with. - */ - std::vector - pushIntoBlock(const UnitaryInterface& op, mlir::Block& block, - const mlir::OperandRange blockArgs, - mlir::PatternRewriter& rewriter) const { - std::vector newBlockArgs; - // We start by inserting the operation at the beginning of the block. - rewriter.setInsertionPointToStart(&block); - auto* clone = rewriter.clone(*op); - - // We iterate over all block args: If any of them involved the result of the - // pushed operation, they can be removed, as the operation is now already in - // the block. - for (int i = static_cast(blockArgs.size()) - 1; i >= 0; i--) { - auto arg = blockArgs[i]; - auto found = - std::find(op->getResults().begin(), op->getResults().end(), arg); - if (found == op->getResults().end()) { - newBlockArgs.emplace_back(arg); - continue; - } - auto idx = std::distance(op->getResults().begin(), found); - block.getArgument(i).replaceAllUsesWith(clone->getResult(idx)); - block.eraseArgument(i); - } - - std::unordered_set visited; - replaceAllChildUsesWith(*op, *clone, block, visited); - - // Now, the block instead needs to be passed the inputs of the pushed - // operation so we extend the block with new arguments. - for (size_t i = 0; i < clone->getOperands().size(); i++) { - auto in = clone->getOperand(i); - auto newArg = block.addArgument(in.getType(), clone->getLoc()); - rewriter.modifyOpInPlace(clone, [&]() { clone->setOperand(i, newArg); }); - newBlockArgs.emplace_back(in); - } - - return newBlockArgs; - } - - /** - * @brief Breaks a critical edge through the given branch operation by adding - * a new block. - * - * @param oldTarget The target block to which the critical edge went. - * @param branchOp The branch operation that caused the critical edge. - * @param rewriter The pattern rewriter to use. - * @return The new block that was created. - */ - static mlir::Block* breakCriticalEdge(mlir::Block& oldTarget, - mlir::Operation& branchOp, - mlir::PatternRewriter& rewriter) { - auto* newBlock = rewriter.createBlock(oldTarget.getParent()); - std::vector newBlockOutputs; - newBlockOutputs.reserve(oldTarget.getNumArguments()); - for (auto arg : oldTarget.getArguments()) { - auto newArg = newBlock->addArgument(arg.getType(), branchOp.getLoc()); - newBlockOutputs.emplace_back(newArg); - } - rewriter.setInsertionPointToEnd(newBlock); - rewriter.create(branchOp.getLoc(), &oldTarget, - newBlockOutputs); - return newBlock; - } - - /** - * @brief Pushes a Unitary operation through a single conditional branch - * operation. - * - * @param op The Unitary operation to push. - * @param condBranchOp The conditional branch operation to push through. - * @param rewriter The pattern rewriter to use. - */ - void rewriteCondBranch(const UnitaryInterface& op, - mlir::cf::CondBranchOp condBranchOp, - mlir::PatternRewriter& rewriter) const { - auto* targetBlockTrue = - std::distance(condBranchOp.getTrueDest()->getPredecessors().begin(), - condBranchOp.getTrueDest()->getPredecessors().end()) == 1 - ? condBranchOp.getTrueDest() - : breakCriticalEdge(*condBranchOp.getTrueDest(), *condBranchOp, - rewriter); - auto newBlockArgsTrue = pushIntoBlock( - op, *targetBlockTrue, condBranchOp.getTrueOperands(), rewriter); - - auto* targetBlockFalse = - std::distance(condBranchOp.getFalseDest()->getPredecessors().begin(), - condBranchOp.getFalseDest()->getPredecessors().end()) == 1 - ? condBranchOp.getFalseDest() - : breakCriticalEdge(*condBranchOp.getFalseDest(), *condBranchOp, - rewriter); - auto newBlockArgsFalse = pushIntoBlock( - op, *targetBlockFalse, condBranchOp.getFalseOperands(), rewriter); - - rewriter.setInsertionPoint(condBranchOp); - rewriter.replaceOpWithNewOp( - condBranchOp, condBranchOp.getCondition(), targetBlockTrue, - newBlockArgsTrue, targetBlockFalse, newBlockArgsFalse); - - rewriter.eraseOp(op); - } - - /** - * @brief Pushes a Unitary operation through a single branch operation. - * - * The successor of this branch will always have exactly one predecessor as - * per the match function. Therefore, we do not have to worry about critical - * edges. - * - * @param op The Unitary operation to push. - * @param branchOp The branch operation to push through. - * @param rewriter The pattern rewriter to use. - */ - void rewriteBranch(const UnitaryInterface& op, mlir::cf::BranchOp& branchOp, - mlir::PatternRewriter& rewriter) const { - auto newBlockArgs = pushIntoBlock(op, *branchOp.getDest(), - branchOp.getOperands(), rewriter); - rewriter.modifyOpInPlace(branchOp, [&]() { - branchOp.getDestOperandsMutable().assign(newBlockArgs); - }); - rewriter.eraseOp(op); - } - - mlir::LogicalResult - matchAndRewrite(UnitaryInterface op, - mlir::PatternRewriter& rewriter) const override { - // --- MATCH LOGIC --- - // Only consider 1-qubit gates - if (op.getAllOutQubits().size() != 1) { - return mlir::failure(); - } - - if (const auto& users = op->getUsers(); users.empty()) { - return mlir::failure(); - } - - auto* nextBranch = getNextBranchOpUser(op); - if (nextBranch == nullptr) { - return mlir::failure(); - } - - if (nextBranch->getBlock() != op->getBlock()) { - return mlir::failure(); - } - - // --- REWRITE LOGIC --- - if (const auto condBranchOp = - mlir::dyn_cast(nextBranch)) { - rewriteCondBranch(op, condBranchOp, rewriter); - return mlir::success(); - } - - if (auto branchOp = mlir::dyn_cast(nextBranch)) { - if (auto* successor = branchOp.getDest(); - std::distance(successor->getPredecessors().begin(), - successor->getPredecessors().end()) == 1) { - rewriteBranch(op, branchOp, rewriter); - rewriter.eraseOp(op); - return mlir::success(); - } - } - - return mlir::failure(); - } -}; - -/** - * @brief Populates the given pattern set with the - * `QuantumSinkPushPattern`. - * - * @param patterns The pattern set to populate. - */ -void populateQuantumSinkPushPatterns(mlir::RewritePatternSet& patterns) { - patterns.add(patterns.getContext()); -} - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/QuantumSinkShiftPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/QuantumSinkShiftPattern.cpp deleted file mode 100644 index 0180d3f7da..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/QuantumSinkShiftPattern.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -/** - * @brief This pattern is responsible to shift Unitary operations into the block - * their results are used in. - */ -struct QuantumSinkShiftPattern final - : mlir::OpInterfaceRewritePattern { - - explicit QuantumSinkShiftPattern(mlir::MLIRContext* context) - : OpInterfaceRewritePattern(context) {} - - /** - * @brief Recursively checks if a block is a successor of another block or the - * same block. - * - * @param successor The block that might be after the other block. - * @param predecessor The block to check against. - */ - bool isAfterOrEqual(mlir::Block& successor, mlir::Block& predecessor, - std::unordered_set& visited) const { - if (visited.contains(&successor)) { - return false; - } - visited.insert(&successor); - if (&successor == &predecessor) { - return true; - } - auto parents = successor.getPredecessors(); - return llvm::any_of(parents, [&](auto* parent) { - return isAfterOrEqual(*parent, predecessor, visited); - }); - } - - /** - * @brief Returns the earliest users of a given operation. - * - * In this case, "earliest" denotes all operations that are not preceded - * another operation in the list of users. - * - * @param users The users to find the earliest users for. - * @return A vector of all earliest users of the given operation. - */ - [[nodiscard]] std::vector - getEarliestUsers(mlir::ResultRange::user_range users) const { - std::vector earliestUsers; - llvm::copy_if(users, std::back_inserter(earliestUsers), [&](auto* user) { - return llvm::none_of(users, [&](auto* other) { - std::unordered_set visited; - return user != other && - isAfterOrEqual(*user->getBlock(), *other->getBlock(), visited); - }); - }); - return earliestUsers; - } - - /** - * @brief Replaces all uses of a given operation with the results of a clone, - * - * @param rewriter The pattern rewriter to use. - * @param original The original operation to replace. - * @param clone The clone to replace with. - * @param user The user operation to replace the inputs in. - */ - static void replaceInputsWithClone(mlir::PatternRewriter& rewriter, - mlir::Operation& original, - mlir::Operation& clone, - mlir::Operation& user) { - for (size_t i = 0; i < user.getOperands().size(); i++) { - const auto& operand = user.getOperand(i); - const auto found = std::find(original.getResults().begin(), - original.getResults().end(), operand); - if (found == original.getResults().end()) { - continue; - } - const auto idx = std::distance(original.getResults().begin(), found); - rewriter.modifyOpInPlace( - &user, [&] { user.setOperand(i, clone.getResults()[idx]); }); - } - } - - mlir::LogicalResult - matchAndRewrite(UnitaryInterface op, - mlir::PatternRewriter& rewriter) const override { - // We only consider 1-qubit gates. - if (op.getAllOutQubits().size() != 1) { - return mlir::failure(); - } - - // Ensure that there is at least one user. - const auto& users = op->getUsers(); - if (users.empty()) { - return mlir::failure(); - } - - // There may be multiple users if canonicalization has deemed a branch - // operand as pass-through and therefore removed it. If more than one user - // is in the same block, then the `QuantumSinkShiftPattern` cannot be - // applied. - if (!llvm::all_of(users, [&](auto* user) { - return user->getBlock() != op->getBlock(); - })) { - return mlir::failure(); - } - - // --- Begin Rewrite Phase --- - auto earliestUsers = getEarliestUsers(users); - - for (auto* user : earliestUsers) { - auto* block = user->getBlock(); - rewriter.setInsertionPoint(&block->front()); - auto* clone = rewriter.clone(*op); - for (size_t i = 0; i < user->getOperands().size(); i++) { - const auto& operand = user->getOperand(i); - const auto found = std::find(op->getResults().begin(), - op->getResults().end(), operand); - if (found == op->getResults().end()) { - continue; - } - const auto idx = std::distance(op->getResults().begin(), found); - rewriter.modifyOpInPlace( - user, [&] { user->setOperand(i, clone->getResult(idx)); }); - } - } - - rewriter.eraseOp(op); - return mlir::success(); - } -}; - -/** - * @brief Populates the given pattern set with the - * `QuantumSinkShiftPattern`. - * - * @param patterns The pattern set to populate. - */ -void populateQuantumSinkShiftPatterns(mlir::RewritePatternSet& patterns) { - patterns.add(patterns.getContext()); -} - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp deleted file mode 100644 index c5d328cc0d..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" -#include "mlir/IR/Attributes.h" -#include "mlir/IR/OperationSupport.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -static const std::unordered_set DIAGONAL_GATES = { - "i", "z", "s", "sdg", "t", "tdg", "p"}; - -/** - * @brief This pattern is responsible for replacing controls after measurements - * with `if` constructs. - */ -struct ReplaceBasisStateControlsWithIfPattern final - : mlir::OpInterfaceRewritePattern { - - explicit ReplaceBasisStateControlsWithIfPattern(mlir::MLIRContext* context) - : OpInterfaceRewritePattern(context) {} - - /** - * @brief Clones the given Unitary operation without the specified control - * operand. - * - * @param op The Unitary operation to clone. - * @param operand The control operand to remove. - * @param rewriter The pattern rewriter to use for creating the new operation. - * @return A new operation that is a clone of `op` without the specified - * control operand. - */ - static UnitaryInterface - cloneUnitaryOpWithoutControl(UnitaryInterface op, mlir::Value operand, - mlir::PatternRewriter& rewriter) { - rewriter.setInsertionPointAfter(op); - mlir::SmallVector remainingOperands; - mlir::SmallVector remainingTypes; - - // Determine whether the replaced operand is a target, positive control or - // negative control. - uint8_t groupIndex = 0; - if (llvm::find(op.getPosCtrlInQubits(), operand) != - op.getPosCtrlInQubits().end()) { - groupIndex = 1; - } else if (llvm::find(op.getNegCtrlInQubits(), operand) != - op.getNegCtrlInQubits().end()) { - groupIndex = 2; - } - if (groupIndex == 0) { - if (!op.getPosCtrlInQubits().empty()) { - // By setting the group index to 1, the first control qubit will be - // adapted as a target qubit when changing the variadic group sizes. - groupIndex = 1; - } else { - // If there are no positive controls, we can also use a negative control - // by setting the groupIndex to 2. In this case, however, we first need - // to add an x gate to the corresponding qubit. - auto targetNegCtrlInput = op.getNegCtrlInQubits().front(); - rewriter.setInsertionPoint(op); - auto xGate = rewriter.create( - op.getLoc(), targetNegCtrlInput.getType(), mlir::TypeRange{}, - mlir::TypeRange{}, mlir::DenseF64ArrayAttr{}, - mlir::DenseBoolArrayAttr{}, mlir::ValueRange{}, - mlir::ValueRange{targetNegCtrlInput}, mlir::ValueRange{}, - mlir::ValueRange{}); - rewriter.replaceUsesWithIf(targetNegCtrlInput, - xGate.getOutQubits().front(), - [&](mlir::OpOperand& operand) { - // We only replace the single use by the - // modified operation. - return operand.getOwner() == op; - }); - groupIndex = 2; - } - } - - // First, we add all parameters. - for (const auto paramOperand : op.getParams()) { - remainingOperands.emplace_back(paramOperand); - } - // Then we add all other operands, except the one that should be removed. - for (const auto [index, otherOperand] : - llvm::enumerate(op.getAllInQubits())) { - if (operand != otherOperand) { - remainingOperands.emplace_back(otherOperand); - } - } - for (auto it = op->getResults().begin() + 1; it != op->getResults().end(); - ++it) { - remainingTypes.push_back((*it).getType()); - } - - mlir::OperationState state(op.getLoc(), op->getName()); - state.addOperands(remainingOperands); - state.addTypes(remainingTypes); - - // We need to update the `operandSegmentSizes` and `resultSegmentSizes` - // attributes to reflect the removed operand. - mlir::SmallVector inSegSizes( - op->getAttrOfType("operandSegmentSizes") - .asArrayRef()); - mlir::SmallVector outSegSizes( - op->getAttrOfType("resultSegmentSizes") - .asArrayRef()); - inSegSizes[groupIndex + 1] -= - 1; // Adjust the segment size for the removed control - outSegSizes[groupIndex] -= - 1; // Adjust the segment size for the removed control - state.addAttribute("operandSegmentSizes", - rewriter.getDenseI32ArrayAttr(inSegSizes)); - state.addAttribute("resultSegmentSizes", - rewriter.getDenseI32ArrayAttr(outSegSizes)); - return mlir::dyn_cast(rewriter.create(state)); - } - - /** - * @brief Matches and rewrites a single control of a Unitary operation - * @param op The Unitary operation to match and rewrite. - * @param operand The control operand to match. - * @param positive Whether the control is positive or negative. - * @param rewriter The pattern rewriter to use for creating the new operation. - * @return A new UnitaryInterface operation if the control was successfully or - * `nullptr` if the control could not be matched or rewritten. - */ - static UnitaryInterface - matchAndRewriteSingleControl(UnitaryInterface op, mlir::Value operand, - bool positive, mlir::PatternRewriter& rewriter) { - auto* predecessor = operand.getDefiningOp(); - auto predecessorMeasurement = mlir::dyn_cast(predecessor); - if (!predecessorMeasurement) { - return nullptr; // The operand does not come from a measurement. - } - // The control's output is removed, so other operations need to use the - // previous input now. - const auto correspondingOutput = op.getCorrespondingOutput(operand); - rewriter.replaceAllUsesWith(correspondingOutput, operand); - - // We first create a new operation that is the same as before, just with the - // current control removed - auto reducedOp = cloneUnitaryOpWithoutControl(op, operand, rewriter); - - // Now we create the `scf.if` operation that uses the measurement result as - // condition and yields the outcome of the reducedOp. - const auto outcome = predecessorMeasurement.getOutBit(); - auto ifOp = rewriter.create( - op.getLoc(), reducedOp->getResultTypes(), outcome, true); - auto* satisfiedBlock = positive ? ifOp.thenBlock() : ifOp.elseBlock(); - auto* nonSatisfiedBlock = positive ? ifOp.elseBlock() : ifOp.thenBlock(); - - // If the control is satisfied, the reduced operation is executed. - rewriter.moveOpBefore(reducedOp, satisfiedBlock, satisfiedBlock->begin()); - rewriter.setInsertionPointAfter(reducedOp); - rewriter.create(op.getLoc(), reducedOp->getResults()); - - // If the control is not satisfied, we yield the original values of the - // qubits. - rewriter.setInsertionPointToStart(nonSatisfiedBlock); - rewriter.create(op.getLoc(), - reducedOp.getAllInQubits()); - - // All remaining uses of the original op are replaced by the results of the - // `if` operation. Then, the original operation is erased. - int offset = 0; - for (const auto [index, value] : llvm::enumerate(op->getResults())) { - if (value == correspondingOutput) { - // Set `offset` to align the values with the results of the `if` - // operation then skip the operand, as it is already replaced. - offset = -1; - continue; - } - rewriter.replaceAllUsesWith(value, ifOp.getResult(index + offset)); - } - rewriter.eraseOp(op); - - return reducedOp; - } - - /** - * @brief Tries to process the control set (positive or negative) of a Unitary - * operation. - * - * Returns a new UnitaryInterface with updated controls once the first control - * is successfully processed. - * - * @param op The Unitary operation to process. - * @param controls The set of control operands to process. - * @param isPositive Whether the controls are positive or negative. - * @param rewriter The pattern rewriter to use for creating the new operation. - * @return The new UnitaryInterface operation if a control was successfully - * processed, or `std::nullopt` if no controls were processed. - */ - std::optional - tryProcessControls(UnitaryInterface op, const auto& controls, bool isPositive, - mlir::PatternRewriter& rewriter) const { - for (const auto& operand : controls) { - if (auto result = - matchAndRewriteSingleControl(op, operand, isPositive, rewriter)) { - return result; - } - } - return std::nullopt; - } - - mlir::LogicalResult - matchAndRewrite(UnitaryInterface op, - mlir::PatternRewriter& rewriter) const override { - auto foundMatch = false; - while (true) { - if (auto result = - tryProcessControls(op, op.getPosCtrlInQubits(), true, rewriter)) { - foundMatch = true; - op = *result; // Update the operation to the new one with the control - // removed. - continue; - } - if (auto result = tryProcessControls(op, op.getNegCtrlInQubits(), false, - rewriter)) { - foundMatch = true; - op = *result; // Update the operation to the new one with the control - // removed. - continue; - } - if (DIAGONAL_GATES.count(op->getName().stripDialect().str()) == 1 && - op.getAllInQubits().size() > op.getInQubits().size()) { - // For diagonal gates, targets can also be treated as controls - // Therefore, we also check if there are more total in qubits than - // target in qubits (i.e. at least one control exists). - if (auto result = - tryProcessControls(op, op.getInQubits(), true, rewriter)) { - foundMatch = true; - op = *result; // Update the operation to the new one with the target - // removed. - continue; - } - } - - // If no more controls can be processed, we break out of the loop. - break; - } - return mlir::success(foundMatch); - } -}; - -/** - * @brief Populates the given pattern set with the - * `ReplaceBasisStateControlsWithIfPattern`. - * - * @param patterns The pattern set to populate. - */ -void populateReplaceBasisStateControlsWithIfPatterns( - mlir::RewritePatternSet& patterns) { - patterns.add(patterns.getContext()); -} - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPass.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPass.cpp deleted file mode 100644 index 4cfc906d45..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPass.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include - -namespace mqt::ir::opt { - -#define GEN_PASS_DEF_REUSEQUBITSPASS -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" - -/** - * @brief This pass attempts to reduce the number of required qubits by reusing - * existing ones that are no longer used. - */ -struct ReuseQubitsPass final : impl::ReuseQubitsPassBase { - - void runOnOperation() override { - // Get the current operation being operated on. - auto op = getOperation(); - auto* ctx = &getContext(); - - // Define the set of patterns to use. - mlir::RewritePatternSet patterns(ctx); - populateReuseQubitsPatterns(patterns); - - // Apply patterns in an iterative and greedy manner. - if (mlir::failed(mlir::applyPatternsGreedily(op, std::move(patterns)))) { - signalPassFailure(); - } - } -}; - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp deleted file mode 100644 index 13fca936fd..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/ReuseQubitsPattern.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -/** - * @brief This pattern is responsible applying qubit reuse. - */ -struct ReuseQubitsPattern final : mlir::OpRewritePattern { - - explicit ReuseQubitsPattern(mlir::MLIRContext* context) - : OpRewritePattern(context) {} - - /** - * @brief Finds all reachable `DeallocQubitOp` operation starting from some - * qubit. - * @param allocQubit The starting qubit to check (e.g. a newly allocated - * qubit). - * @return A set of all DeallocQubitOp operations reachable from the given - */ - static llvm::DenseSet - findAllReachableDeallocs(mlir::Value allocQubit) { - // Traverse def-use chain using BFS. - llvm::DenseSet reachableSinks; - llvm::SmallVector toVisit{allocQubit.getUsers().begin(), - allocQubit.getUsers().end()}; - llvm::DenseSet visited; - while (!toVisit.empty()) { - auto* current = toVisit.back(); - toVisit.pop_back(); - visited.insert(current); - - // If we reach the dealloc operation, we add it to the list of sinks. - if (mlir::isa(current)) { - reachableSinks.insert(current); - continue; - } - - if (auto yieldOp = mlir::dyn_cast(current)) { - // If we reach a yield operation, we continue from the corresponding - // `parent` (e.g. `scf.if`). - toVisit.push_back(yieldOp->getParentOp()); - continue; - } - - // Add all users of the current operation to the visit list. - for (auto result : current->getResults()) { - for (auto* user : result.getUsers()) { - if (visited.contains(user)) { - continue; - } - toVisit.push_back(user); - } - } - } - - return reachableSinks; - } - - /** - * @brief Reorders the users of the given operation to ensure that they are - * after it. - * @param op The operation whose users should be reordered. - * @param rewriter The pattern rewriter to use for moving operations. - */ - static void reorderUsers(mlir::Operation* startingOp, - mlir::PatternRewriter& rewriter) { - // Search for operations that need re-ordering using BFS. - mlir::DenseSet toVisit{startingOp}; - mlir::DenseSet visited; - - while (!toVisit.empty()) { - auto* op = *toVisit.begin(); - toVisit.erase(op); - visited.insert(op); - for (auto* user : op->getUsers()) { - // Move the user operation after the current operation. - - while (op->getBlock() != user->getBlock()) { - user = user->getParentOp(); - } - if (op->isBeforeInBlock(user)) { - continue; // Already in the correct order. - } - rewriter.moveOpAfter(user, op); - - if (!visited.contains(user)) { - toVisit.insert(user); - } - } - } - } - - /** - * @brief Rewrites the given `AllocQubitOp` and `DeallocQubitOp` to reuse the - * qubit instead. - * - * @param alloc The allocation that will be replaced by qubit reuse. - * @param sink The deallocation that will be replaced by a new reset - * operation. - * @param rewriter The pattern rewriter to use for the rewrite. - */ - static void rewriteForReuse(AllocQubitOp alloc, mlir::Operation* sink, - mlir::PatternRewriter& rewriter) { - rewriter.setInsertionPointAfter(sink); - const mlir::Value originalInput = sink->getOperand(0); - auto reset = rewriter.replaceOpWithNewOp( - alloc, alloc.getQubit().getType(), originalInput); - rewriter.eraseOp(sink); - - reorderUsers(reset, rewriter); - } - - mlir::LogicalResult - matchAndRewrite(AllocQubitOp op, - mlir::PatternRewriter& rewriter) const override { - // Find all `DeallocQubitOp` operations in the current block and check - // if any of them are disjoint from the qubit being allocated, indicating - // potential for reuse. - - auto* currentBlock = op->getBlock(); - auto deallocs = currentBlock->getOps(); - llvm::DenseSet reachableDeallocs = - findAllReachableDeallocs(op.getQubit()); - // We search `reverse(deallocs)` rather than `deallocs` because this tends - // to give more readable results. - auto reusableDeallocs = - llvm::find_if(llvm::reverse(deallocs), [&](DeallocQubitOp dealloc) { - // Check if the qubit to be deallocated is disjoint from the qubit to - // be allocated. - return !reachableDeallocs.contains(dealloc); - }); - - if (reusableDeallocs == llvm::reverse(deallocs).end()) { - return mlir::failure(); - // No reusable dealloc found. - // We could also check `reset` operations next, which would - // always result in the optimal solution, but the complexity explodes. - // Therefore, we only check for deallocs here. - } - - rewriteForReuse(op, *reusableDeallocs, rewriter); - return mlir::success(); - } -}; - -/** - * @brief Populates the given pattern set with the - * `ReuseQubitsPattern`. - * - * @param patterns The pattern set to populate. - */ -void populateReuseQubitsPatterns(mlir::RewritePatternSet& patterns) { - patterns.add(patterns.getContext()); -} - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/SwapReconstructionAndElision.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/SwapReconstructionAndElision.cpp deleted file mode 100644 index 1b8e47d4d6..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/SwapReconstructionAndElision.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include - -namespace mqt::ir::opt { - -#define GEN_PASS_DEF_SWAPRECONSTRUCTIONANDELISION -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" - -/** - * @brief This pattern attempts to remove SWAP gates by re-ordering qubits. - */ -struct SwapReconstructionAndElision final - : impl::SwapReconstructionAndElisionBase { - - void runOnOperation() override { - // Get the current operation being operated on. - auto op = getOperation(); - auto* ctx = &getContext(); - - // Define the set of patterns to use. - mlir::RewritePatternSet patterns(ctx); - populateSwapReconstructionAndElisionPatterns(patterns); - - // Configure greedy driver - mlir::GreedyRewriteConfig config; - config.setUseTopDownTraversal(true); - - // Apply patterns in an iterative and greedy manner. - if (mlir::failed( - mlir::applyPatternsGreedily(op, std::move(patterns), config))) { - signalPassFailure(); - } - } -}; - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/SwapReconstructionAndElisionPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/SwapReconstructionAndElisionPattern.cpp deleted file mode 100644 index 17f48449e9..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/SwapReconstructionAndElisionPattern.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -/** - * @brief This pattern attempts to remove uncontrolled SWAP gates by re-ordering - * qubits. - */ -struct ElidePermutationsPattern final : mlir::OpRewritePattern { - - explicit ElidePermutationsPattern(mlir::MLIRContext* context) - : OpRewritePattern(context) { - setHasBoundedRewriteRecursion(true); - } - - mlir::LogicalResult - matchAndRewrite(SWAPOp op, mlir::PatternRewriter& rewriter) const override { - if (op.isControlled()) { - return mlir::failure(); - } - - const auto inQubits = op.getInQubits(); - assert(inQubits.size() == 2); - - rewriter.replaceOp(op, {inQubits[1], inQubits[0]}); - - return mlir::success(); - } -}; - -/** - * @brief This pattern attempts to find CNOT patterns which can be replaced by a - * SWAP gate and directly elides the new permutation. - * - * Example (matchTwoCNOTPattern == true): - * ┌───┐ ┌───┐ ┌───┐ - * q1 ──■──┤ X ├ q1 ──■──┤ X ├──■────■── ──╳────■── q1 ─q0─┤ X ├ q0 - * ┌─┴─┐└─┬─┘ => ┌─┴─┐└─┬─┘┌─┴─┐┌─┴─┐ => | ┌─┴─┐ => └─┬─┘ - * q0 ┤ X ├──■── q0 ┤ X ├──■──┤ X ├┤ X ├ ──╳──┤ X ├ q0 ─q1───■── q1 - * └───┘ └───┘ └───┘└───┘ └───┘ - * - * Example (matchTwoCNOTPattern == false) - * ┌───┐ - * q1: ──■──┤ X ├──■── q1 q1: ──╳── q1 q1 ─ q0 - * ┌─┴─┐└─┬─┘┌─┴─┐ => | => - * q0: ┤ X ├──■──┤ X ├ q0 q0: ──╳── q0 q0 ─ q1 - * └───┘ └───┘ - */ -template -struct SwapReconstructionAndElisionPattern final : mlir::OpRewritePattern { - - explicit SwapReconstructionAndElisionPattern(mlir::MLIRContext* context) - : OpRewritePattern(context, benefit()) { - setHasBoundedRewriteRecursion(true); - } - - [[nodiscard]] constexpr static mlir::PatternBenefit benefit() { - return matchTwoCNOTPattern ? 2 : 3; - } - - /** - * @brief If pattern is applicable, perform MLIR rewrite. - * - * Steps(matchTwoCNOTPattern == true): - * 1. Find CNOT with exactly one positive control qubit (1st CNOT) - * 2. Check if it has adjacent CNOT with swapped control/target (2nd CNOT) - * (3. Theoretically place two CNOTs identical to 1st CNOT on other side of - * 2nd CNOT; not actually done because they would be immediately replaced - * by SWAP anyway) - * 4. Replace 1st CNOT with swapped target and control; this swap makes it - * equivalent to 2nd CNOT and directly permutates the order of the qubits - * to elide the theoretically inserted SWAP - * 5. Erase 2nd CNOT - * - * Steps(matchTwoCNOTPattern == false): - * 1. Find CNOT with exactly one positive control qubit (1st CNOT) - * 2. Check if it has adjacent CNOT with swapped control/target (2nd CNOT) - * 3. Check if 2nd CNOT has another adjacent CNOT with swapped - * control/target (3rd CNOT) - * 4. Erase 1st CNOT and swap output qubits for swap elision - * 5. Erase 2nd CNOT and 3rd CNOT - */ - mlir::LogicalResult - matchAndRewrite(XOp op, mlir::PatternRewriter& rewriter) const override { - // step 1 - if (op.getPosCtrlInQubits().size() != 1 && - op.getNegCtrlInQubits().empty()) { - return mlir::failure(); - } - - // steps 2 - if (auto secondCNOT = findReverseCNOTPattern(op)) { - if constexpr (matchTwoCNOTPattern) { - performTwoCNOTSwapReconstructionAndElision(rewriter, op, *secondCNOT); - return mlir::success(); - } - - if (auto thirdCNOT = findReverseCNOTPattern(*secondCNOT)) { - performThreeCNOTSwapReconstructionAndElision(rewriter, op, *secondCNOT, - *thirdCNOT); - return mlir::success(); - } - } - - return mlir::failure(); - } - - /** - * @brief Perform two-CNOT-swap-reconstruction and elision on two CNOTs for - * which isReverseCNOTPattern() must return true. - * - * @note This is used when matchTwoCNOTPattern is true. - * - * ┌───┐ ┌───┐ - * q1 ──■──┤ X ├ q1 q1 ─q0─┤ X ├ q0 - * ┌─┴─┐└─┬─┘ => └─┬─┘ - * q0 ┤ X ├──■── q0 q0 ─q1───■── q1 - * └───┘ - */ - static void performTwoCNOTSwapReconstructionAndElision( - mlir::PatternRewriter& rewriter, XOp& firstCNOT, const XOp& secondCNOT) { - const auto qubitType = QubitType::get(rewriter.getContext()); - - // step 3 + 4 - rewriter.replaceOpWithNewOp( - firstCNOT, qubitType, qubitType, mlir::TypeRange{}, - mlir::DenseF64ArrayAttr{}, mlir::DenseBoolArrayAttr{}, - mlir::ValueRange{}, - // swap control and target - firstCNOT.getPosCtrlInQubits(), firstCNOT.getInQubits(), - mlir::ValueRange{}); - // step 5 - rewriter.replaceOp(secondCNOT, secondCNOT->getOperands()); - } - - /** - * @brief Perform swap reconstruction and elision on given CNOT operations - * which must be equivalent to a swap. - * - * This is done by swapping the output of one of the CNOTs and removing all - * others. - * - * @note This is used when matchTwoCNOTPattern is false. - * - * ┌───┐ - * q1: ──■──┤ X ├──■── q1 q1 ─ q0 - * ┌─┴─┐└─┬─┘┌─┴─┐ => - * q0: ┤ X ├──■──┤ X ├ q0 q0 ─ q1 - * └───┘ └───┘ - */ - static void performThreeCNOTSwapReconstructionAndElision( - mlir::PatternRewriter& rewriter, const XOp& firstCNOT, - const XOp& secondCNOT, const XOp& thirdCNOT) { - // step 4 - rewriter.replaceOp(firstCNOT, - {firstCNOT->getOperand(1), firstCNOT->getOperand(0)}); - // step 5 - rewriter.replaceOp(secondCNOT, secondCNOT->getOperands()); - rewriter.replaceOp(thirdCNOT, thirdCNOT->getOperands()); - } - - /** - * @brief Checks if two consecutive gates have reversed control and target - * qubits. - * - * ┌───┐ - * q0: ──■──┤ X ├ - * ┌─┴─┐└─┬─┘ - * q1: ┤ X ├──■── - * └───┘ - * - * @param a The first gate. - * @param b The second gate which must be one of the users of a. - * @return True if the gates match the pattern described above, otherwise - * false. - */ - [[nodiscard]] static bool isReverseCNOTPattern(XOp& a, XOp& b) { - return a.getNegCtrlOutQubits().empty() && b.getNegCtrlInQubits().empty() && - llvm::equal(a.getOutQubits(), b.getPosCtrlInQubits()) && - llvm::equal(b.getInQubits(), a.getPosCtrlOutQubits()); - } - - /** - * @brief Find a user of the given operation for which isReverseCNOTPattern() - * is true. - * - * @param op Operation for which the users should be scanned for the pattern - */ - [[nodiscard]] static std::optional findReverseCNOTPattern(XOp& op) { - for (auto* user : op->getUsers()) { - if (auto cx = llvm::dyn_cast(user)) { - if (isReverseCNOTPattern(op, cx)) { - return cx; - } - } - } - return std::nullopt; - } -}; - -/** - * @brief Populates the given pattern set with the `ElidePermutationsPattern`. - * - * @param patterns The pattern set to populate. - */ -void populateSwapReconstructionAndElisionPatterns( - mlir::RewritePatternSet& patterns) { - patterns.add(patterns.getContext()); - patterns.add>( - patterns.getContext()); - patterns.add>( - patterns.getContext()); -} - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/ToQuantumComputationPattern.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/ToQuantumComputationPattern.cpp deleted file mode 100644 index b7b17bf5c5..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/ToQuantumComputationPattern.cpp +++ /dev/null @@ -1,488 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "ir/Definitions.hpp" -#include "ir/QuantumComputation.hpp" -#include "ir/operations/Control.hpp" -#include "ir/operations/OpType.hpp" -#include "ir/operations/StandardOperation.hpp" -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -static bool isQubitType(mlir::MemRefType type) { - return llvm::isa(type.getElementType()); -} - -static bool isQubitType(mlir::memref::LoadOp op) { - const auto& memRef = op.getMemref(); - const auto& memRefType = llvm::cast(memRef.getType()); - return isQubitType(memRefType); -} - -static bool isQubitType(mlir::memref::StoreOp op) { - const auto& memRef = op.getMemref(); - const auto& memRefType = llvm::cast(memRef.getType()); - return isQubitType(memRefType); -} - -static bool isSupportedMemRefOp(mlir::Operation* op) { - if (auto loadOp = llvm::dyn_cast(op)) { - return isQubitType(loadOp); - } - if (auto storeOp = llvm::dyn_cast(op)) { - return isQubitType(storeOp); - } - return false; -} - -/// Analysis pattern that filters out all quantum operations from a given -/// program and creates a quantum computation from them. -struct ToQuantumComputationPattern final - : mlir::OpRewritePattern { - qc::QuantumComputation& circuit; // NOLINT(*-avoid-const-or-ref-data-members) - - explicit ToQuantumComputationPattern(mlir::MLIRContext* context, - qc::QuantumComputation& qc) - : OpRewritePattern(context), circuit(qc) {} - - /** - * @brief Finds the index of a qubit in the list of previously defined qubit - * variables. - * - * In particular, this function checks if two value definitions are the same, - * and, in case of array-style variables, also checks if the result index is - * the same. - * - * @param input The qubit to find. - * @param currentQubitVariables The list of previously defined qubit - * variables. - * - * @return The index of the qubit in the list of previously defined qubit - * variables. - * - * @throws `std::runtime_error` if the qubit is not found. - */ - static size_t - findQubitIndex(const mlir::Value& input, - std::vector& currentQubitVariables) { - size_t arrayIndex = 0; - - // Check if the input is an operation result - if (const auto opResult = llvm::dyn_cast(input)) { - arrayIndex = opResult.getResultNumber(); - } else { - throw std::runtime_error( - "Operand is not an operation result. This should never happen!"); - } - - // Find the qubit in the list of previously defined qubit variables - for (size_t i = 0; i < currentQubitVariables.size(); i++) { - size_t qubitArrayIndex = 0; - if (currentQubitVariables[i] != nullptr) { - auto opResult = - llvm::dyn_cast(currentQubitVariables[i]); - // E.g., index `1` if it comes from a load op - qubitArrayIndex = opResult.getResultNumber(); - } else { - // Qubit is not (yet) in currentQubitVariables - throw std::runtime_error( - "Qubit was not found in list of previously defined qubits"); - } - - if (currentQubitVariables[i] == input && arrayIndex == qubitArrayIndex) { - return i; - } - } - - // Qubit is not (yet) in currentQubitVariables - throw std::runtime_error( - "Qubit was not found in list of previously defined qubits"); - } - - /** - * @brief Converts a measurement to an operation on the - * `qc::QuantumComputation` and updates the `currentQubitVariables`. - * - * @param op The operation to convert. - * @param currentQubitVariables The list of previously defined qubit - * variables. - * - * @return True if the operation was successfully handled, false if the - * operation defining the qubit operand was not yet added to the - * currentQubitVariables. - */ - bool handleMeasureOp(MeasureOp& op, - std::vector& currentQubitVariables) const { - const auto in = op.getInQubit(); - const auto out = op.getOutQubit(); - - size_t inIndex = 0; - - try { - inIndex = findQubitIndex(in, currentQubitVariables); - } catch (const std::runtime_error& e) { - if (strcmp(e.what(), - "Qubit was not found in list of previously defined qubits") == - 0) { - // Try again later when all qubits are available - return false; - } - throw; // Rethrow the exception if it's not the expected one. - } - - currentQubitVariables[inIndex] = out; - circuit.measure(inIndex, inIndex); - return true; - } - - /** - * @brief Converts a unitary operation to an operation on the - * `qc::QuantumComputation` and updates the `currentQubitVariables`. - * - * @param op The operation to convert. - * @param currentQubitVariables The list of previously defined qubit - * variables. - * - * @return True if the operation was successfully handled, false if the - * operation defining the qubit operand was not yet added to the - * currentQubitVariables. - */ - bool handleUnitaryOp(UnitaryInterface op, - std::vector& currentQubitVariables) const { - - // Add the operation to the QuantumComputation. - qc::OpType opType = qc::OpType::None; - - try { - const std::string type = op->getName().stripDialect().str(); - opType = qc::opTypeFromString(type); - } catch (const std::invalid_argument& e) { - throw std::runtime_error( - "Unsupported operation type: " + op->getName().getStringRef().str() + - " (" + e.what() + ")"); - } - - const auto in = op.getInQubits(); - const auto posCtrlIns = op.getPosCtrlInQubits(); - const auto negCtrlIns = op.getNegCtrlInQubits(); - const auto outs = op.getAllOutQubits(); - - // Get the qubit index of every control qubit. - std::vector posCtrlInsIndices; - std::vector negCtrlInsIndices; - std::vector targetIndex(2); // adapted for two-target gates - try { - for (const auto& val : posCtrlIns) { - posCtrlInsIndices.emplace_back( - findQubitIndex(val, currentQubitVariables)); - } - for (const auto& val : negCtrlIns) { - negCtrlInsIndices.emplace_back( - findQubitIndex(val, currentQubitVariables)); - } - // Get the qubit index of the target qubit (if already collected). - targetIndex[0] = findQubitIndex(in[0], currentQubitVariables); - if (in.size() > 1) { - targetIndex[1] = findQubitIndex(in[1], currentQubitVariables); - } - } catch (const std::runtime_error& e) { - if (strcmp(e.what(), - "Qubit was not found in list of previously defined qubits") == - 0) { - // Try again later when all qubits are available - return false; - } - throw; // Rethrow the exception if it's not the expected one. - } - // Update `currentQubitVariables` with the new qubit values. - for (size_t i = 0; i < posCtrlInsIndices.size(); i++) { - currentQubitVariables[posCtrlInsIndices[i]] = outs[i + 1]; - } - for (size_t i = 0; i < negCtrlInsIndices.size(); i++) { - currentQubitVariables[negCtrlInsIndices[i]] = - outs[i + 1 + posCtrlInsIndices.size()]; - } - currentQubitVariables[targetIndex[0]] = outs[0]; - if (op.getOutQubits().size() > 1) { - currentQubitVariables[targetIndex[1]] = outs[1]; - } - - // Add the operation to the QuantumComputation. - qc::Controls controls; - for (const auto& index : posCtrlInsIndices) { - controls.emplace(static_cast(index), qc::Control::Type::Pos); - } - for (const auto& index : negCtrlInsIndices) { - controls.emplace(static_cast(index), qc::Control::Type::Neg); - } - - std::vector parameters; - if (const auto staticParamsAttr = - op->getAttrOfType("static_params")) { - parameters.reserve(staticParamsAttr.size()); - for (const auto& param : staticParamsAttr.asArrayRef()) { - parameters.emplace_back(param); - } - } - - if (op.getOutQubits().size() > 1) { - circuit.emplace_back( - controls, targetIndex[0], targetIndex[1], opType, parameters); - } else { - circuit.emplace_back(controls, targetIndex[0], - opType, parameters); - } - - return true; - } - - /** - * @brief Recursively deletes an operation and all its defining operations if - * they have no users. - * - * This procedure cleans up the AST so that only the base `alloc` operation - * remains. Operations that still have users are ignored so that their users - * can be handled first in a later step. - * - * @param op The operation to delete. - * @param rewriter The pattern rewriter to use for deleting the operation. - */ - static void deleteRecursively(mlir::Operation& op, - mlir::PatternRewriter& rewriter) { - if (llvm::isa(op)) { - return; // Do not delete alloc operations. - } - if (!op.getUsers().empty()) { - return; // Do not delete operations with users. - } - - rewriter.eraseOp(&op); - for (auto operand : op.getOperands()) { - if (auto* defOp = operand.getDefiningOp()) { - deleteRecursively(*defOp, rewriter); - } - } - } - - /** - * @brief Updates the inputs of non MQTOpt-operations that use - * MQTOpt-operations as inputs. - * - * Currently, such an operation should only be the return operation, but this - * function is compatible with any operation that uses MQTOpt-operations as - * inputs. Only qregs and classical values may be used as inputs to non - * MQTOpt-operations, Qubits are not supported! - * - * @param op The operation to update. - * @param rewriter The pattern rewriter to use. - * @param qreg The new qreg to replace old qreg uses with. - */ - static void updateMQTOptInputs(mlir::Operation& op, - mlir::PatternRewriter& rewriter, - const mlir::Value& qreg) { - size_t i = 0; - auto* const cloned = rewriter.clone(op); - rewriter.setInsertionPoint(cloned); - for (auto operand : op.getOperands()) { - i++; - const auto type = operand.getType(); - if (llvm::isa(type)) { - throw std::runtime_error( - "Interleaving of qubits with non MQTOpt-operations not supported " - "by round-trip pass!"); - } - if (llvm::isa(type)) { - // Operations that used the old `qreg` will now use the new one - // instead. - cloned->setOperand(i - 1, qreg); - } - if (llvm::isa(type)) { - // Operations that used `i1` values (i.e. classical measurement results) - // will now use a constant value of `false`. - auto newInput = rewriter.create( - op.getLoc(), rewriter.getI1Type(), rewriter.getBoolAttr(false)); - cloned->setOperand(i - 1, newInput.getResult()); - } - } - - rewriter.replaceOp(&op, cloned); - } - - static std::optional getLoadIndex(mlir::memref::LoadOp loadOp) { - if (const auto index = loadOp.getIndices().front()) { - if (auto constOp = index.getDefiningOp()) { - if (auto intAttr = - llvm::dyn_cast(constOp.getValue())) { - return static_cast(intAttr.getInt()); - } - } - } - return std::nullopt; - } - - mlir::LogicalResult - matchAndRewrite(mlir::memref::AllocOp op, - mlir::PatternRewriter& rewriter) const override { - // Skip if already marked - if (op->hasAttr("to_replace") || op->hasAttr("mqt_core")) { - return mlir::failure(); - } - - if (!op.getType().hasStaticShape()) { - throw std::runtime_error( - "Failed to resolve size of qubit register in alloc operation"); - } - auto size = op.getType().getShape().front(); - - // First, we create a new `AllocOp` that will replace the old one. It - // includes the flag `to_replace`. - // Create a new qubit register with the correct number of qubits. - const auto& qubitType = QubitType::get(rewriter.getContext()); - const auto& memRefType = mlir::MemRefType::get({0}, qubitType); - auto newAlloc = rewriter.create( - op.getLoc(), memRefType, mlir::ValueRange{}); - newAlloc->setAttr("to_replace", rewriter.getUnitAttr()); - - const std::size_t numQubits = size; - // `currentQubitVariables` holds the current `Value` representation of each - // qubit from the original register. - std::vector currentQubitVariables(numQubits); - - std::string regName; - llvm::raw_string_ostream os(regName); - op.getResult().print(os); - - circuit.addQubitRegister(numQubits, regName); - circuit.addClassicalRegister(numQubits); - - std::unordered_set visited; - // `set` to prevent 'nondeterministic iteration of pointers' - std::set mqtUsers; - - mlir::Operation* current = op; - while (current != nullptr) { - // no need to visit non-mqtopt operations - if (visited.contains(current) || - (current->getDialect()->getNamespace() != DIALECT_NAME_MQTOPT && - !isSupportedMemRefOp(current))) { - current = current->getNextNode(); - continue; - } - visited.insert(current); - - // collect all non-mqtopt users of the current operation - for (mlir::Operation* user : current->getUsers()) { - if (user->getDialect()->getNamespace() != DIALECT_NAME_MQTOPT && - !isSupportedMemRefOp(user)) { - mqtUsers.insert(user); - } - } - - if (llvm::isa(current)) { - auto unitaryOp = llvm::dyn_cast(current); - handleUnitaryOp(unitaryOp, currentQubitVariables); - } else if (auto loadOp = llvm::dyn_cast(current)) { - auto maybeIndex = getLoadIndex(loadOp); - if (!maybeIndex.has_value()) { - throw std::runtime_error( - "Failed to resolve index in extractQubit operation"); - } - const size_t index = *maybeIndex; - currentQubitVariables[index] = loadOp.getResult(); - } else if (llvm::isa(current)) { - // Do nothing for now, may (and probably should) change later. - } else if (llvm::isa(current)) { - // We count the number of measurements and add a measurement operation - // to the QuantumComputation. - auto measureOp = llvm::dyn_cast(current); - handleMeasureOp(measureOp, currentQubitVariables); - } else { - llvm::outs() << "Skipping unsupported operation: " << *current << "\n"; - continue; - } - - current = current->getNextNode(); - } - - llvm::outs() << "----------------------------------------\n\n"; - llvm::outs() << "-------------------QC-------------------\n"; - std::stringstream ss{}; - circuit.print(ss); - const auto circuitString = ss.str(); - llvm::outs() << circuitString << "\n"; - llvm::outs() << "----------------------------------------\n\n"; - - // Update the inputs of all non-mqtopt operations that use mqtopt operations - // as inputs, as these will be deleted later. - for (auto* operation : mqtUsers) { - updateMQTOptInputs(*operation, rewriter, newAlloc.getMemref()); - } - - // Remove all dead mqtopt operations except AllocOp. - visited.erase(op); // Skip the original AllocOp - bool progress = true; - while (progress) { - progress = false; - - for (auto it = visited.begin(); it != visited.end(); /* no increment */) { - mlir::Operation* operation = *it; - - // Only erase ops with no remaining users - if (operation->getUsers().empty()) { - it = visited.erase(it); // remove from visited - rewriter.eraseOp(operation); // erase from IR - progress = true; - } else { - ++it; - } - } - } - - rewriter.replaceOp(op, newAlloc); - return mlir::success(); - } -}; - -/** - * @brief Populates the given pattern set with the - * `ToQuantumComputationPattern`. - * - * @param patterns The pattern set to populate. - * @param circuit The quantum computation to create MLIR instructions from. - */ -void populateToQuantumComputationPatterns(mlir::RewritePatternSet& patterns, - qc::QuantumComputation& circuit) { - patterns.add(patterns.getContext(), circuit); -} - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/Architecture.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/Architecture.cpp deleted file mode 100644 index f3deb6a8e2..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/Architecture.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Architecture.h" - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -llvm::SmallVector> -Architecture::shortestSWAPsBetween(uint32_t u, uint32_t v) const { - if (u == v) { - return {}; - } - - if (prev_[u][v] == UINT64_MAX) { - throw std::domain_error("No path between qubits " + std::to_string(u) + - " and " + std::to_string(v)); - } - - llvm::SmallVector> swaps; - uint32_t last = v; - uint32_t curr = prev_[u][v]; - - while (curr != u) { - swaps.emplace_back(last, curr); // Insert SWAP(last, curr). - last = curr; - curr = prev_[u][curr]; - } - - return swaps; -} - -std::size_t Architecture::distanceBetween(uint32_t u, uint32_t v) const { - if (dist_[u][v] == UINT64_MAX) { - throw std::domain_error("No path between qubits " + std::to_string(u) + - " and " + std::to_string(v)); - } - return dist_[u][v]; -} - -void Architecture::floydWarshallWithPathReconstruction() { - for (const auto& [u, v] : couplingSet_) { - dist_[u][v] = 1; - prev_[u][v] = u; - } - for (std::size_t v = 0; v < nqubits(); ++v) { - dist_[v][v] = 0; - prev_[v][v] = v; - } - - for (std::size_t k = 0; k < nqubits(); ++k) { - for (std::size_t i = 0; i < nqubits(); ++i) { - for (std::size_t j = 0; j < nqubits(); ++j) { - if (dist_[i][k] == UINT64_MAX || dist_[k][j] == UINT64_MAX) { - continue; // avoid overflow with "infinite" distances - } - const std::size_t sum = dist_[i][k] + dist_[k][j]; - if (dist_[i][j] > sum) { - dist_[i][j] = sum; - prev_[i][j] = prev_[k][j]; - } - } - } - } -} - -void Architecture::collectNeighbours() { - for (const auto& [u, v] : couplingSet_) { - neighbours_[u].push_back(v); - } -} - -llvm::SmallVector Architecture::neighboursOf(uint32_t u) const { - return neighbours_[u]; -} - -bool Architecture::isExecutable(UnitaryInterface op, - const Layout& layout) const { - assert(isTwoQubitGate(op)); - const auto ins = getIns(op); - return areAdjacent(layout.lookupHardwareIndex(ins.first), - layout.lookupHardwareIndex(ins.second)); -} - -std::unique_ptr getArchitecture(const llvm::StringRef name) { - if (name == "MQTTest") { - static const Architecture::CouplingSet COUPLING{ - {0, 1}, {1, 0}, {0, 2}, {2, 0}, {1, 3}, {3, 1}, {2, 3}, - {3, 2}, {2, 4}, {4, 2}, {3, 5}, {5, 3}, {4, 5}, {5, 4}}; - - return std::make_unique("MQT-Test", 6, COUPLING); - } - - if (name == "IBMFalcon") { - static const Architecture::CouplingSet COUPLING{ - {0, 1}, {0, 14}, {1, 0}, {1, 2}, {2, 1}, {2, 3}, - {3, 2}, {3, 4}, {4, 3}, {4, 5}, {4, 15}, {5, 4}, - {5, 6}, {6, 5}, {6, 7}, {7, 6}, {7, 8}, {8, 7}, - {8, 16}, {9, 10}, {10, 9}, {10, 11}, {11, 10}, {11, 12}, - {12, 11}, {12, 13}, {12, 17}, {13, 12}, {14, 0}, {14, 18}, - {15, 4}, {15, 22}, {16, 8}, {16, 26}, {17, 12}, {17, 30}, - {18, 14}, {18, 19}, {19, 18}, {19, 20}, {20, 19}, {20, 21}, - {20, 33}, {21, 20}, {21, 22}, {22, 15}, {22, 21}, {22, 23}, - {23, 22}, {23, 24}, {24, 23}, {24, 25}, {24, 34}, {25, 24}, - {25, 26}, {26, 16}, {26, 25}, {26, 27}, {27, 26}, {27, 28}, - {28, 27}, {28, 29}, {28, 35}, {29, 28}, {29, 30}, {30, 17}, - {30, 29}, {30, 31}, {31, 30}, {31, 32}, {32, 31}, {32, 36}, - {33, 20}, {33, 39}, {34, 24}, {34, 43}, {35, 28}, {35, 47}, - {36, 32}, {36, 51}, {37, 38}, {37, 52}, {38, 37}, {38, 39}, - {39, 33}, {39, 38}, {39, 40}, {40, 39}, {40, 41}, {41, 40}, - {41, 42}, {41, 53}, {42, 41}, {42, 43}, {43, 34}, {43, 42}, - {43, 44}, {44, 43}, {44, 45}, {45, 44}, {45, 46}, {45, 54}, - {46, 45}, {46, 47}, {47, 35}, {47, 46}, {47, 48}, {48, 47}, - {48, 49}, {49, 48}, {49, 50}, {49, 55}, {50, 49}, {50, 51}, - {51, 36}, {51, 50}, {52, 37}, {52, 56}, {53, 41}, {53, 60}, - {54, 45}, {54, 64}, {55, 49}, {55, 68}, {56, 52}, {56, 57}, - {57, 56}, {57, 58}, {58, 57}, {58, 59}, {58, 71}, {59, 58}, - {59, 60}, {60, 53}, {60, 59}, {60, 61}, {61, 60}, {61, 62}, - {62, 61}, {62, 63}, {62, 72}, {63, 62}, {63, 64}, {64, 54}, - {64, 63}, {64, 65}, {65, 64}, {65, 66}, {66, 65}, {66, 67}, - {66, 73}, {67, 66}, {67, 68}, {68, 55}, {68, 67}, {68, 69}, - {69, 68}, {69, 70}, {70, 69}, {70, 74}, {71, 58}, {71, 77}, - {72, 62}, {72, 81}, {73, 66}, {73, 85}, {74, 70}, {74, 89}, - {75, 76}, {75, 90}, {76, 75}, {76, 77}, {77, 71}, {77, 76}, - {77, 78}, {78, 77}, {78, 79}, {79, 78}, {79, 80}, {79, 91}, - {80, 79}, {80, 81}, {81, 72}, {81, 80}, {81, 82}, {82, 81}, - {82, 83}, {83, 82}, {83, 84}, {83, 92}, {84, 83}, {84, 85}, - {85, 73}, {85, 84}, {85, 86}, {86, 85}, {86, 87}, {87, 86}, - {87, 88}, {87, 93}, {88, 87}, {88, 89}, {89, 74}, {89, 88}, - {90, 75}, {90, 94}, {91, 79}, {91, 98}, {92, 83}, {92, 102}, - {93, 87}, {93, 106}, {94, 90}, {94, 95}, {95, 94}, {95, 96}, - {96, 95}, {96, 97}, {96, 109}, {97, 96}, {97, 98}, {98, 91}, - {98, 97}, {98, 99}, {99, 98}, {99, 100}, {100, 99}, {100, 101}, - {100, 110}, {101, 100}, {101, 102}, {102, 92}, {102, 101}, {102, 103}, - {103, 102}, {103, 104}, {104, 103}, {104, 105}, {104, 111}, {105, 104}, - {105, 106}, {106, 93}, {106, 105}, {106, 107}, {107, 106}, {107, 108}, - {108, 107}, {108, 112}, {109, 96}, {110, 100}, {110, 118}, {111, 104}, - {111, 122}, {112, 108}, {112, 126}, {113, 114}, {114, 113}, {114, 115}, - {115, 114}, {115, 116}, {116, 115}, {116, 117}, {117, 116}, {117, 118}, - {118, 110}, {118, 117}, {118, 119}, {119, 118}, {119, 120}, {120, 119}, - {120, 121}, {121, 120}, {121, 122}, {122, 111}, {122, 121}, {122, 123}, - {123, 122}, {123, 124}, {124, 123}, {124, 125}, {125, 124}, {125, 126}, - {126, 112}, {126, 125}}; - - return std::make_unique("IBM-Falcon", 127, COUPLING); - } - - return nullptr; -} -}; // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/Common.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/Common.cpp deleted file mode 100644 index a2adc9d318..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/Common.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h" - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { -namespace { -/** - * @brief A function attribute that specifies an (QIR) entry point function. - */ -constexpr mlir::StringLiteral ENTRY_POINT_ATTR{"entry_point"}; - -/** - * @brief Attribute to forward function-level attributes to LLVM IR. - */ -constexpr mlir::StringLiteral PASSTHROUGH_ATTR{"passthrough"}; -} // namespace - -bool isEntryPoint(mlir::func::FuncOp op) { - const auto passthroughAttr = - op->getAttrOfType(PASSTHROUGH_ATTR); - if (!passthroughAttr) { - return false; - } - - return llvm::any_of(passthroughAttr, [](const mlir::Attribute attr) { - return mlir::isa(attr) && - mlir::cast(attr) == ENTRY_POINT_ATTR; - }); -} - -bool isTwoQubitGate(UnitaryInterface op) { - return (op.getInQubits().size() + op.getPosCtrlInQubits().size() + - op.getNegCtrlInQubits().size()) == 2; -} - -[[nodiscard]] ValuePair getIns(UnitaryInterface op) { - assert(isTwoQubitGate(op)); - const auto target = op.getInQubits(); - const auto targetSize = target.size(); - - if (targetSize == 2) { - return {target[0], target[1]}; - } - - const auto posCtrl = op.getPosCtrlInQubits(); - return (posCtrl.size() == 1) - ? std::pair{target[0], posCtrl[0]} - : std::pair{target[0], op.getNegCtrlInQubits()[0]}; -} - -[[nodiscard]] ValuePair getOuts(UnitaryInterface op) { - assert(isTwoQubitGate(op)); - const auto target = op.getOutQubits(); - const auto targetSize = target.size(); - - if (targetSize == 2) { - return {target[0], target[1]}; - } - - const auto posCtrl = op.getPosCtrlOutQubits(); - return (posCtrl.size() == 1) - ? std::pair{target[0], posCtrl[0]} - : std::pair{target[0], op.getNegCtrlOutQubits()[0]}; -} - -[[nodiscard]] mlir::Operation* getUserInRegion(mlir::Value v, - mlir::Region* region) { - for (mlir::Operation* user : v.getUsers()) { - if (user->getParentRegion() == region) { - return user; - } - } - return nullptr; -} - -[[nodiscard]] SWAPOp createSwap(mlir::Location location, mlir::Value in0, - mlir::Value in1, - mlir::PatternRewriter& rewriter) { - const mlir::SmallVector resultTypes{in0.getType(), in1.getType()}; - const mlir::SmallVector inQubits{in0, in1}; - - return rewriter.create( - /* location = */ location, - /* out_qubits = */ resultTypes, - /* pos_ctrl_out_qubits = */ mlir::TypeRange{}, - /* neg_ctrl_out_qubits = */ mlir::TypeRange{}, - /* static_params = */ nullptr, - /* params_mask = */ nullptr, - /* params = */ mlir::ValueRange{}, - /* in_qubits = */ inQubits, - /* pos_ctrl_in_qubits = */ mlir::ValueRange{}, - /* neg_ctrl_in_qubits = */ mlir::ValueRange{}); -} - -void replaceAllUsesInRegionAndChildrenExcept(mlir::Value oldValue, - mlir::Value newValue, - mlir::Region* region, - mlir::Operation* exceptOp, - mlir::PatternRewriter& rewriter) { - if (oldValue == newValue) { - return; - } - - rewriter.replaceUsesWithIf(oldValue, newValue, [&](mlir::OpOperand& use) { - mlir::Operation* user = use.getOwner(); - if (user == exceptOp) { - return false; - } - - // For other blocks, check if in region tree - mlir::Region* userRegion = user->getParentRegion(); - while (userRegion) { - if (userRegion == region) { - return true; - } - userRegion = userRegion->getParentRegion(); - } - return false; - }); -} -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp deleted file mode 100644 index 65825c8033..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.h" - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/IR/WireIterator.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Unit.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { -namespace { - -/// @brief A wire links a WireIterator to a program index. -struct Wire { - Wire(const WireIterator& it, uint32_t index) : it(it), index(index) {} - WireIterator it; - uint32_t index; -}; - -/// @brief Map to handle multi-qubit gates when traversing the def-use chain. -class SynchronizationMap { -public: - /// @returns true iff. the operation is contained in the map. - bool contains(mlir::Operation* op) const { return onHold.contains(op); } - - /// @brief Add op with respective wire and ref count to map. - void add(mlir::Operation* op, Wire wire, const std::size_t cnt) { - onHold.try_emplace(op, mlir::SmallVector{wire}); - /// 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> visit(mlir::Operation* op, - Wire wire) { - assert(refCount.contains(op) && "expected sync map to contain op"); - - /// Add iterator for later release. - onHold[op].push_back(wire); - - /// Release iterators whenever the ref count reaches zero. - if (--refCount[op] == 0) { - return onHold[op]; - } - - return std::nullopt; - } - -private: - /// @brief Maps operations to to-be-released iterators. - mlir::DenseMap> onHold; - /// @brief Maps operations to ref counts. - mlir::DenseMap refCount; -}; -} // namespace - -static mlir::SmallVector skipTwoQubitBlock(mlir::ArrayRef wires, - Layer& opLayer) { - assert(wires.size() == 2 && "expected two wires"); - - auto [it0, index0] = wires[0]; - auto [it1, index1] = wires[1]; - while (it0 != std::default_sentinel && it1 != std::default_sentinel) { - mlir::Operation* op0 = *it0; - if (!mlir::isa(op0) || mlir::isa(op0)) { - break; - } - - mlir::Operation* op1 = *it1; - if (!mlir::isa(op1) || mlir::isa(op1)) { - break; - } - - const UnitaryInterface u0 = cast(op0); - - /// Advance for single qubit gate on wire 0. - if (!isTwoQubitGate(u0)) { - opLayer.addOp(u0); - ++it0; - continue; - } - - const UnitaryInterface u1 = cast(op1); - - /// Advance for single qubit gate on wire 1. - if (!isTwoQubitGate(u1)) { - opLayer.addOp(u1); - ++it1; - continue; - } - - /// Stop if the wires reach different two qubit gates. - if (op0 != op1) { - break; - } - - /// Advance if u0 == u1. - opLayer.addOp(u1); - - ++it0; - ++it1; - } - - return {Wire(it0, index0), Wire(it1, index1)}; -} - -LayeredUnit LayeredUnit::fromEntryPointFunction(mlir::func::FuncOp func, - const std::size_t nqubits) { - Layout layout(nqubits); - for_each(func.getOps(), [&](QubitOp op) { - layout.add(op.getIndex(), op.getIndex(), op.getQubit()); - }); - return {std::move(layout), &func.getBody()}; -} - -LayeredUnit::LayeredUnit(Layout layout, mlir::Region* region) - : Unit(std::move(layout), region) { - SynchronizationMap sync; - - mlir::SmallVector curr; - mlir::SmallVector next; - curr.reserve(layout_.getNumQubits()); - next.reserve(layout_.getNumQubits()); - - for (const auto q : layout_.getHardwareQubits()) { - /// Increment the iterator here to skip the defining operation. - curr.emplace_back(++WireIterator(q, region_), - layout_.lookupProgramIndex(q)); - } - - while (true) { - - /// Advance each wire until (>=2)-qubit gates are found, collect the indices - /// of the respective two-qubit gates, and prepare iterators for next - /// iteration. - - Layer layer; - - bool haltOnWire{}; - - for (auto& [it, index] : curr) { - while (it != std::default_sentinel) { - haltOnWire = - mlir::TypeSwitch(*it) - .Case([&](UnitaryInterface op) { - const auto nins = op.getInQubits().size() + - op.getPosCtrlInQubits().size() + - op.getNegCtrlInQubits().size(); - - /// Skip over one-qubit gates. Note: Might be a BarrierOp. - if (nins == 1) { - layer.addOp(op); - ++it; - return false; - } - - /// Otherwise, add it to the sync map. - if (!sync.contains(op)) { - sync.add(op, Wire(++it, index), nins); - return true; - } - - if (const auto iterators = - sync.visit(op, Wire(++it, index))) { - layer.addOp(op); - - // Is ready two-qubit unitary? - if (!mlir::isa(op)) { - layer.twoQubitProgs.emplace_back((*iterators)[0].index, - (*iterators)[1].index); - next.append(skipTwoQubitBlock(*iterators, layer)); - } else { - next.append(*iterators); - } - } - - return true; - }) - .Case([&](auto op) { - layer.addOp(op); - ++it; - return false; - }) - .Case([&](mlir::scf::YieldOp yield) { - if (!sync.contains(yield)) { - sync.add(yield, Wire(++it, index), layout_.getNumQubits()); - return true; - } - - if (const auto iterators = - sync.visit(yield, Wire(++it, index))) { - layer.addOp(yield); - } - - return true; - }) - .Case( - [&](mlir::RegionBranchOpInterface op) { - if (!sync.contains(op)) { - sync.add(op, Wire(++it, index), layout_.getNumQubits()); - return true; - } - - if (const auto iterators = - sync.visit(op, Wire(++it, index))) { - divider_ = op; - } - return true; - }) - .Default([](auto) { - llvm_unreachable("unhandled operation"); - return true; - }); - - if (haltOnWire) { - break; - } - } - - if (divider_ != nullptr) { - break; - } - } - - if (!layer.hasZeroOps()) { - if (!layer.hasZero2QOps() || layers_.empty()) { - layers_.emplace_back(layer); - } else { - /// If there is no gates to route, merge into the previous layer. - layers_.back().ops.append(layer.ops); - if (layers_.back().anchor == nullptr) { - layers_.back().anchor = layer.anchor; - } - } - } - - /// Prepare next iteration or stop. - curr.swap(next); - next.clear(); - - if (curr.empty() || divider_ != nullptr) { - break; - } - }; -} - -mlir::SmallVector LayeredUnit::nextImpl() { - if (divider_ == nullptr) { - return {}; - } - - mlir::SmallVector units; - mlir::TypeSwitch(divider_) - .Case([&](mlir::scf::ForOp op) { - Layout forLayout(layout_); // Copy layout. - forLayout.remapToLoopBody(op); - layout_.remapToLoopResults(op); - units.emplace_back(std::move(layout_), region_); - units.emplace_back(std::move(forLayout), &op.getRegion()); - }) - .Case([&](mlir::scf::IfOp op) { - units.emplace_back(layout_, &op.getThenRegion()); - units.emplace_back(layout_, &op.getElseRegion()); - layout_.remapIfResults(op); - units.emplace_back(layout_, region_); - }) - .Default([](auto) { llvm_unreachable("invalid 'next' operation"); }); - - return units; -} - -#ifndef NDEBUG -LLVM_DUMP_METHOD void LayeredUnit::dump(llvm::raw_ostream& os) const { - os << "schedule: layers=\n"; - for (const auto& [i, layer] : llvm::enumerate(layers_)) { - os << '\t' << '[' << i << "]:\n"; - os << "\t #ops= " << layer.ops.size(); - if (!layer.ops.empty()) { - os << " anchor= " << layer.anchor->getLoc(); - } - os << '\n'; - os << "\t gates= "; - if (!layer.hasZero2QOps()) { - for (const auto& [prog0, prog1] : layer.twoQubitProgs) { - os << "(" << prog0 << "," << prog1 << "), "; - } - } else { - os << "(), "; - } - os << '\n'; - } - if (divider_ != nullptr) { - os << "schedule: followUp= " << divider_->getLoc() << '\n'; - } -} -#endif -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/SequentialUnit.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/SequentialUnit.cpp deleted file mode 100644 index 0f548c07b0..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/SequentialUnit.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/SequentialUnit.h" - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Unit.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::opt { - -SequentialUnit -SequentialUnit::fromEntryPointFunction(mlir::func::FuncOp func, - const std::size_t nqubits) { - Layout layout(nqubits); - for_each(func.getOps(), [&](QubitOp op) { - layout.add(op.getIndex(), op.getIndex(), op.getQubit()); - }); - return {std::move(layout), &func.getBody()}; -} - -SequentialUnit::SequentialUnit(Layout layout, mlir::Region* region, - mlir::Region::OpIterator start) - : Unit(std::move(layout), region), start_(start), end_(region->op_end()) { - mlir::Region::OpIterator it = start_; - for (; it != end_; ++it) { - mlir::Operation* op = &*it; - if (mlir::isa(op)) { - divider_ = op; - break; - } - } - end_ = it; -} - -mlir::SmallVector SequentialUnit::nextImpl() { - if (divider_ == nullptr) { - return {}; - } - - mlir::SmallVector units; - mlir::TypeSwitch(divider_) - .Case([&](mlir::scf::ForOp op) { - Layout forLayout(layout_); // Copy layout. - forLayout.remapToLoopBody(op); - layout_.remapToLoopResults(op); - units.emplace_back(std::move(layout_), region_, std::next(end_)); - units.emplace_back(std::move(forLayout), &op.getRegion()); - }) - .Case([&](mlir::scf::IfOp op) { - units.emplace_back(layout_, &op.getThenRegion()); - units.emplace_back(layout_, &op.getElseRegion()); - layout_.remapIfResults(op); - units.emplace_back(std::move(layout_), region_, std::next(end_)); - }) - .Default([](auto) { llvm_unreachable("invalid 'next' operation"); }); - - return units; -} -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/AStarRoutingPass.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/AStarRoutingPass.cpp deleted file mode 100644 index 7d247e7128..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/AStarRoutingPass.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Architecture.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Router.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEBUG_TYPE "route-astar-sc" - -namespace mqt::ir::opt { - -#define GEN_PASS_DEF_ASTARROUTINGPASSSC -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" - -namespace { -using namespace mlir; - -/** - * @brief Routes the program by dividing the circuit into layers of parallel - * two-qubit gates and iteratively searches and inserts SWAPs for each layer - * using A*-search. - */ -struct AStarRoutingPassSC final - : impl::AStarRoutingPassSCBase { - using AStarRoutingPassSCBase::AStarRoutingPassSCBase; - - void runOnOperation() override { - if (failed(preflight())) { - signalPassFailure(); - return; - } - - if (failed(route())) { - signalPassFailure(); - return; - } - } - -private: - /** - * @brief Route the given module for the targeted architecture using - * A*-search. Processes each entry_point function separately. - */ - LogicalResult route() { - ModuleOp module(getOperation()); - PatternRewriter rewriter(module->getContext()); - const AStarHeuristicRouter router( - HeuristicWeights(alpha, lambda, nlookahead)); - std::unique_ptr arch(getArchitecture(archName)); - - if (!arch) { - const Location loc = UnknownLoc::get(&getContext()); - emitError(loc) << "unsupported architecture '" << archName << "'"; - return failure(); - } - - for (auto func : module.getOps()) { - LLVM_DEBUG(llvm::dbgs() << "handleFunc: " << func.getSymName() << '\n'); - - if (!isEntryPoint(func)) { - LLVM_DEBUG(llvm::dbgs() << "\tskip non entry\n"); - continue; - } - - /// Iteratively process each unit in the function. - std::queue units; - units.emplace(LayeredUnit::fromEntryPointFunction(func, arch->nqubits())); - for (; !units.empty(); units.pop()) { - LayeredUnit& unit = units.front(); - LLVM_DEBUG(unit.dump()); - - SmallVector history; - for (const auto& [i, layer] : llvm::enumerate(unit)) { - - /// Compute sliding window. - const auto len = std::min(1 + nlookahead, unit.size() - i); - SmallVector> window; - window.reserve(len); - llvm::transform(ArrayRef(unit.begin(), unit.end()).slice(i, len), - std::back_inserter(window), [&](const Layer& l) { - return ArrayRef(l.twoQubitProgs); - }); - - /// Find and insert SWAPs. - rewriter.setInsertionPoint(layer.anchor); - const auto swaps = router.route(window, unit.layout(), *arch); - if (!swaps) { - const Location loc = UnknownLoc::get(&getContext()); - return emitError(loc, "A* failed to find a valid SWAP sequence"); - } - - if (!swaps->empty()) { - history.append(*swaps); - insertSWAPs(layer.anchor->getLoc(), *swaps, unit.layout(), - rewriter); - numSwaps += swaps->size(); - - LLVM_DEBUG({ - for (const auto [hw0, hw1] : *swaps) { - llvm::dbgs() - << llvm::format("route: swap= hw(%d, %d)\n", hw0, hw1); - } - }); - } - - /// Process all operations contained in the layer. - for (Operation* curr : layer.ops) { - rewriter.setInsertionPoint(curr); - - /// Re-order to fix any SSA Dominance issues. - if (i + 1 < unit.size()) { - rewriter.moveOpBefore(curr, unit[i + 1].anchor); - } - - /// Forward layout. - TypeSwitch(curr) - .Case([&](UnitaryInterface op) { - if (auto swap = dyn_cast(op.getOperation())) { - const auto in0 = swap.getInQubits()[0]; - const auto in1 = swap.getInQubits()[1]; - history.emplace_back( - unit.layout().lookupHardwareIndex(in0), - unit.layout().lookupHardwareIndex(in1)); - } - unit.layout().remap(op); - }) - .Case([&](ResetOp op) { unit.layout().remap(op); }) - .Case([&](MeasureOp op) { unit.layout().remap(op); }) - .Case([&](scf::YieldOp op) { - rewriter.setInsertionPoint(op); - insertSWAPs(op.getLoc(), llvm::reverse(history), - unit.layout(), rewriter); - }) - .Default([](auto) { - llvm_unreachable("unhandled 'curr' operation"); - }); - } - } - - for (const auto& next : unit.next()) { - units.emplace(next); - } - } - } - - return success(); - } - - LogicalResult preflight() { - if (archName.empty()) { - const Location loc = UnknownLoc::get(&getContext()); - return emitError(loc, "required option 'arch' not provided"); - } - - return success(); - } -}; - -} // namespace -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/NaiveRoutingPass.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/NaiveRoutingPass.cpp deleted file mode 100644 index ae747292bd..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/NaiveRoutingPass.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Architecture.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Router.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/SequentialUnit.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEBUG_TYPE "route-naive-sc" - -namespace mqt::ir::opt { - -#define GEN_PASS_DEF_NAIVEROUTINGPASSSC -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" - -namespace { -using namespace mlir; - -/** - * @brief Simple pre-order traversal of the IR that routes any non-executable - * gates by inserting SWAPs along the shortest path. - */ -struct NaiveRoutingPassSC final - : impl::NaiveRoutingPassSCBase { - using NaiveRoutingPassSCBase::NaiveRoutingPassSCBase; - - void runOnOperation() override { - if (failed(preflight())) { - signalPassFailure(); - return; - } - - if (failed(route())) { - signalPassFailure(); - return; - } - } - -private: - LogicalResult route() { - ModuleOp module(getOperation()); - PatternRewriter rewriter(module->getContext()); - std::unique_ptr arch(getArchitecture(archName)); - - if (!arch) { - const Location loc = UnknownLoc::get(&getContext()); - emitError(loc) << "unsupported architecture '" << archName << "'"; - return failure(); - } - - for (auto func : module.getOps()) { - LLVM_DEBUG(llvm::dbgs() << "handleFunc: " << func.getSymName() << '\n'); - - if (!isEntryPoint(func)) { - LLVM_DEBUG(llvm::dbgs() << "\tskip non entry\n"); - continue; - } - - /// Iteratively process each unit in the function. - std::queue units; - units.emplace( - SequentialUnit::fromEntryPointFunction(func, arch->nqubits())); - for (; !units.empty(); units.pop()) { - SequentialUnit& unit = units.front(); - - SmallVector history; - for (Operation& curr : unit) { - rewriter.setInsertionPoint(&curr); - - /// Forward layout. - TypeSwitch(&curr) - .Case([&](UnitaryInterface op) { - if (isTwoQubitGate(op)) { - if (!arch->isExecutable(op, unit.layout())) { - const auto ins = getIns(op); - const auto gate = std::make_pair( - unit.layout().lookupProgramIndex(ins.first), - unit.layout().lookupProgramIndex(ins.second)); - const auto swaps = - NaiveRouter::route(gate, unit.layout(), *arch); - if (!swaps.empty()) { - history.append(swaps); - insertSWAPs(op->getLoc(), swaps, unit.layout(), rewriter); - numSwaps += swaps.size(); - - LLVM_DEBUG({ - for (const auto [hw0, hw1] : swaps) { - llvm::dbgs() << llvm::format( - "route: swap= hw(%d, %d)\n", hw0, hw1); - } - }); - } - } - } - - if (auto swap = dyn_cast(op.getOperation())) { - const auto in0 = swap.getInQubits()[0]; - const auto in1 = swap.getInQubits()[1]; - history.emplace_back(unit.layout().lookupHardwareIndex(in0), - unit.layout().lookupHardwareIndex(in1)); - } - unit.layout().remap(op); - }) - .Case([&](ResetOp op) { unit.layout().remap(op); }) - .Case([&](MeasureOp op) { unit.layout().remap(op); }) - .Case([&](scf::YieldOp op) { - rewriter.setInsertionPoint(op); - insertSWAPs(op.getLoc(), llvm::reverse(history), unit.layout(), - rewriter); - }); - } - - for (const auto& next : unit.next()) { - units.emplace(next); - } - } - } - - return success(); - } - - LogicalResult preflight() { - if (archName.empty()) { - return emitError(UnknownLoc::get(&getContext()), - "required option 'arch' not provided"); - } - - return success(); - } -}; - -} // namespace -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/PlacementPass.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/PlacementPass.cpp deleted file mode 100644 index 5a47443386..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/PlacementPass.cpp +++ /dev/null @@ -1,500 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Architecture.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Stack.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEBUG_TYPE "placement-sc" - -namespace mqt::ir::opt { - -using namespace mlir; - -#define GEN_PASS_DEF_PLACEMENTPASSSC -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" - -namespace { - -/** - * @brief 'For' pushes once onto the stack, hence the parent is at depth one. - */ -constexpr std::size_t FOR_PARENT_DEPTH = 1UL; - -/** - * @brief 'If' pushes twice onto the stack, hence the parent is at depth two. - */ -constexpr std::size_t IF_PARENT_DEPTH = 2UL; - -/** - * @brief A queue of hardware indices. - */ -using HardwareIndexPool = std::deque; - -/** - * @brief A base class for all initial placement strategies. - */ -class InitialPlacer { -public: - InitialPlacer() = default; - InitialPlacer(const InitialPlacer&) = default; - InitialPlacer& operator=(const InitialPlacer&) = default; - InitialPlacer(InitialPlacer&&) noexcept = default; - InitialPlacer& operator=(InitialPlacer&&) noexcept = default; - virtual ~InitialPlacer() = default; - [[nodiscard]] virtual SmallVector operator()() = 0; -}; - -/** - * @brief Identity initial placement. - */ -class IdentityPlacer final : public InitialPlacer { -public: - explicit IdentityPlacer(const std::size_t nqubits) : nqubits_(nqubits) {} - - [[nodiscard]] SmallVector operator()() override { - SmallVector mapping(nqubits_); - std::iota(mapping.begin(), mapping.end(), 0); - return mapping; - } - -private: - std::size_t nqubits_; -}; - -/** - * @brief Random initial placement. - */ -class RandomPlacer final : public InitialPlacer { -public: - explicit RandomPlacer(const std::size_t nqubits, const std::mt19937_64& rng) - : nqubits_(nqubits), rng_(rng) {} - - [[nodiscard]] SmallVector operator()() override { - SmallVector mapping(nqubits_); - std::iota(mapping.begin(), mapping.end(), 0); - std::ranges::shuffle(mapping, rng_); - return mapping; - } - -private: - std::size_t nqubits_; - std::mt19937_64 rng_; -}; - -/** - * @brief The necessary datastructures to apply the placement. - */ -struct PlacementContext { - explicit PlacementContext(Architecture& arch, InitialPlacer& placer) - : arch(&arch), placer(&placer) {} - - Architecture* arch; - InitialPlacer* placer; - HardwareIndexPool pool; - LayoutStack stack{}; -}; - -} // namespace - -/** - * @brief Adds the necessary hardware qubits for entry_point functions and - * prepares the stack for the qubit placement in the function body. - */ -static WalkResult handleFunc(func::FuncOp op, PlacementContext& ctx, - PatternRewriter& rewriter) { - assert(ctx.stack.empty() && "handleFunc: stack must be empty"); - - rewriter.setInsertionPointToStart(&op.getBody().front()); - LLVM_DEBUG({ - llvm::dbgs() << "handleFunc: entry_point= " << op.getSymName() << '\n'; - }); - - ctx.stack.emplace(ctx.arch->nqubits()); - ctx.pool.clear(); - - /// Create static / hardware qubits for entry_point functions. - SmallVector qubits(ctx.arch->nqubits()); - for (uint32_t i = 0; i < ctx.arch->nqubits(); ++i) { - auto qubitOp = - rewriter.create(rewriter.getInsertionPoint()->getLoc(), i); - rewriter.setInsertionPointAfter(qubitOp); - qubits[i] = qubitOp.getQubit(); - } - - /// Initialize pool with qubits in the initial layout order. - /// Initialize SSA Value <-> Hardware-Index Mapping. - for (const auto layout = (*ctx.placer)(); - const auto [programIdx, hardwareIdx] : llvm::enumerate(layout)) { - const Value q = qubits[hardwareIdx]; - ctx.pool.push_back(hardwareIdx); - ctx.stack.top().add(programIdx, hardwareIdx, q); - } - - return WalkResult::advance(); -} - -/** - * @brief Indicates the end of a region defined by a function. Consequently, pop - * the region's state from the stack. - */ -static WalkResult handleReturn(PlacementContext& ctx) { - ctx.stack.pop(); - return WalkResult::advance(); -} - -/** - * @brief Replaces the 'for' loop with one that has all hardware qubits as init - * arguments. - * - * Prepares the stack for the placement of the loop body by adding a copy of the - * current state to the stack. Forwards the results in the parent state. - */ -static WalkResult handleFor(scf::ForOp op, PlacementContext& ctx, - PatternRewriter& rewriter) { - const std::size_t nargs = op.getBody()->getNumArguments(); - const std::size_t nresults = op->getNumResults(); - - /// Construct new init arguments. - const ArrayRef qubits = ctx.stack.top().getHardwareQubits(); - const SmallVector newInitArgs(qubits); - - /// Replace old for with a new one with updated init arguments. - auto forOp = rewriter.create(op.getLoc(), op.getLowerBound(), - op.getUpperBound(), op.getStep(), - newInitArgs); - - rewriter.mergeBlocks(op.getBody(), forOp.getBody(), - forOp.getBody()->getArguments().take_front(nargs)); - - if (nresults > 0) { - rewriter.replaceOp(op, forOp.getResults().take_front(nresults)); - } else { - rewriter.eraseOp(op); - } - - /// Prepare stack. - ctx.stack.duplicateTop(); - - // Forward out-of-loop and in-loop state. - for (const auto [arg, res, iter] : - llvm::zip(forOp.getInitArgs(), forOp.getResults(), - forOp.getRegionIterArgs())) { - if (isa(arg.getType())) { - ctx.stack.getItemAtDepth(FOR_PARENT_DEPTH).remapQubitValue(arg, res); - ctx.stack.top().remapQubitValue(arg, iter); - } - } - - return WalkResult::advance(); -} - -/** - * @brief Replaces the 'if' statement with one that has all hardware qubits as - * result. - * - * Prepares the stack for the placement of the 'then' and 'else' body by adding - * a copy of the current state to the stack for each branch. Forwards the - * results in the parent state. - */ -static WalkResult handleIf(scf::IfOp op, PlacementContext& ctx, - PatternRewriter& rewriter) { - const std::size_t nresults = op->getNumResults(); - - /// Construct new result types. - const ArrayRef qubits = ctx.stack.top().getHardwareQubits(); - const auto rng = llvm::map_range(qubits, [](Value q) { return q.getType(); }); - const SmallVector resultTypes(rng.begin(), rng.end()); - - /// Replace old if with a new one with updated result types. - const bool hasElse = !op.getElseRegion().empty(); - auto ifOp = rewriter.create(op.getLoc(), resultTypes, - op.getCondition(), hasElse); - - rewriter.mergeBlocks(&op.getThenRegion().front(), - &ifOp.getThenRegion().front()); - if (hasElse) { - rewriter.mergeBlocks(&op.getElseRegion().front(), - &ifOp.getElseRegion().front()); - } - - if (nresults > 0) { - rewriter.replaceOp(op, ifOp.getResults().take_front(nresults)); - } else { - rewriter.eraseOp(op); - } - - /// Prepare stack. - ctx.stack.duplicateTop(); // Else - ctx.stack.duplicateTop(); // Then - - /// Forward results for all hardware qubits. - Layout& layoutBeforeIf = ctx.stack.getItemAtDepth(IF_PARENT_DEPTH); - for (std::size_t i = 0; i < qubits.size(); ++i) { - const Value in = layoutBeforeIf.getHardwareQubits()[i]; - const Value out = ifOp->getResult(i); - layoutBeforeIf.remapQubitValue(in, out); - } - - return WalkResult::advance(); -} - -/** - * @brief Indicates the end of a region defined by a branching op. Consequently, - * we pop the region's state from the stack. - */ -static WalkResult handleYield(scf::YieldOp op, PlacementContext& ctx, - PatternRewriter& rewriter) { - if (!isa(op->getParentOp()) && - !isa(op->getParentOp())) { - return WalkResult::skip(); - } - - rewriter.replaceOpWithNewOp( - op, ctx.stack.top().getHardwareQubits()); - - ctx.stack.pop(); - - return WalkResult::advance(); -} - -/** - * @brief Retrieve free qubit from pool and replace the allocated qubit with it. - * Reset the qubit if it has already been allocated before. - */ -static WalkResult handleAlloc(AllocQubitOp op, PlacementContext& ctx, - PatternRewriter& rewriter) { - if (ctx.pool.empty()) { - return op.emitOpError( - "requires one too many qubits for the targeted architecture"); - } - - /// Retrieve free qubit. - const std::size_t index = ctx.pool.front(); - ctx.pool.pop_front(); - - LLVM_DEBUG({ llvm::dbgs() << "handleAlloc: index= " << index << '\n'; }); - - const Value q = ctx.stack.top().lookupHardwareValue(index); - - /// Newly allocated? - const Operation* defOp = q.getDefiningOp(); - if (defOp != nullptr && isa(defOp)) { - rewriter.replaceOp(op, q); - return WalkResult::advance(); - } - - auto reset = rewriter.create(op.getLoc(), q); - rewriter.replaceOp(op, reset); - - /// Update layout. - ctx.stack.top().remapQubitValue(reset.getInQubit(), reset.getOutQubit()); - - return WalkResult::advance(); -} - -/** - * @brief Release hardware qubit and erase dealloc operation. - */ -static WalkResult handleDealloc(DeallocQubitOp op, PlacementContext& ctx, - PatternRewriter& rewriter) { - const std::size_t index = ctx.stack.top().lookupHardwareIndex(op.getQubit()); - ctx.pool.push_back(index); - rewriter.eraseOp(op); - return WalkResult::advance(); -} - -/** - * @brief Update layout. - */ -static WalkResult handleReset(ResetOp op, PlacementContext& ctx) { - ctx.stack.top().remapQubitValue(op.getInQubit(), op.getOutQubit()); - return WalkResult::advance(); -} - -/** - * @brief Update layout. - */ -static WalkResult handleMeasure(MeasureOp op, PlacementContext& ctx) { - ctx.stack.top().remapQubitValue(op.getInQubit(), op.getOutQubit()); - return WalkResult::advance(); -} - -/** - * @brief Update layout. - */ -static WalkResult handleUnitary(UnitaryInterface op, PlacementContext& ctx) { - for (const auto [in, out] : - llvm::zip(op.getAllInQubits(), op.getAllOutQubits())) { - ctx.stack.top().remapQubitValue(in, out); - } - - return WalkResult::advance(); -} - -static LogicalResult run(ModuleOp module, MLIRContext* mlirCtx, - PlacementContext& ctx) { - PatternRewriter rewriter(mlirCtx); - - /// Prepare work-list. - SmallVector worklist; - for (const auto func : module.getOps()) { - if (!isEntryPoint(func)) { - continue; // Ignore non entry_point functions for now. - } - func->walk( - [&](Operation* op) { worklist.push_back(op); }); - } - - /// Iterate work-list. - for (Operation* curr : worklist) { - if (curr == nullptr) { - continue; // Skip erased ops. - } - - const OpBuilder::InsertionGuard guard(rewriter); - rewriter.setInsertionPoint(curr); - - /// TypeSwitch performs sequential dyn_cast checks. - /// Hence, always put most frequent ops first. - - const auto res = - TypeSwitch(curr) - /// mqtopt Dialect - .Case( - [&](UnitaryInterface op) { return handleUnitary(op, ctx); }) - .Case( - [&](AllocQubitOp op) { return handleAlloc(op, ctx, rewriter); }) - .Case([&](DeallocQubitOp op) { - return handleDealloc(op, ctx, rewriter); - }) - .Case([&](ResetOp op) { return handleReset(op, ctx); }) - .Case( - [&](MeasureOp op) { return handleMeasure(op, ctx); }) - /// built-in Dialect - .Case( - [&](ModuleOp /* op */) { return WalkResult::advance(); }) - /// func Dialect - .Case( - [&](func::FuncOp op) { return handleFunc(op, ctx, rewriter); }) - .Case( - [&](func::ReturnOp /* op */) { return handleReturn(ctx); }) - /// scf Dialect - .Case( - [&](scf::ForOp op) { return handleFor(op, ctx, rewriter); }) - .Case( - [&](scf::IfOp op) { return handleIf(op, ctx, rewriter); }) - .Case( - [&](scf::YieldOp op) { return handleYield(op, ctx, rewriter); }) - /// Skip the rest. - .Default([](auto) { return WalkResult::skip(); }); - - if (res.wasInterrupted()) { - return failure(); - } - } - - return success(); -} - -namespace { - -/** - * @brief This pass maps program qubits to hardware qubits on superconducting - * quantum devices using initial placement strategies. - */ -struct PlacementPassSC final : impl::PlacementPassSCBase { - using PlacementPassSCBase::PlacementPassSCBase; - - void runOnOperation() override { - if (preflight().failed()) { - signalPassFailure(); - return; - } - - const auto arch = getArchitecture(archName); - if (!arch) { - emitError(UnknownLoc::get(&getContext())) - << "unsupported architecture '" << archName << "'"; - signalPassFailure(); - return; - } - - const auto placer = getPlacer(*arch); - - if (PlacementContext ctx(*arch, *placer); - failed(run(getOperation(), &getContext(), ctx))) { - signalPassFailure(); - } - } - -private: - [[nodiscard]] std::unique_ptr - getPlacer(const Architecture& arch) const { - switch (static_cast(strategy)) { - case PlacementStrategy::Identity: - LLVM_DEBUG({ llvm::dbgs() << "getPlacer: identity placement\n"; }); - return std::make_unique(arch.nqubits()); - case PlacementStrategy::Random: - std::random_device rd; - const std::size_t seed = rd(); - LLVM_DEBUG({ - llvm::dbgs() << "getPlacer: random placement with seed =" << seed - << '\n'; - }); - return std::make_unique(arch.nqubits(), - std::mt19937_64(seed)); - } - llvm_unreachable("Unknown strategy"); - } - - LogicalResult preflight() { - if (archName.empty()) { - return emitError(UnknownLoc::get(&getContext()), - "required option 'arch' not provided"); - } - - return success(); - } -}; - -} // namespace - -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/RoutingVerificationPass.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/RoutingVerificationPass.cpp deleted file mode 100644 index b85e47a80a..0000000000 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/RoutingVerificationPass.cpp +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Architecture.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h" -#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/SequentialUnit.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEBUG_TYPE "routing-verification-sc" - -namespace mqt::ir::opt { - -#define GEN_PASS_DEF_ROUTINGVERIFICATIONSCPASS -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" - -namespace { -using namespace mlir; - -/** - * @brief This pass verifies that all two-qubit gates are executable on the - * target architecture. - */ -struct RoutingVerificationPassSC final - : impl::RoutingVerificationSCPassBase { - using RoutingVerificationSCPassBase< - RoutingVerificationPassSC>::RoutingVerificationSCPassBase; - - void runOnOperation() override { - if (failed(preflight())) { - signalPassFailure(); - return; - } - - if (failed(verify())) { - signalPassFailure(); - return; - } - } - -private: - LogicalResult verify() { - ModuleOp module(getOperation()); - std::unique_ptr arch(getArchitecture(archName)); - - if (!arch) { - const Location loc = UnknownLoc::get(&getContext()); - emitError(loc) << "unsupported architecture '" << archName << "'"; - return failure(); - } - - for (auto func : module.getOps()) { - LLVM_DEBUG(llvm::dbgs() << "handleFunc: " << func.getSymName() << '\n'); - - if (!isEntryPoint(func)) { - LLVM_DEBUG(llvm::dbgs() << "\tskip non entry\n"); - continue; - } - - /// Iteratively process each unit in the function. - std::queue units; - units.emplace( - SequentialUnit::fromEntryPointFunction(func, arch->nqubits())); - for (; !units.empty(); units.pop()) { - SequentialUnit& unit = units.front(); - - Layout unmodified(unit.layout()); - for (const Operation& curr : unit) { - const auto res = - TypeSwitch(&curr) - .Case([&](UnitaryInterface op) - -> LogicalResult { - if (isTwoQubitGate(op)) { - /// Verify that the two-qubit gate is executable. - if (!arch->isExecutable(op, unit.layout())) { - const auto ins = getIns(op); - const auto hw0 = - unit.layout().lookupHardwareIndex(ins.first); - const auto hw1 = - unit.layout().lookupHardwareIndex(ins.second); - - return op->emitOpError() - << "(" << hw0 << "," << hw1 << ")" - << " is not executable on target architecture '" - << arch->name() << "'"; - } - } - - unit.layout().remap(op); - return success(); - }) - .Case([&](ResetOp op) { - unit.layout().remap(op); - return success(); - }) - .Case([&](MeasureOp op) { - unit.layout().remap(op); - return success(); - }) - .Case([&](scf::YieldOp op) -> LogicalResult { - /// Verify that the layouts match at the end. - const auto mappingBefore = unmodified.getCurrentLayout(); - const auto mappingNow = unit.layout().getCurrentLayout(); - if (llvm::equal(mappingBefore, mappingNow)) { - return success(); - } - - return op.emitOpError() - << "layouts must match after restoration"; - }) - .Default([](auto) { return success(); }); - - if (failed(res)) { - return res; - } - } - - for (const auto& next : unit.next()) { - units.emplace(next); - } - } - } - - return success(); - } - - LogicalResult preflight() { - if (archName.empty()) { - return emitError(UnknownLoc::get(&getContext()), - "required option 'arch' not provided"); - } - - return success(); - } -}; -} // namespace -} // namespace mqt::ir::opt diff --git a/mlir/lib/Dialect/MQTRef/CMakeLists.txt b/mlir/lib/Dialect/MQTRef/CMakeLists.txt deleted file mode 100644 index c98100e703..0000000000 --- a/mlir/lib/Dialect/MQTRef/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -add_subdirectory(IR) -add_subdirectory(Translation) diff --git a/mlir/lib/Dialect/MQTRef/IR/CMakeLists.txt b/mlir/lib/Dialect/MQTRef/IR/CMakeLists.txt deleted file mode 100644 index 43efa049b5..0000000000 --- a/mlir/lib/Dialect/MQTRef/IR/CMakeLists.txt +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -add_mlir_dialect_library( - MLIRMQTRef - MQTRefOps.cpp - DEPENDS - MLIRMQTRefOpsIncGen - MLIRMQTRefInterfacesIncGen - LINK_LIBS - MLIRIR - MLIRInferTypeOpInterface - DISABLE_INSTALL) - -# collect header files -file(GLOB_RECURSE IR_HEADERS_SOURCE "${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Dialect/MQTRef/IR/*.h") -file(GLOB_RECURSE IR_HEADERS_BUILD "${MQT_MLIR_BUILD_INCLUDE_DIR}/mlir/Dialect/MQTRef/IR/*.inc") - -# add public headers using file sets -target_sources( - MLIRMQTRef - PUBLIC FILE_SET - HEADERS - BASE_DIRS - ${MQT_MLIR_SOURCE_INCLUDE_DIR} - FILES - ${IR_HEADERS_SOURCE} - FILE_SET - HEADERS - BASE_DIRS - ${MQT_MLIR_BUILD_INCLUDE_DIR} - FILES - ${IR_HEADERS_BUILD}) diff --git a/mlir/lib/Dialect/MQTRef/IR/MQTRefOps.cpp b/mlir/lib/Dialect/MQTRef/IR/MQTRefOps.cpp deleted file mode 100644 index 3f1037b832..0000000000 --- a/mlir/lib/Dialect/MQTRef/IR/MQTRefOps.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTRef/IR/MQTRefDialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -// The following headers are needed for some template instantiations. -// IWYU pragma: begin_keep -#include -#include -#include -#include -// IWYU pragma: end_keep - -using namespace mqt::ir::common; - -//===----------------------------------------------------------------------===// -// Dialect -//===----------------------------------------------------------------------===// - -#include "mlir/Dialect/MQTRef/IR/MQTRefOpsDialect.cpp.inc" - -void mqt::ir::ref::MQTRefDialect::initialize() { - addTypes< -#define GET_TYPEDEF_LIST -#include "mlir/Dialect/MQTRef/IR/MQTRefOpsTypes.cpp.inc" - >(); - - addOperations< -#define GET_OP_LIST -#include "mlir/Dialect/MQTRef/IR/MQTRefOps.cpp.inc" - >(); -} - -//===----------------------------------------------------------------------===// -// Types -//===----------------------------------------------------------------------===// - -#define GET_TYPEDEF_CLASSES -#include "mlir/Dialect/MQTRef/IR/MQTRefOpsTypes.cpp.inc" - -//===----------------------------------------------------------------------===// -// Interfaces -//===----------------------------------------------------------------------===// - -#include "mlir/Dialect/MQTRef/IR/MQTRefInterfaces.cpp.inc" - -//===----------------------------------------------------------------------===// -// Operations -//===----------------------------------------------------------------------===// - -#define GET_OP_CLASSES -#include "mlir/Dialect/MQTRef/IR/MQTRefOps.cpp.inc" - -namespace mqt::ir::ref { -mlir::ParseResult parseRefParams( - mlir::OpAsmParser& parser, - llvm::SmallVectorImpl& params, - mlir::Attribute& staticParams, mlir::Attribute& paramsMask) { - if (mlir::OpAsmParser::UnresolvedOperand operand; - parser.parseOptionalOperand(operand).has_value()) { - params.push_back(operand); - while (parser.parseOptionalComma().succeeded()) { - if (parser.parseOperand(operand).failed()) { - return mlir::failure(); - } - params.push_back(operand); - } - } - - if (parser.parseOptionalKeyword("static").succeeded()) { - staticParams = mlir::DenseF64ArrayAttr::parse(parser, mlir::Type{}); - } - - if (parser.parseOptionalKeyword("mask").succeeded()) { - paramsMask = mlir::DenseBoolArrayAttr::parse(parser, mlir::Type{}); - } - - return mlir::success(); -} - -void printRefParams(mlir::OpAsmPrinter& printer, mlir::Operation* /*op*/, - mlir::ValueRange params, - mlir::DenseF64ArrayAttr staticParams, - mlir::DenseBoolArrayAttr paramsMask) { - auto needSpace = false; - if (!params.empty()) { - printer << params; - needSpace = true; - } - - if (staticParams) { - if (needSpace) { - printer << " "; - } - std::string staticStr; - llvm::raw_string_ostream ostream(staticStr); - staticParams.print(ostream); - printer << "static " << ostream.str(); - needSpace = true; - } - - if (paramsMask) { - if (needSpace) { - printer << " "; - } - std::string maskStr; - llvm::raw_string_ostream ostream(maskStr); - paramsMask.print(ostream); - printer << "mask " << ostream.str(); - } -} -} // namespace mqt::ir::ref diff --git a/mlir/lib/Dialect/MQTRef/Translation/CMakeLists.txt b/mlir/lib/Dialect/MQTRef/Translation/CMakeLists.txt deleted file mode 100644 index 758809b532..0000000000 --- a/mlir/lib/Dialect/MQTRef/Translation/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -add_mlir_library( - MLIRMQTRefTranslation - ImportQuantumComputation.cpp - LINK_LIBS - MLIRArithDialect - MLIRFuncDialect - MLIRMemRefDialect - MLIRSCFDialect - MLIRMQTRef - MQT::CoreIR - DISABLE_INSTALL) - -# collect header files -file(GLOB_RECURSE TRANSLATION_HEADERS_SOURCE - ${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Dialect/MQTRef/Translation/*.h) - -# add public headers using file sets -target_sources( - MLIRMQTRefTranslation PUBLIC FILE_SET HEADERS BASE_DIRS ${MQT_MLIR_SOURCE_INCLUDE_DIR} FILES - ${TRANSLATION_HEADERS_SOURCE}) diff --git a/mlir/lib/Dialect/MQTRef/Translation/ImportQuantumComputation.cpp b/mlir/lib/Dialect/MQTRef/Translation/ImportQuantumComputation.cpp deleted file mode 100644 index 0dd71c47e4..0000000000 --- a/mlir/lib/Dialect/MQTRef/Translation/ImportQuantumComputation.cpp +++ /dev/null @@ -1,660 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTRef/Translation/ImportQuantumComputation.h" - -#include "ir/QuantumComputation.hpp" -#include "ir/Register.hpp" -#include "ir/operations/CompoundOperation.hpp" -#include "ir/operations/Control.hpp" -#include "ir/operations/IfElseOperation.hpp" -#include "ir/operations/NonUnitaryOperation.hpp" -#include "ir/operations/OpType.hpp" -#include "ir/operations/Operation.hpp" -#include "mlir/Dialect/MQTRef/IR/MQTRefDialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace { - -struct QregInfo { - const qc::QuantumRegister* qregPtr; - mlir::Value qreg; - llvm::SmallVector qubits; -}; - -using BitMemInfo = std::pair; // (memref, localIdx) -using BitIndexVec = llvm::SmallVector; - -} // namespace - -/** - * @brief Allocates a quantum register in the MLIR module. - * - * @param builder The MLIR OpBuilder used to create operations - * @param context The MLIR context in which types are created - * @param numQubits The number of qubits to allocate in the register - * @return mlir::Value The allocated quantum register value - */ -static mlir::Value allocateQreg(mlir::OpBuilder& builder, - mlir::MLIRContext* context, - const std::size_t numQubits) { - const auto& qubitType = mqt::ir::ref::QubitType::get(context); - auto memRefType = - mlir::MemRefType::get({static_cast(numQubits)}, qubitType); - auto memref = builder.create(builder.getUnknownLoc(), - memRefType); - return memref.getResult(); -} - -/** - * @brief Extracts all qubits from a quantum register. - * - * @param builder The MLIR OpBuilder used to create operations - * @param qreg The quantum register from which to extract qubits - * @param numQubits The number of qubits to extract - * @return llvm::SmallVector Vector of extracted qubit values - */ -static llvm::SmallVector -extractQubits(mlir::OpBuilder& builder, mlir::Value qreg, - const std::size_t numQubits) { - llvm::SmallVector qubits; - qubits.reserve(numQubits); - - for (std::size_t qubit = 0; qubit < numQubits; ++qubit) { - auto index = builder.create( - builder.getUnknownLoc(), qubit); - qubits.emplace_back( - builder - .create(builder.getUnknownLoc(), qreg, - mlir::ValueRange{index}) - .getResult()); - } - - return qubits; -} - -/** - * @brief Allocates quantum registers and extracts qubits. - * - * @param builder The MLIR OpBuilder used to create operations - * @param context The MLIR context in which types are created - * @param quantumComputation The quantum computation to translate - * @return llvm::SmallVector Vector containing information about all - * quantum registers - */ -static llvm::SmallVector -getQregs(mlir::OpBuilder& builder, mlir::MLIRContext* context, - const qc::QuantumComputation& quantumComputation) { - // Build list of pointers for sorting - llvm::SmallVector qregPtrs; - qregPtrs.reserve(quantumComputation.getQuantumRegisters().size() + - quantumComputation.getAncillaRegisters().size()); - for (const auto& qreg : - quantumComputation.getQuantumRegisters() | std::views::values) { - qregPtrs.emplace_back(&qreg); - } - for (const auto& qreg : - quantumComputation.getAncillaRegisters() | std::views::values) { - qregPtrs.emplace_back(&qreg); - } - - // Sort by start index - std::ranges::sort( - qregPtrs, [](const qc::QuantumRegister* a, const qc::QuantumRegister* b) { - return a->getStartIndex() < b->getStartIndex(); - }); - - // Allocate quantum registers and extract qubits - llvm::SmallVector qregs; - for (const auto* qregPtr : qregPtrs) { - const auto qreg = allocateQreg(builder, context, qregPtr->getSize()); - auto qubits = extractQubits(builder, qreg, qregPtr->getSize()); - qregs.emplace_back(qregPtr, qreg, std::move(qubits)); - } - - return qregs; -} - -/** - * @brief Builds a mapping from global qubit index to extracted qubit value. - * - * @param builder The MLIR OpBuilder used to create operations - * @param context The MLIR context in which types are created - * @param qregs Vector containing information about all quantum registers - * @return llvm::SmallVector Sorted vector of qubit values - */ -static llvm::SmallVector -getQubits(const qc::QuantumComputation& quantumComputation, - llvm::SmallVector& qregs) { - llvm::SmallVector flatQubits; - const auto maxPhys = quantumComputation.getHighestPhysicalQubitIndex(); - flatQubits.resize(static_cast(maxPhys) + 1); - for (const auto& qreg : qregs) { - for (std::size_t i = 0; i < qreg.qregPtr->getSize(); ++i) { - const auto globalIdx = - static_cast(qreg.qregPtr->getStartIndex() + i); - flatQubits[globalIdx] = qreg.qubits[i]; - } - } - - return flatQubits; -} - -/** - * @brief Deallocates the quantum register in the MLIR module. - * - * @param builder The MLIR OpBuilder used to create operations - * @param qreg The quantum register to deallocate - */ -static void deallocateQreg(mlir::OpBuilder& builder, mlir::Value qreg) { - builder.create(builder.getUnknownLoc(), qreg); -} - -/** - * @brief Allocates a classical register in the MLIR module. - * - * @param builder The MLIR OpBuilder used to create operations - * @param numBits The number of bits to allocate in the register - * @return mlir::Value The allocated classical register value - */ -static mlir::Value allocateBits(mlir::OpBuilder& builder, int64_t numBits) { - auto memRefType = mlir::MemRefType::get({numBits}, builder.getI1Type()); - auto memref = builder.create(builder.getUnknownLoc(), - memRefType); - return memref.getResult(); -} - -/** - * @brief Builds a mapping from global bit index to (memref, localIdx). - * - * @param builder The MLIR OpBuilder used to create operations - * @param numBits The number of bits to allocate in the register - * @return mlir::Value The allocated classical register value - */ -static BitIndexVec getBitMap(mlir::OpBuilder& builder, - const qc::QuantumComputation& quantumComputation) { - // Build list of pointers for sorting - llvm::SmallVector cregPtrs; - cregPtrs.reserve(quantumComputation.getClassicalRegisters().size()); - for (const auto& [_, reg] : quantumComputation.getClassicalRegisters()) { - cregPtrs.emplace_back(®); - } - - // Sort by start index - std::ranges::sort(cregPtrs, [](const qc::ClassicalRegister* a, - const qc::ClassicalRegister* b) { - return a->getStartIndex() < b->getStartIndex(); - }); - - // Build mapping - BitIndexVec bitMap; - bitMap.resize(quantumComputation.getNcbits()); - for (const auto* reg : cregPtrs) { - auto mem = allocateBits(builder, static_cast(reg->getSize())); - for (std::size_t i = 0; i < reg->getSize(); ++i) { - const auto globalIdx = static_cast(reg->getStartIndex() + i); - bitMap[globalIdx] = {mem, i}; - } - } - - return bitMap; -} - -/** - * @brief Adds a single quantum operation to the MLIR module. - * - * @tparam OpType The type of the operation to create - * @param builder The MLIR OpBuilder - * @param operation The quantum operation to add - * @param qubits The qubits of the quantum register - */ -template -static void addUnitaryOp(mlir::OpBuilder& builder, - const qc::Operation& operation, - const llvm::SmallVector& qubits) { - // Define operation parameters - mlir::DenseF64ArrayAttr staticParamsAttr = nullptr; - if (const auto& parameters = operation.getParameter(); !parameters.empty()) { - staticParamsAttr = builder.getDenseF64ArrayAttr(parameters); - } - - // Define input qubits - const auto& targets = operation.getTargets(); - llvm::SmallVector inQubits; - inQubits.reserve(targets.size()); - for (const auto& t : targets) { - inQubits.emplace_back(qubits[t]); - } - - // Define control qubits - llvm::SmallVector posCtrlQubits; - llvm::SmallVector negCtrlQubits; - if (const auto& controls = operation.getControls(); !controls.empty()) { - posCtrlQubits.reserve(controls.size()); - negCtrlQubits.reserve(controls.size()); - for (const auto& [qubit, type] : controls) { - if (type == qc::Control::Type::Pos) { - posCtrlQubits.emplace_back(qubits[qubit]); - } else if (type == qc::Control::Type::Neg) { - negCtrlQubits.emplace_back(qubits[qubit]); - } - } - } - - // Create operation - builder.create(builder.getUnknownLoc(), staticParamsAttr, nullptr, - mlir::ValueRange{}, inQubits, posCtrlQubits, - negCtrlQubits); -} - -/** - * @brief Adds a measure operation to the MLIR module. - * - * @param builder The MLIR OpBuilder - * @param operation The measure operation to add - * @param qubits The qubits of the quantum register - * @param bitMap The mapping from global classical bit index to (memref, - * localIdx) - */ -static void addMeasureOp(mlir::OpBuilder& builder, - const qc::Operation& operation, - const llvm::SmallVector& qubits, - const BitIndexVec& bitMap) { - const auto& measureOp = - dynamic_cast(operation); - const auto& targets = measureOp.getTargets(); - const auto& classics = measureOp.getClassics(); - for (std::size_t i = 0; i < targets.size(); ++i) { - const auto& qubit = qubits[targets[i]]; - auto result = - builder.create(builder.getUnknownLoc(), qubit); - const auto bitIdx = static_cast(classics[i]); - assert(bitIdx < bitMap.size() && "Classical bit index out of range"); - const auto& [mem, localIdx] = bitMap[bitIdx]; - auto idxVal = builder.create( - builder.getUnknownLoc(), static_cast(localIdx)); - builder.create(builder.getUnknownLoc(), result, mem, - mlir::ValueRange{idxVal}); - } -} - -/** - * @brief Adds a reset operation to the MLIR module. - * - * @param builder The MLIR OpBuilder - * @param operation The reset operation to add - * @param qubits The qubits of the quantum register - */ -static void addResetOp(mlir::OpBuilder& builder, const qc::Operation& operation, - const llvm::SmallVector& qubits) { - for (const auto& target : operation.getTargets()) { - const mlir::Value inQubit = qubits[target]; - builder.create(builder.getUnknownLoc(), inQubit); - } -} - -// Forward declaration -static llvm::LogicalResult -addOperation(mlir::OpBuilder& builder, const qc::Operation& operation, - const llvm::SmallVector& qubits, - const BitIndexVec& bitMap); - -/** - * @brief Adds the operation(s) in a block to the MLIR module. - * - * @param builder The MLIR OpBuilder - * @param operationInBlock The operation(s) in a block - * @param qubits The qubits of the quantum register - * @param bitMap The mapping from global classical bit index to (memref, - * localIdx) - * @return llvm::LogicalResult whether the adding of the blockOps was successful - */ -static llvm::LogicalResult -addBlockOps(mlir::OpBuilder& builder, const qc::Operation* operationInBlock, - const llvm::SmallVector& qubits, - const BitIndexVec& bitMap) { - if (operationInBlock->isCompoundOperation()) { - for (const auto& operation : - dynamic_cast(*operationInBlock)) { - if (addOperation(builder, *operation, qubits, bitMap).failed()) { - return llvm::failure(); - } - } - } else { - if (addOperation(builder, *operationInBlock, qubits, bitMap).failed()) { - return llvm::failure(); - } - } - return llvm::success(); -} - -/** - * @brief Compute integer value from a classical register (memref). - * - * @param builder The MLIR OpBuilder - * @param bits The bits of the classical register - * @return mlir::Value The integer value of the register - */ -static mlir::Value getIntegerValueFromRegister(mlir::OpBuilder& builder, - const mlir::Value bits) { - const auto loc = builder.getUnknownLoc(); - - // Extract length (assumed 1-D static memref) - const auto bitsType = mlir::cast(bits.getType()); - const auto shape = bitsType.getShape(); - assert(shape.size() == 1 && - "Expected 1-D memref type in getIntegerValueFromRegister"); - const auto size = shape[0]; - - // Loop constants - auto lb = builder.create(loc, 0); - auto ub = builder.create(loc, size); - auto step = builder.create(loc, 1); - - // Initial accumulator (i64 0) - auto initial = builder.create( - loc, builder.getI64IntegerAttr(0)); - - // for (i = 0; i < size; ++i) acc += (bit_i << i) - auto loop = builder.create( - loc, lb, ub, step, mlir::ValueRange{initial}, - [&builder, &bits](mlir::OpBuilder& b, const mlir::Location forLoc, - mlir::Value iv, const mlir::ValueRange iterArgs) { - auto bit = - b.create(forLoc, bits, mlir::ValueRange{iv}); - auto bitExt = - b.create(forLoc, builder.getI64Type(), bit); - - // Cast loop index to i64 for shift amount - auto shiftAmt = b.create( - forLoc, builder.getI64Type(), iv); - // shifted = bitExt << shiftAmt - auto shifted = b.create(forLoc, bitExt, shiftAmt); - - auto acc = b.create(forLoc, iterArgs[0], shifted); - b.create(forLoc, acc.getResult()); - }); - - return loop.getResult(0); -} - -/** - * @brief Adds an if-else operation to the MLIR module. - * - * @param builder The MLIR OpBuilder - * @param op The if-else operation to add - * @param qubits The qubits of the quantum register - * @param bitMap The mapping from global classical bit index to (memref, - * localIdx) - * @return mlir::LogicalResult Success if all operations were added, failure - * otherwise - */ -static llvm::LogicalResult -addIfElseOp(mlir::OpBuilder& builder, const qc::Operation& op, - const llvm::SmallVector& qubits, - const BitIndexVec& bitMap) { - const auto loc = builder.getUnknownLoc(); - const auto& ifElse = dynamic_cast(op); - - const auto* thenOp = ifElse.getThenOp(); - // Canonicalization should have removed empty then blocks - assert(thenOp != nullptr); - const auto* elseOp = ifElse.getElseOp(); - - mlir::Value controlValue; - mlir::Value expectedValue; - if (ifElse.getControlRegister().has_value()) { - const auto& ctrlReg = ifElse.getControlRegister().value(); - const auto startIdx = static_cast(ctrlReg.getStartIndex()); - assert(startIdx < bitMap.size() && - "Control register start index out of range"); - const auto mem = bitMap[startIdx].first; - controlValue = getIntegerValueFromRegister(builder, mem); - - expectedValue = builder.create( - loc, builder.getI64IntegerAttr( - static_cast(ifElse.getExpectedValueRegister()))); - } else { - const auto controlBit = ifElse.getControlBit(); - const auto bitIdx = static_cast(*controlBit); - assert(bitIdx < bitMap.size() && "Control bit index out of range"); - const auto& [mem, localIdx] = bitMap[bitIdx]; - auto indexValue = builder.create( - loc, static_cast(localIdx)); - controlValue = builder.create( - loc, mem, mlir::ValueRange{indexValue}); - expectedValue = builder.create( - loc, builder.getIntegerAttr( - builder.getI1Type(), - static_cast(ifElse.getExpectedValueBit()))); - } - - // Define comparison predicate - const auto comparisonKind = ifElse.getComparisonKind(); - auto predicate = mlir::arith::CmpIPredicate::eq; - switch (comparisonKind) { - case qc::ComparisonKind::Eq: - predicate = mlir::arith::CmpIPredicate::eq; - break; - case qc::ComparisonKind::Neq: - predicate = mlir::arith::CmpIPredicate::ne; - break; - case qc::ComparisonKind::Lt: - predicate = mlir::arith::CmpIPredicate::ult; - break; - case qc::ComparisonKind::Leq: - predicate = mlir::arith::CmpIPredicate::ule; - break; - case qc::ComparisonKind::Gt: - predicate = mlir::arith::CmpIPredicate::ugt; - break; - case qc::ComparisonKind::Geq: - predicate = mlir::arith::CmpIPredicate::uge; - break; - } - - // Define condition - auto condition = builder.create( - loc, predicate, controlValue, expectedValue); - - // Define operation - auto ifOp = builder.create(loc, mlir::TypeRange{}, - condition.getResult(), true); - - // Populate then block - { - const mlir::OpBuilder::InsertionGuard thenGuard(builder); - builder.setInsertionPointToStart(ifOp.thenBlock()); - if (addBlockOps(builder, thenOp, qubits, bitMap).failed()) { - return llvm::failure(); - } - } - - // Populate else block - if (elseOp != nullptr) { - const mlir::OpBuilder::InsertionGuard elseGuard(builder); - builder.setInsertionPointToStart(ifOp.elseBlock()); - if (addBlockOps(builder, elseOp, qubits, bitMap).failed()) { - return llvm::failure(); - } - } - - return llvm::success(); -} - -#define ADD_OP_CASE(op) \ - case qc::OpType::op: \ - addUnitaryOp(builder, operation, qubits); \ - return llvm::success(); - -/** - * @brief Adds a single quantum operation to the MLIR module. - * - * @param builder The MLIR OpBuilder used to create operations - * @param operation The quantum operation to add - * @param qubits The qubits of the quantum register - * @param bitMap The mapping from global classical bit index to (memref, - * localIdx) - * @return mlir::LogicalResult Success if all operations were added, failure - * otherwise - */ -static llvm::LogicalResult -addOperation(mlir::OpBuilder& builder, const qc::Operation& operation, - const llvm::SmallVector& qubits, - const BitIndexVec& bitMap) { - switch (operation.getType()) { - ADD_OP_CASE(Barrier) - ADD_OP_CASE(I) - ADD_OP_CASE(H) - ADD_OP_CASE(X) - ADD_OP_CASE(Y) - ADD_OP_CASE(Z) - ADD_OP_CASE(S) - ADD_OP_CASE(Sdg) - ADD_OP_CASE(T) - ADD_OP_CASE(Tdg) - ADD_OP_CASE(V) - ADD_OP_CASE(Vdg) - ADD_OP_CASE(U) - ADD_OP_CASE(U2) - ADD_OP_CASE(P) - ADD_OP_CASE(SX) - ADD_OP_CASE(SXdg) - ADD_OP_CASE(R) - ADD_OP_CASE(RX) - ADD_OP_CASE(RY) - ADD_OP_CASE(RZ) - ADD_OP_CASE(SWAP) - ADD_OP_CASE(iSWAP) - ADD_OP_CASE(iSWAPdg) - ADD_OP_CASE(Peres) - ADD_OP_CASE(Peresdg) - ADD_OP_CASE(DCX) - ADD_OP_CASE(ECR) - ADD_OP_CASE(RXX) - ADD_OP_CASE(RYY) - ADD_OP_CASE(RZZ) - ADD_OP_CASE(RZX) - ADD_OP_CASE(XXminusYY) - ADD_OP_CASE(XXplusYY) - case qc::OpType::Measure: - addMeasureOp(builder, operation, qubits, bitMap); - return llvm::success(); - case qc::OpType::Reset: - addResetOp(builder, operation, qubits); - return llvm::success(); - case qc::OpType::IfElse: - return addIfElseOp(builder, operation, qubits, bitMap); - default: - return llvm::failure(); - } -} - -/** - * @brief Adds quantum operations to the MLIR module. - * - * @param builder The MLIR OpBuilder used to create operations - * @param quantumComputation The quantum computation to translate - * @param qubits The qubits of the quantum register - * @param bitMap The mapping from global classical bit index to (memref, - * localIdx) - * @return mlir::LogicalResult Success if all operations were added, failure - * otherwise - */ -static llvm::LogicalResult addOperations( - mlir::OpBuilder& builder, const qc::QuantumComputation& quantumComputation, - const llvm::SmallVector& qubits, const BitIndexVec& bitMap) { - for (const auto& operation : quantumComputation) { - if (const auto result = addOperation(builder, *operation, qubits, bitMap); - result.failed()) { - return result; - } - } - return llvm::success(); -} - -/** - * @brief Translates a QuantumComputation to an MLIR module with MQTRef - * operations. - * - * This function takes a quantum computation and translates it into an MLIR - * module containing MQTRef dialect operations. It creates a main function that - * contains all quantum operations from the input computation. - * - * @param context The MLIR context in which the module will be created - * @param quantumComputation The quantum computation to translate - * @return mlir::OwningOpRef The translated MLIR module - */ -mlir::OwningOpRef translateQuantumComputationToMLIR( - mlir::MLIRContext* context, - const qc::QuantumComputation& quantumComputation) { - mlir::OpBuilder builder(context); - const auto loc = builder.getUnknownLoc(); - - // Create module - auto module = builder.create(loc); - builder.setInsertionPointToStart(module.getBody()); - - // Create main function as entry point - auto funcType = builder.getFunctionType({}, {}); - auto mainFunc = builder.create(loc, "main", funcType); - - // Add entry_point attribute to identify the main function - const auto entryPointAttr = mlir::StringAttr::get(context, "entry_point"); - mainFunc->setAttr("passthrough", - mlir::ArrayAttr::get(context, {entryPointAttr})); - - auto& entryBlock = mainFunc.getBody().emplaceBlock(); - builder.setInsertionPointToStart(&entryBlock); - - // Allocate quantum registers and extract qubits - auto qregs = getQregs(builder, context, quantumComputation); - auto qubits = getQubits(quantumComputation, qregs); - - // Allocate classical registers - auto bitMap = getBitMap(builder, quantumComputation); - - // Add operations and handle potential failures - if (addOperations(builder, quantumComputation, qubits, bitMap).failed()) { - // Even if operations fail, return the module with what we could translate - emitError(loc) << "Failed to translate some quantum operations"; - } - - // Deallocate quantum registers - for (const auto& qreg : qregs) { - deallocateQreg(builder, qreg.qreg); - } - - // Create terminator - builder.create(loc); - - return module; -} diff --git a/mlir/test/CMakeLists.txt b/mlir/test/CMakeLists.txt deleted file mode 100644 index 5ebca4fa39..0000000000 --- a/mlir/test/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in - ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py - MAIN_CONFIG - ${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py - DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in - ${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py) - -set(MQT_CORE_MLIR_TEST_DEPENDS FileCheck count not split-file quantum-opt) - -add_custom_target(mqt-core-mlir-lit-test-build-only DEPENDS ${MQT_CORE_MLIR_TEST_DEPENDS}) -set_target_properties(mqt-core-mlir-lit-test-build-only PROPERTIES FOLDER "Tests") - -add_lit_testsuite(mqt-core-mlir-lit-test "Running the MQT MLIR tests" ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ${MQT_CORE_MLIR_TEST_DEPENDS}) -set_target_properties(mqt-core-mlir-lit-test PROPERTIES FOLDER "Tests") - -add_test(NAME mqt-core-mlir-lit-test COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target - mqt-core-mlir-lit-test --config $) diff --git a/mlir/test/Conversion/mqtopt-to-mqtref.mlir b/mlir/test/Conversion/mqtopt-to-mqtref.mlir deleted file mode 100644 index 0b40d6d759..0000000000 --- a/mlir/test/Conversion/mqtopt-to-mqtref.mlir +++ /dev/null @@ -1,606 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --mqtopt-to-mqtref | FileCheck %s - -// ----- -// This test checks if a non-!mqtref.Qubit is not converted. -module { - // CHECK-LABEL: func.func @testDoNotConvertMemRef() - func.func @testDoNotConvertMemRef() { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Memref:.*]] = memref.alloc() : memref<1xi1> - // CHECK: %[[C:.*]] = memref.load %[[Memref]][%[[I0]]] : memref<1xi1 - // CHECK: memref.store %[[C]], %[[Memref]][%[[I0]]] : memref<1xi1 - // CHECK: memref.dealloc %[[Memref]] : memref<1xi1> - - %i0 = arith.constant 0 : index - %memref = memref.alloc() : memref<1xi1> - %0 = memref.load %memref[%i0] : memref<1xi1> - memref.store %0, %memref[%i0] : memref<1xi1> - memref.dealloc %memref : memref<1xi1> - - return - } -} - -// ----- -// This test checks if the AllocOp is converted correctly using a static attribute. -module { - // CHECK-LABEL: func.func @testConvertAllocOpStatic() - func.func @testConvertAllocOpStatic() { - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtref.Qubit> - - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if the AllocOp is converted correctly using a dynamic operand. -module { - // CHECK-LABEL: func.func @testConvertAllocOpDynamic() - func.func @testConvertAllocOpDynamic() { - // CHECK: %[[I2:.*]] = arith.constant 2 : index - // CHECK: %[[Qreg:.*]] = memref.alloc(%[[I2]]) : memref - - %i2 = arith.constant 2 : index - %qreg = memref.alloc(%i2) : memref - memref.dealloc %qreg : memref - - return - } -} - -// ----- -// This test checks if the DeallocOp is converted correctly -module { - // CHECK-LABEL: func.func @testConvertDeallocOp - func.func @testConvertDeallocOp() { - // CHECK: memref.dealloc %[[ANY:.*]] : memref<2x!mqtref.Qubit> - - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if the LoadOp is converted correctly. -module { - // CHECK-LABEL: func.func @testConvertLoadOp - func.func @testConvertLoadOp() { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Q0:.*]] = memref.load %[[ANY:.*]][%[[I0]]] : memref<1x!mqtref.Qubit> - // CHECK-NOT: memref.store %[[Q0:.*]], %[[ANY:.*]][%[[I0]]] : memref<1x!mqtref.Qubit> - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.store %q0, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if the operands/results are correctly replaced for the def-use chain. -module { - // CHECK-LABEL: func.func @testConvertOperandChain - func.func @testConvertOperandChain() { - // CHECK: %[[I2:.*]] = arith.constant 2 : index - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<3x!mqtref.Qubit> - // CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<3x!mqtref.Qubit> - // CHECK: %[[Q1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<3x!mqtref.Qubit> - // CHECK: %[[Q2:.*]] = memref.load %[[Qreg]][%[[I2]]] : memref<3x!mqtref.Qubit> - // CHECK: memref.dealloc %[[Qreg]] : memref<3x!mqtref.Qubit> - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtopt.Qubit> - %q0 = memref.load %qreg[%i0] : memref<3x!mqtopt.Qubit> - %q1 = memref.load %qreg[%i1] : memref<3x!mqtopt.Qubit> - %q2 = memref.load %qreg[%i2] : memref<3x!mqtopt.Qubit> - memref.store %q0, %qreg[%i0] : memref<3x!mqtopt.Qubit> - memref.store %q1, %qreg[%i1] : memref<3x!mqtopt.Qubit> - memref.store %q2, %qreg[%i2] : memref<3x!mqtopt.Qubit> - memref.dealloc %qreg : memref<3x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if the MeasureOp is converted correctly -module { - // CHECK-LABEL: func.func @testConvertMeasureOp - func.func @testConvertMeasureOp() { - // CHECK: %[[m_0:.*]] = mqtref.measure %[[ANY:.*]] - // CHECK: memref.store %[[m_0]], %[[ANY:.*]][%[[ANY:.*]]] : memref<1xi1> - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %creg = memref.alloca() : memref<1xi1> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q1, %m0 = mqtopt.measure %q0 - memref.store %m0, %creg[%i0] : memref<1xi1> - - memref.store %q1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if the ResetOp is converted correctly -module { - // CHECK-LABEL: func.func @testConvertResetOp - func.func @testConvertResetOp() { - // CHECK: mqtref.reset %[[ANY:.*]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q1 = mqtopt.reset %q0 - - memref.store %q1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if single-qubit gates are converted correctly -module { - // CHECK-LABEL: func.func @testConvertSingleQubitOp - func.func @testConvertSingleQubitOp() { - // CHECK: mqtref.i() %[[q_0:.*]] - // CHECK: mqtref.h() %[[q_0]] - // CHECK: mqtref.x() %[[q_0]] - // CHECK: mqtref.y() %[[q_0]] - // CHECK: mqtref.z() %[[q_0]] - // CHECK: mqtref.s() %[[q_0]] - // CHECK: mqtref.sdg() %[[q_0]] - // CHECK: mqtref.t() %[[q_0]] - // CHECK: mqtref.tdg() %[[q_0]] - // CHECK: mqtref.v() %[[q_0]] - // CHECK: mqtref.vdg() %[[q_0]] - // CHECK: mqtref.sx() %[[q_0]] - // CHECK: mqtref.sxdg() %[[q_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q1 = mqtopt.i() %q0 : !mqtopt.Qubit - %q2 = mqtopt.h() %q1 : !mqtopt.Qubit - %q3 = mqtopt.x() %q2 : !mqtopt.Qubit - %q4 = mqtopt.y() %q3 : !mqtopt.Qubit - %q5 = mqtopt.z() %q4 : !mqtopt.Qubit - %q6 = mqtopt.s() %q5 : !mqtopt.Qubit - %q7 = mqtopt.sdg() %q6 : !mqtopt.Qubit - %q8 = mqtopt.t() %q7 : !mqtopt.Qubit - %q9 = mqtopt.tdg() %q8 : !mqtopt.Qubit - %q10 = mqtopt.v() %q9 : !mqtopt.Qubit - %q11 = mqtopt.vdg() %q10 : !mqtopt.Qubit - %q12 = mqtopt.sx() %q11 : !mqtopt.Qubit - %q13 = mqtopt.sxdg() %q12 : !mqtopt.Qubit - - memref.store %q13, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if two-qubit gates are converted correctly -module { - // CHECK-LABEL: func.func @testConvertTwoQubitOp - func.func @testConvertTwoQubitOp() { - // CHECK: mqtref.swap() %[[q_0:.*]], %[[q_1:.*]] - // CHECK: mqtref.iswap() %[[q_0]], %[[q_1]] - // CHECK: mqtref.iswapdg() %[[q_0]], %[[q_1]] - // CHECK: mqtref.peres() %[[q_0]], %[[q_1]] - // CHECK: mqtref.peresdg() %[[q_0]], %[[q_1]] - // CHECK: mqtref.dcx() %[[q_0]], %[[q_1]] - // CHECK: mqtref.ecr() %[[q_0]], %[[q_1]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.swap() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_2, %q1_2 = mqtopt.iswap() %q0_1, %q1_1 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_3, %q1_3 = mqtopt.iswapdg() %q0_2, %q1_2 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_4, %q1_4 = mqtopt.peres() %q0_3, %q1_3 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_5, %q1_5 = mqtopt.peresdg() %q0_4, %q1_4 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_6, %q1_6 = mqtopt.dcx() %q0_5, %q1_5 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_7, %q1_7 = mqtopt.ecr() %q0_6, %q1_6 : !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q0_7, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_7, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if controlled gates are converted correctly -module { - // CHECK-LABEL: func.func @testConvertControlledOp - func.func @testConvertControlledOp() { - // CHECK: %[[I2:.*]] = arith.constant 2 : index - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<3x!mqtref.Qubit> - // CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<3x!mqtref.Qubit> - // CHECK: %[[Q1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<3x!mqtref.Qubit> - // CHECK: %[[Q2:.*]] = memref.load %[[Qreg]][%[[I2]]] : memref<3x!mqtref.Qubit> - // CHECK: mqtref.x() %[[Q1]] ctrl %[[Q2]] nctrl %[[Q0]] - // CHECK: mqtref.swap() %[[Q1]], %[[Q0]] ctrl %[[Q2]] - // CHECK: memref.dealloc %[[Qreg]] : memref<3x!mqtref.Qubit> - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<3x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<3x!mqtopt.Qubit> - %q2_0 = memref.load %qreg[%i2] : memref<3x!mqtopt.Qubit> - - %q1_1, %q2_1, %q0_1 = mqtopt.x() %q1_0 ctrl %q2_0 nctrl %q0_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - %q1_2, %q0_2, %q2_2 = mqtopt.swap() %q1_1, %q0_1 ctrl %q2_1 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - - memref.store %q0_2, %qreg[%i0] : memref<3x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<3x!mqtopt.Qubit> - memref.store %q2_2, %qreg[%i2] : memref<3x!mqtopt.Qubit> - memref.dealloc %qreg : memref<3x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if parameterized single qubit gates are converted correctly -module { - // CHECK-LABEL: func.func @testSingleQubitRotationOp - func.func @testSingleQubitRotationOp() { - // CHECK: %[[c_0:.*]] = arith.constant 3.000000e-01 - // CHECK: mqtref.u(%[[c_0]], %[[c_0]], %[[c_0]]) %[[q_0:.*]] - // CHECK: mqtref.u2(%[[c_0]], %[[c_0]]) %[[q_0]] - // CHECK: mqtref.p(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.rx(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.ry(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.rz(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.r(%[[c_0]], %[[c_0]]) %[[q_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %cst = arith.constant 3.000000e-01 : f64 - %q1 = mqtopt.u(%cst, %cst, %cst) %q0 : !mqtopt.Qubit - %q2 = mqtopt.u2(%cst, %cst) %q1 : !mqtopt.Qubit - %q3 = mqtopt.p(%cst) %q2 : !mqtopt.Qubit - %q4 = mqtopt.rx(%cst) %q3 : !mqtopt.Qubit - %q5 = mqtopt.ry(%cst) %q4 : !mqtopt.Qubit - %q6 = mqtopt.rz(%cst) %q5 : !mqtopt.Qubit - %q7 = mqtopt.r(%cst, %cst) %q6 : !mqtopt.Qubit - - memref.store %q7, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if parameterized multiple qubit gates are converted correctly -module { - // CHECK-LABEL: func.func @testMultipleQubitRotationOp - func.func @testMultipleQubitRotationOp() { - // CHECK: %[[c_0:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: mqtref.rxx(%[[c_0]]) %[[q_0:.*]], %[[q_1:.*]] - // CHECK: mqtref.ryy(%[[c_0]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.rzz(%[[c_0]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.rzx(%[[c_0]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.xx_minus_yy(%[[c_0]], %[[c_0]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.xx_plus_yy(%[[c_0]], %[[c_0]]) %[[q_0]], %[[q_1]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %cst = arith.constant 3.000000e-01 : f64 - %q01_1:2 = mqtopt.rxx(%cst) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_2:2 = mqtopt.ryy(%cst) %q01_1#0, %q01_1#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_3:2 = mqtopt.rzz(%cst) %q01_2#0, %q01_2#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_4:2 = mqtopt.rzx(%cst) %q01_3#0, %q01_3#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_5:2 = mqtopt.xx_minus_yy(%cst, %cst) %q01_4#0, %q01_4#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_6:2 = mqtopt.xx_plus_yy(%cst, %cst) %q01_5#0, %q01_5#1 : !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q01_6#0, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q01_6#1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if static params and paramask is converted correctly -module { - // CHECK-LABEL: func.func @testConvertStaticParams - func.func @testConvertStaticParams() { - // CHECK: mqtref.u(%[[ANY:.*]], %[[ANY:.*]] static [3.000000e-01] mask [false, true, false]) %[[ANY:.*]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %cst = arith.constant 3.000000e-01 : f64 - %q1 = mqtopt.u(%cst, %cst static [3.000000e-01] mask [false, true, false]) %q0 : !mqtopt.Qubit - - memref.store %q1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a controlled op is converted correctly -module { - // CHECK-LABEL: func.func @testConvertControlledOp - func.func @testConvertControlledOp() { - // CHECK: mqtref.x() %[[q_0:.*]] ctrl %[[q_1:.*]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q1_1, %q0_1 = mqtopt.x() %q1_0 ctrl %q0_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - memref.store %q0_1, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a negative controlled op is converted correctly -module { - // CHECK-LABEL: func.func @testConvertNegativeControlledOp - func.func @testConvertNegativeControlledOp() { - // CHECK: mqtref.x() %[[q_0:.*]] nctrl %[[q_1:.*]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q1_1, %q0_1 = mqtopt.x() %q1_0 nctrl %q0_0 : !mqtopt.Qubit nctrl !mqtopt.Qubit - - memref.store %q0_1, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a Bell state is converted correctly. -module { - // CHECK-LABEL: func.func @bellState() - func.func @bellState() { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtref.Qubit> - // CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtref.Qubit> - // CHECK: %[[Q1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtref.Qubit> - // CHECK: mqtref.h() %[[Q0]] - // CHECK: mqtref.x() %[[Q1]] ctrl %[[Q0]] - // CHECK: %[[M0:.*]] = mqtref.measure %[[Q0]] - // CHECK: %[[M1:.*]] = mqtref.measure %[[Q1]] - // CHECK: memref.dealloc %[[Qreg]] : memref<2x!mqtref.Qubit> - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.x() %q1_0 ctrl %q0_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %m0 = mqtopt.measure %q0_2 - %q1_2, %m1 = mqtopt.measure %q1_1 - - memref.store %q0_3, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a gphaseOp with no target no controlled qubit is converted correctly -module { - // CHECK-LABEL: func.func @testConvertGPhaseOp() - func.func @testConvertGPhaseOp() { - // CHECK: %[[c_0:.*]] = arith.constant 3.000000e-01 : f64 - // mqtref.gphase(%[[c_0]]) - - %cst = arith.constant 3.000000e-01 : f64 - mqtopt.gphase(%cst) - return - } -} - -// ----- -// This test checks if a gphaseOp with a controlled qubit is converted correctly -module { - // CHECK-LABEL: func.func @testConvertGPhaseOpControlled() - func.func @testConvertGPhaseOpControlled() { - // CHECK: %[[c_0:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: mqtref.gphase(%[[c_0]]) ctrl %[[ANY:.*]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %cst = arith.constant 3.000000e-01 : f64 - %q1 = mqtopt.gphase(%cst) ctrl %q0 : ctrl !mqtopt.Qubit - - memref.store %q1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a gphaseOp with a positive controlled qubit and a negative controlled qubit is converted correctly -module { - // CHECK-LABEL: func.func @testConvertGPhaseOpPositiveNegativeControlled() - func.func @testConvertGPhaseOpPositiveNegativeControlled() { - // CHECK: %[[c_0:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: mqtref.gphase(%[[c_0]]) ctrl %[[ANY:.*]] nctrl %[[ANY:.*]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %cst = arith.constant 3.000000e-01 : f64 - %q0_1, %q1_1 = mqtopt.gphase(%cst) ctrl %q0_0 nctrl %q1_0 : ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - memref.store %q0_1, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a barrierOp is converted correctly -module { - // CHECK-LABEL: func.func @testConvertBarrierOp() - func.func @testConvertBarrierOp() { - // CHECK: mqtref.barrier() %[[ANY:.*]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q1 = mqtopt.barrier() %q0 : !mqtopt.Qubit - - memref.store %q1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a barrierOp with multiple inputs is converted correctly -module { - // CHECK-LABEL: func.func @testConvertBarrierOpMultipleInputs() - func.func @testConvertBarrierOpMultipleInputs() { - // CHECK: mqtref.barrier() %[[ANY:.*]], %[[ANY:.*]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q01_1:2 = mqtopt.barrier() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q01_1#0, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q01_1#1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if the QubitOp is converted correctly -module { - // CHECK-LABEL: func.func @testConvertQubitOp - func.func @testConvertQubitOp() { - // CHECK: %[[Q0:.*]] = mqtref.qubit 0 - // CHECK-NOT: %[[ANY:.*]] = mqtopt.qubit 0 - - %q0 = mqtopt.qubit 0 - return - } -} - -// ----- -// This test checks if a Bell state is converted correctly for static qubits. -module { - // CHECK-LABEL: func.func @bellStateStatic() - func.func @bellStateStatic() { - // CHECK: %[[q_0:.*]] = mqtref.qubit 0 - // CHECK: %[[q_1:.*]] = mqtref.qubit 1 - // CHECK: mqtref.h() %[[q_0]] - // CHECK: mqtref.x() %[[q_1]] ctrl %[[q_0]] - // CHECK: %[[m_0:.*]] = mqtref.measure %[[q_0]] - // CHECK: %[[m_1:.*]] = mqtref.measure %[[q_1]] - - %q0 = mqtopt.qubit 0 - %q1 = mqtopt.qubit 1 - - %q0_1 = mqtopt.h() %q0 : !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.x() %q1 ctrl %q0_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %m0 = mqtopt.measure %q0_2 - %q1_2, %m1 = mqtopt.measure %q1_1 - - return - } -} - -// ----- -// This test checks if single-qubit allocation and deallocation are converted correctly -module { - // CHECK-LABEL: func.func @testConvertAllocDeallocQubit() - func.func @testConvertAllocDeallocQubit() { - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: mqtref.deallocQubit %[[q_0]] - %q0 = mqtopt.allocQubit - mqtopt.deallocQubit %q0 - return - } -} diff --git a/mlir/test/Conversion/mqtref-to-mqtopt.mlir b/mlir/test/Conversion/mqtref-to-mqtopt.mlir deleted file mode 100644 index 24990eb3ef..0000000000 --- a/mlir/test/Conversion/mqtref-to-mqtopt.mlir +++ /dev/null @@ -1,593 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --mqtref-to-mqtopt | FileCheck %s - -// ----- -// This test checks if a non-!mqtref.Qubit is not converted. -module { - // CHECK-LABEL: func.func @testDoNotConvertMemRef() - func.func @testDoNotConvertMemRef() { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Memref:.*]] = memref.alloc() : memref<1xi1> - // CHECK: %[[C:.*]] = memref.load %[[Memref]][%[[I0]]] : memref<1xi1 - // CHECK: memref.store %[[C]], %[[Memref]][%[[I0]]] : memref<1xi1 - // CHECK: memref.dealloc %[[Memref]] : memref<1xi1> - - %i0 = arith.constant 0 : index - %memref = memref.alloc() : memref<1xi1> - %0 = memref.load %memref[%i0] : memref<1xi1> - memref.store %0, %memref[%i0] : memref<1xi1> - memref.dealloc %memref : memref<1xi1> - - return - } -} - -// ----- -// This test checks if the AllocOp is converted correctly using a static attribute. -module { - // CHECK-LABEL: func.func @testConvertAllocOpStatic() - func.func @testConvertAllocOpStatic() { - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> - - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - - return - } -} - -// ----- -// This test checks if the AllocOp is converted correctly using a dynamic operand. -module { - // CHECK-LABEL: func.func @testConvertAllocOpDynamic() - func.func @testConvertAllocOpDynamic() { - // CHECK: %[[I2:.*]] = arith.constant 2 : index - // CHECK: %[[Qreg:.*]] = memref.alloc(%[[I2]]) : memref - - %i2 = arith.constant 2 : index - %qreg = memref.alloc(%i2) : memref - - return - } -} - -// ----- -// This test checks if the DeallocOp is converted correctly -module { - // CHECK-LABEL: func.func @testConvertDeallocOp - func.func @testConvertDeallocOp() { - // CHECK: memref.dealloc %[[ANY:.*]] : memref<2x!mqtopt.Qubit> - - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - memref.dealloc %qreg : memref<2x!mqtref.Qubit> - - return - } -} - -// ----- -// This test checks if the ExtractOp is converted correctly. -module { - // CHECK-LABEL: func.func @testConvertExtractOp - func.func @testConvertExtractOp() { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[q_0:.*]] = memref.load %[[ANY:.*]][%[[I0]]] : memref<1x!mqtopt.Qubit> - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - return - } -} - -// ----- -// This test checks if the operands/results are correctly replaced for the def-use chain in the opt dialect -module { - // CHECK-LABEL: func.func @testConvertOperandChain - func.func @testConvertOperandChain() { - // CHECK: %[[I2:.*]] = arith.constant 2 : index - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<3x!mqtopt.Qubit> - // CHECK: %[[q_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<3x!mqtopt.Qubit> - // CHECK: %[[q_1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<3x!mqtopt.Qubit> - // CHECK: %[[q_2:.*]] = memref.load %[[Qreg]][%[[I2]]] : memref<3x!mqtopt.Qubit> - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<3x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<3x!mqtref.Qubit> - %q2 = memref.load %qreg[%i2] : memref<3x!mqtref.Qubit> - - return - } -} - -// ----- -// This test checks if StoreOps are inserted correctly for a single register. -module { - // CHECK-LABEL: func.func @testConvertStoreOpSingleRegister - func.func @testConvertStoreOpSingleRegister() { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: memref.store %[[Q0]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: memref.dealloc %[[Qreg]] : memref<1x!mqtopt.Qubit> - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - - return - } -} - -// ----- -// This test checks if StoreOps are inserted correctly for multiple registers. -module { - // CHECK-LABEL: func.func @testConvertStoreOpMultipleRegisters - func.func @testConvertStoreOpMultipleRegisters() { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[QregA:.*]] = memref.alloc() : memref<1x!mqtopt.Qubit> - // CHECK: %[[QregB:.*]] = memref.alloc() : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0A:.*]] = memref.load %[[QregA]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0B:.*]] = memref.load %[[QregB]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: memref.store %[[Q0A]], %[[QregA]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: memref.dealloc %[[QregA]] : memref<1x!mqtopt.Qubit> - // CHECK: memref.store %[[Q0B]], %[[QregB]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: memref.dealloc %[[QregB]] : memref<1x!mqtopt.Qubit> - - %i0 = arith.constant 0 : index - %qrega = memref.alloc() : memref<1x!mqtref.Qubit> - %qregb = memref.alloc() : memref<1x!mqtref.Qubit> - %q0a = memref.load %qrega[%i0] : memref<1x!mqtref.Qubit> - %q0b = memref.load %qregb[%i0] : memref<1x!mqtref.Qubit> - memref.dealloc %qrega : memref<1x!mqtref.Qubit> - memref.dealloc %qregb : memref<1x!mqtref.Qubit> - - return - } -} - -// ----- -// This test checks if the MeasureOp is converted correctly -module { - // CHECK-LABEL: func.func @testConvertMeasureOp - func.func @testConvertMeasureOp() { - // CHECK: %[[q_0:.*]], %[[m_0:.*]] = mqtopt.measure %[[ANY:.*]] - // CHECK: memref.store %[[m_0]], %[[ANY:.*]][%[[ANY:.*]]] : memref<1xi1> - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %creg = memref.alloca() : memref<1xi1> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - %m0 = mqtref.measure %q0 - memref.store %m0, %creg[%i0] : memref<1xi1> - - return - } -} - -// ----- -// This test checks if the ResetOp is converted correctly -module { - // CHECK-LABEL: func.func @testConvertResetOp - func.func @testConvertResetOp() { - // CHECK: %[[q_0:.*]] = mqtopt.reset %[[ANY:.*]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - mqtref.reset %q0 - - return - } -} - -// ----- -// This test checks if single-qubit gates are converted correctly -module { - // CHECK-LABEL: func.func @testConvertSingleQubitOp - func.func @testConvertSingleQubitOp() { - // CHECK: %[[q_0:.*]] = mqtopt.i() %[[ANY:.*]] : !mqtopt.Qubit - // CHECK: %[[q_1:.*]] = mqtopt.h() %[[q_0]] : !mqtopt.Qubit - // CHECK: %[[q_2:.*]] = mqtopt.x() %[[q_1]] : !mqtopt.Qubit - // CHECK: %[[q_3:.*]] = mqtopt.y() %[[q_2]] : !mqtopt.Qubit - // CHECK: %[[q_4:.*]] = mqtopt.z() %[[q_3]] : !mqtopt.Qubit - // CHECK: %[[q_5:.*]] = mqtopt.s() %[[q_4]] : !mqtopt.Qubit - // CHECK: %[[q_6:.*]] = mqtopt.sdg() %[[q_5]] : !mqtopt.Qubit - // CHECK: %[[q_7:.*]] = mqtopt.t() %[[q_6]] : !mqtopt.Qubit - // CHECK: %[[q_8:.*]] = mqtopt.tdg() %[[q_7]] : !mqtopt.Qubit - // CHECK: %[[q_9:.*]] = mqtopt.v() %[[q_8]] : !mqtopt.Qubit - // CHECK: %[[q_10:.*]] = mqtopt.vdg() %[[q_9]] : !mqtopt.Qubit - // CHECK: %[[q_11:.*]] = mqtopt.sx() %[[q_10]] : !mqtopt.Qubit - // CHECK: %[[q_12:.*]] = mqtopt.sxdg() %[[q_11]] : !mqtopt.Qubit - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - mqtref.i() %q0 - mqtref.h() %q0 - mqtref.x() %q0 - mqtref.y() %q0 - mqtref.z() %q0 - mqtref.s() %q0 - mqtref.sdg() %q0 - mqtref.t() %q0 - mqtref.tdg() %q0 - mqtref.v() %q0 - mqtref.vdg() %q0 - mqtref.sx() %q0 - mqtref.sxdg() %q0 - - return - } -} - -// ----- -// This test checks if two-qubit gates are converted correctly -module { - // CHECK-LABEL: func.func @testConvertTwoQubitOp - func.func @testConvertTwoQubitOp() { - // CHECK: %[[q01_1:.*]]:2 = mqtopt.swap() %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[q01_2:.*]]:2 = mqtopt.iswap() %[[q01_1]]#0, %[[q01_1]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[q01_3:.*]]:2 = mqtopt.iswapdg() %[[q01_2]]#0, %[[q01_2]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[q01_4:.*]]:2 = mqtopt.peres() %[[q01_3]]#0, %[[q01_3]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[q01_5:.*]]:2 = mqtopt.peresdg() %[[q01_4]]#0, %[[q01_4]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[q01_6:.*]]:2 = mqtopt.dcx() %[[q01_5]]#0, %[[q01_5]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[q01_7:.*]]:2 = mqtopt.ecr() %[[q01_6]]#0, %[[q01_6]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - mqtref.swap() %q0, %q1 - mqtref.iswap() %q0, %q1 - mqtref.iswapdg() %q0, %q1 - mqtref.peres() %q0, %q1 - mqtref.peresdg() %q0, %q1 - mqtref.dcx() %q0, %q1 - mqtref.ecr() %q0, %q1 - - return - } -} - -// ----- -// This test checks if controlled gates are converted correctly -module { - // CHECK-LABEL: func.func @testConvertControlledOp - func.func @testConvertControlledOp() { - // CHECK: %[[I2:.*]] = arith.constant 2 : index - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<3x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<3x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<3x!mqtopt.Qubit> - // CHECK: %[[Q2_0:.*]] = memref.load %[[Qreg]][%[[I2]]] : memref<3x!mqtopt.Qubit> - // CHECK: %[[Q1_1:.*]], %[[Q2_1:.*]], %[[Q0_1:.*]] = mqtopt.x() %[[Q1_0]] ctrl %[[Q2_0]] nctrl %[[Q0_0]] - // CHECK: %[[ANY:.*]]:2, %[[Q2_2:.*]] = mqtopt.swap() %[[Q1_1]], %[[Q0_1]] ctrl %[[Q2_1]] - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<3x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<3x!mqtref.Qubit> - %q2 = memref.load %qreg[%i2] : memref<3x!mqtref.Qubit> - - mqtref.x() %q1 ctrl %q2 nctrl %q0 - mqtref.swap() %q1, %q0 ctrl %q2 - - return - } -} - -// ----- -// This test checks if parameterized single qubit gates are converted correctly -module { - // CHECK-LABEL: func.func @testSingleQubitRotationOp - func.func @testSingleQubitRotationOp() { - // CHECK: %[[c_0:.*]] = arith.constant 3.000000e-01 - // CHECK: %[[q_0:.*]] = mqtopt.u(%[[c_0]], %[[c_0]], %[[c_0]]) %[[ANY:.*]] : !mqtopt.Qubit - // CHECK: %[[q_1:.*]] = mqtopt.u2(%[[c_0]], %[[c_0]]) %[[q_0]] : !mqtopt.Qubit - // CHECK: %[[q_2:.*]] = mqtopt.p(%[[c_0]]) %[[q_1]] : !mqtopt.Qubit - // CHECK: %[[q_3:.*]] = mqtopt.rx(%[[c_0]]) %[[q_2]] : !mqtopt.Qubit - // CHECK: %[[q_4:.*]] = mqtopt.ry(%[[c_0]]) %[[q_3]] : !mqtopt.Qubit - // CHECK: %[[q_5:.*]] = mqtopt.rz(%[[c_0]]) %[[q_4]] : !mqtopt.Qubit - // CHECK: %[[q_6:.*]] = mqtopt.r(%[[c_0]], %[[c_0]]) %[[q_5]] : !mqtopt.Qubit - - %cst = arith.constant 3.000000e-01 : f64 - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - - mqtref.u(%cst, %cst, %cst) %q0 - mqtref.u2(%cst, %cst) %q0 - mqtref.p(%cst) %q0 - mqtref.rx(%cst) %q0 - mqtref.ry(%cst) %q0 - mqtref.rz(%cst) %q0 - mqtref.r(%cst, %cst) %q0 - - return - } -} - -// ----- -// This test checks if parameterized multiple qubit gates are converted correctly -module { - // CHECK-LABEL: func.func @testMultipleQubitRotationOp - func.func @testMultipleQubitRotationOp() { - // CHECK: %[[c_0:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: %[[q01_1:.*]]:2 = mqtopt.rxx(%[[c_0]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[q01_2:.*]]:2 = mqtopt.ryy(%[[c_0]]) %[[q01_1]]#0, %[[q01_1]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[q01_3:.*]]:2 = mqtopt.rzz(%[[c_0]]) %[[q01_2]]#0, %[[q01_2]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[q01_4:.*]]:2 = mqtopt.rzx(%[[c_0]]) %[[q01_3]]#0, %[[q01_3]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[q01_5:.*]]:2 = mqtopt.xx_minus_yy(%[[c_0]], %[[c_0]]) %[[q01_4]]#0, %[[q01_4]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[q01_6:.*]]:2 = mqtopt.xx_plus_yy(%[[c_0]], %[[c_0]]) %[[q01_5]]#0, %[[q01_5]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - - %cst = arith.constant 3.000000e-01 : f64 - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - mqtref.rxx(%cst) %q0, %q1 - mqtref.ryy(%cst) %q0, %q1 - mqtref.rzz(%cst) %q0, %q1 - mqtref.rzx(%cst) %q0, %q1 - mqtref.xx_minus_yy(%cst, %cst) %q0, %q1 - mqtref.xx_plus_yy(%cst, %cst) %q0, %q1 - - return - } -} - -// ----- -// This test checks if static params and paramask is converted correctly -module { - // CHECK-LABEL: func.func @testConvertStaticParams - func.func @testConvertStaticParams() { - // CHECK: %[[q_0:.*]] = mqtopt.u(%[[ANY:.*]], %[[ANY:.*]] static [3.000000e-01] mask [false, true, false]) %[[ANY:.*]] - - %cst = arith.constant 3.000000e-01 : f64 - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - - mqtref.u(%cst, %cst static [3.000000e-01] mask [false, true, false]) %q0 - - return - } -} - -// ----- -// This test checks if a controlled op is converted correctly -module { - // CHECK-LABEL: func.func @testConvertControlledOp - func.func @testConvertControlledOp() { - // CHECK: %[[q0_1:.*]], %[[q1_1:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - mqtref.x() %q1 ctrl %q0 - - return - } -} - -// ----- -// This test checks if a negative controlled op is converted correctly -module { - // CHECK-LABEL: func.func @testConvertNegativeControlledOp - func.func @testConvertNegativeControlledOp() { - // CHECK: %[[q0_1:.*]], %[[q1_1:.*]] = mqtopt.x() %[[ANY:.*]] nctrl %[[ANY:.*]] : !mqtopt.Qubit nctrl !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - mqtref.x() %q1 nctrl %q0 - - return - } -} - -// ----- -// This test checks if a Bell state is converted correctly. -module { - // CHECK-LABEL: func.func @bellConvertState() - func.func @bellConvertState() { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> - // CHECK: %[[q0_1:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[q1_1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[q0_2:.*]] = mqtopt.h() %[[q0_1]] : !mqtopt.Qubit - // CHECK: %[[q1_2:.*]], %[[q0_3:.*]] = mqtopt.x() %[[q1_1:.*]] ctrl %[[q0_2:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[q0_4:.*]], %[[m0_0:.*]] = mqtopt.measure %[[q0_3]] - // CHECK: %[[q1_3:.*]], %[[m1_0:.*]] = mqtopt.measure %[[q1_2]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - mqtref.h() %q0 - mqtref.x() %q1 ctrl %q0 - %m0 = mqtref.measure %q0 - %m1 = mqtref.measure %q1 - - return - } -} - -// ----- -// This test checks if a gphaseOp with no target no controlled qubit is converted correctly -module { - // CHECK-LABEL: func.func @testConvertGPhaseOp() - func.func @testConvertGPhaseOp() { - // CHECK: %[[c_0:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: mqtopt.gphase(%[[c_0]]) - - %cst = arith.constant 3.000000e-01 : f64 - mqtref.gphase(%cst) - return - } -} - -// ----- -// This test checks if a gphaseOp with a controlled qubit is converted correctly -module { - // CHECK-LABEL: func.func @testConvertGPhaseOpControlled() - func.func @testConvertGPhaseOpControlled() { - // CHECK: %[[c_0:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: %[[q_0:.*]] = mqtopt.gphase(%[[c_0]]) ctrl %[[ANY:.*]] : ctrl !mqtopt.Qubit - - %cst = arith.constant 3.000000e-01 : f64 - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - mqtref.gphase(%cst) ctrl %q0 - - return - } -} - -// ----- -// This test checks if a gphaseOp with a positive controlled qubit and a negative controlled qubit is converted correctly -module { - // CHECK-LABEL: func.func @testConvertGPhaseOpPositiveNegativeControlled() - func.func @testConvertGPhaseOpPositiveNegativeControlled() { - // CHECK: %[[c_0:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: %[[q0_1:.*]], %[[q1_1:.*]] = mqtopt.gphase(%[[c_0]]) ctrl %[[ANY:.*]] nctrl %[[ANY:.*]] : ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - %cst = arith.constant 3.000000e-01 : f64 - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - mqtref.gphase(%cst) ctrl %q0 nctrl %q1 - - return - } -} - - -// ----- -// This test checks if a barrierOp is converted correctly -module { - // CHECK-LABEL: func.func @testConvertBarrierOp() - func.func @testConvertBarrierOp() { - // CHECK: %[[q_0:.*]] = mqtopt.barrier() %[[ANY:.*]] : !mqtopt.Qubit - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - mqtref.barrier() %q0 - - return - } -} - -// ----- -// This test checks if a barrierOp with multiple inputs is converted correctly -module { - // CHECK-LABEL: func.func @testConvertBarrierOpMultipleInputs() - func.func @testConvertBarrierOpMultipleInputs() { - // CHECK: %[[q01_1:.*]]:2 = mqtopt.barrier() %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - mqtref.barrier() %q0, %q1 - - return - } -} - -// ----- -// This test checks if the QubitOp is converted correctly -module { - // CHECK-LABEL: func.func @testConvertQubitOp - func.func @testConvertQubitOp() { - // CHECK: %[[Q0:.*]] = mqtopt.qubit 0 - // CHECK-NOT: %[[ANY:.*]] = mqtref.qubit 0 - - %q0 = mqtref.qubit 0 - return - } -} - -// ----- -// This test checks if a Bell state is converted correctly for static qubits. -module { - // CHECK-LABEL: func.func @bellConvertStateStatic() - func.func @bellConvertStateStatic() { - // CHECK: %[[q0_1:.*]] = mqtopt.qubit 0 - // CHECK: %[[q1_1:.*]] = mqtopt.qubit 1 - // CHECK: %[[q0_2:.*]] = mqtopt.h() %[[q0_1]] : !mqtopt.Qubit - // CHECK: %[[q1_2:.*]], %[[q0_3:.*]] = mqtopt.x() %[[q1_1:.*]] ctrl %[[q0_2:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[q0_4:.*]], [[m0_0:.*]] = mqtopt.measure %[[q0_3]] - // CHECK: %[[q1_3:.*]], %[[m1_0:.*]] = mqtopt.measure %[[q1_2]] - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - - mqtref.h() %q0 - mqtref.x() %q1 ctrl %q0 - %m0 = mqtref.measure %q0 - %m1 = mqtref.measure %q1 - - return - } -} - -// ----- -// This test checks if single-qubit allocation and deallocation are converted correctly -module { - // CHECK-LABEL: func.func @testConvertAllocDeallocQubit() - func.func @testConvertAllocDeallocQubit() { - // CHECK: %[[q_0:.*]] = mqtopt.allocQubit - // CHECK: mqtopt.deallocQubit %[[q_0]] - %q0 = mqtref.allocQubit - mqtref.deallocQubit %q0 - return - } -} diff --git a/mlir/test/Conversion/mqtref-to-qir.mlir b/mlir/test/Conversion/mqtref-to-qir.mlir deleted file mode 100644 index 3fa101334b..0000000000 --- a/mlir/test/Conversion/mqtref-to-qir.mlir +++ /dev/null @@ -1,915 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --mqtref-to-qir | FileCheck %s - -// This test checks if a non-!mqtref.Qubit is not converted. -module { - // CHECK-LABEL: llvm.func @testDoNotConvertMemRef() - func.func @testDoNotConvertMemRef() attributes {passthrough = ["entry_point"]} { - // CHECK-NOT: llvm.call @__quantum__rt__qubit_allocate_array - // CHECK-NOT: llvm.call @__quantum__rt__array_get_element_ptr_1d - // CHECK-NOT: llvm.call @__quantum__rt__qubit_release_array - - %i0 = arith.constant 0 : index - %memref = memref.alloc() : memref<1xi1> - %0 = memref.load %memref[%i0] : memref<1xi1> - memref.store %0, %memref[%i0] : memref<1xi1> - memref.dealloc %memref : memref<1xi1> - - return - } -} - -// ----- -// This test checks if the initialize operation and zero operation is inserted. -module { - // CHECK-LABEL: llvm.func @testInitialize() - func.func @testInitialize() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[ptr_0:.*]] = llvm.mlir.zero : !llvm.ptr - // CHECK: llvm.call @__quantum__rt__initialize(%[[ptr_0]]) : (!llvm.ptr) -> () - - return - } -} - -// ----- -// This test checks if the AllocOp is converted correctly using a static attribute. -module { - // CHECK-LABEL: llvm.func @testConvertAllocStatic() - func.func @testConvertAllocStatic() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[size:.*]] = llvm.mlir.constant(2 : i64) : i64 - // CHECK: %[[r_0:.*]] = llvm.call @__quantum__rt__qubit_allocate_array(%[[size]]) : (i64) -> !llvm.ptr - - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - - return - } -} - -// ----- -// This test checks if the AllocOp is converted correctly using a dynamic operand. -module { - // CHECK-LABEL: llvm.func @testConvertAllocDynamic() - func.func @testConvertAllocDynamic() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[size:.*]] = llvm.mlir.constant(2 : index) : i64 - // CHECK: %[[r_0:.*]] = llvm.call @__quantum__rt__qubit_allocate_array(%[[size]]) : (i64) -> !llvm.ptr - - %i2 = arith.constant 2 : index - %qreg = memref.alloc(%i2) : memref - - return - } -} - -// ----- -// This test checks if the DeallocOp call is converted correctly. -module { - // CHECK-LABEL: llvm.func @testConvertDealloc() - func.func @testConvertDealloc() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[r_0:.*]] = llvm.call @__quantum__rt__qubit_allocate_array(%[[ANY:.*]]) : (i64) -> !llvm.ptr - // CHECK: llvm.call @__quantum__rt__qubit_release_array(%[[r_0]]) : (!llvm.ptr) -> () - - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - memref.dealloc %qreg : memref<2x!mqtref.Qubit> - - return - } -} - -// ----- -// This test checks if the allocQubit is converted correctly -module { - // CHECK-LABEL: llvm.func @testConvertAllocQubit() - func.func @testConvertAllocQubit() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[q_0:.*]] = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - // CHECK: %[[q_1:.*]] = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - - %q0 = mqtref.allocQubit - %q1 = mqtref.allocQubit - - return - } -} - -// ----- -// This test checks if the deallocQubit call is converted correctly -module { - // CHECK-LABEL: llvm.func @testConvertDeAllocQubit() - func.func @testConvertDeAllocQubit() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[q_0:.*]] = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - // CHECK: %[[q_1:.*]] = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - // CHECK: llvm.call @__quantum__rt__qubit_release(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__rt__qubit_release(%[[q_1]]) : (!llvm.ptr) -> () - - %q0 = mqtref.allocQubit - %q1 = mqtref.allocQubit - - mqtref.deallocQubit %q0 - mqtref.deallocQubit %q1 - return - } -} - -// ----- -// This test checks if the blocks for the base profile of QIR are correctly created -module { - // CHECK-LABEL: llvm.func @testBlockCreation() - func.func @testBlockCreation() attributes {passthrough = ["entry_point"]} { - // CHECK: llvm.br ^[[main:.*]] - // CHECK: ^[[main]]: - // CHECK: %[[size:.*]] = llvm.mlir.constant(2 : i64) : i64 - // CHECK: %[[r_0:.*]] = llvm.call @__quantum__rt__qubit_allocate_array(%[[size]]) : (i64) -> !llvm.ptr - // CHECK: llvm.br ^[[main2:.*]] - // CHECK: ^[[main2]]: - // CHECK: llvm.br ^[[end:.*]] - // CHECK: ^[[end]]: - // CHECK: llvm.return - - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - - return - } -} - -// ----- -// This test checks if the LoadOp is converted correctly -module { - // CHECK-LABEL: llvm.func @testConvertLoadOp() - func.func @testConvertLoadOp() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[size:.*]] = llvm.mlir.constant(1 : index) : i64 - // CHECK: %[[index:.*]] = llvm.mlir.constant(0 : index) : i64 - // CHECK: %[[r_0:.*]] = llvm.call @__quantum__rt__qubit_allocate_array(%[[size]]) : (i64) -> !llvm.ptr - // CHECK: %[[ptr_0:.*]] = llvm.call @__quantum__rt__array_get_element_ptr_1d(%[[r_0]], %[[index]]) : (!llvm.ptr, i64) -> !llvm.ptr - // CHECK: %[[q_0:.*]] = llvm.load %[[ptr_0]] : !llvm.ptr -> !llvm.ptr - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc(%i1) : memref - %q0 = memref.load %qreg[%i0] : memref - - return - } -} - - -// ----- -// This test checks if the reset operation is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertResetOp() - func.func @testConvertResetOp() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[q_0:.*]] = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - // CHECK: llvm.call @__quantum__qis__reset__body(%[[q_0]]) : (!llvm.ptr) -> () - - %q0 = mqtref.allocQubit - mqtref.reset %q0 - mqtref.deallocQubit %q0 - return - } -} - -// ----- -// This test checks if the reset operation using static qubits is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertResetOpStatic() - func.func @testConvertResetOpStatic() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[q_0:.*]] = llvm.mlir.zero : !llvm.ptr - // CHECK: llvm.call @__quantum__qis__reset__body(%[[q_0]]) : (!llvm.ptr) -> () - - %q0 = mqtref.qubit 0 - mqtref.reset %q0 - mqtref.deallocQubit %q0 - return - } -} - -// ----- -// This test checks if measure operations are converted correctly -module { - // CHECK: llvm.mlir.global internal constant @mlir.llvm.nameless_global_1("r1\00") {addr_space = 0 : i32, dso_local} - // CHECK: llvm.mlir.global internal constant @mlir.llvm.nameless_global_0("r0\00") {addr_space = 0 : i32, dso_local} - - // CHECK-LABEL: llvm.func @testMeasureOp() - func.func @testMeasureOp() attributes {passthrough = ["entry_point"]} { - // CHECK-DAG: %[[a_0:.*]] = llvm.mlir.addressof @mlir.llvm.nameless_global_0 : !llvm.ptr - // CHECK-DAG: %[[a_1:.*]] = llvm.mlir.addressof @mlir.llvm.nameless_global_1 : !llvm.ptr - // CHECK-DAG: %[[ptr_0:.*]] = llvm.mlir.zero : !llvm.ptr - // CHECK: %[[q_0:.*]] = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - // CHECK: %[[q_1:.*]] = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - // CHECK: llvm.call @__quantum__qis__mz__body(%[[q_0]], %[[ptr_0]]) : (!llvm.ptr, !llvm.ptr) - // CHECK-DAG: %[[c_1:.*]] = llvm.mlir.constant(1 : i64) : i64 - // CHECK-DAG: %[[ptr_1:.*]] = llvm.inttoptr %[[c_1]] : i64 to !llvm.ptr - // CHECK: llvm.call @__quantum__qis__mz__body(%[[q_1]], %[[ptr_1]]) : (!llvm.ptr, !llvm.ptr) - // CHECK: llvm.call @__quantum__rt__result_record_output(%[[ptr_1]], %[[a_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__rt__result_record_output(%[[ptr_0]], %[[a_0]]) : (!llvm.ptr, !llvm.ptr) -> () - - %q0 = mqtref.allocQubit - %q1 = mqtref.allocQubit - %mem = memref.alloca() : memref<2xi1> - %m0 = mqtref.measure %q0 - %c0 = arith.constant 0 : index - memref.store %m0, %mem[%c0] : memref<2xi1> - %m1 = mqtref.measure %q1 - %c1 = arith.constant 1 : index - memref.store %m1, %mem[%c1] : memref<2xi1> - mqtref.deallocQubit %q0 - mqtref.deallocQubit %q1 - return - } -} - -// ----- -// This test checks if measure operations using static qubits are converted correctly -module { - // CHECK: llvm.mlir.global internal constant @mlir.llvm.nameless_global_1("r1\00") {addr_space = 0 : i32, dso_local} - // CHECK: llvm.mlir.global internal constant @mlir.llvm.nameless_global_0("r0\00") {addr_space = 0 : i32, dso_local} - - // CHECK-LABEL: llvm.func @testMeasureOpStatic() - func.func @testMeasureOpStatic() attributes {passthrough = ["entry_point"]} { - // CHECK-DAG: %[[a_0:.*]] = llvm.mlir.addressof @mlir.llvm.nameless_global_0 : !llvm.ptr - // CHECK-DAG: %[[a_1:.*]] = llvm.mlir.addressof @mlir.llvm.nameless_global_1 : !llvm.ptr - // CHECK: %[[ptr_0:.*]] = llvm.mlir.zero : !llvm.ptr - // CHECK: %[[c_1:.*]] = llvm.mlir.constant(1 : i64) : i64 - // CHECK: %[[ptr_1:.*]] = llvm.inttoptr %[[c_1]] : i64 to !llvm.ptr - // CHECK: llvm.call @__quantum__qis__mz__body(%[[ptr_0]], %[[ptr_0]]) : (!llvm.ptr, !llvm.ptr) - // CHECK: llvm.call @__quantum__qis__mz__body(%[[ptr_1]], %[[ptr_1]]) : (!llvm.ptr, !llvm.ptr) - // CHECK: llvm.call @__quantum__rt__result_record_output(%[[ptr_1]], %[[a_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__rt__result_record_output(%[[ptr_0]], %[[a_0]]) : (!llvm.ptr, !llvm.ptr) -> () - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - %mem = memref.alloca() : memref<2xi1> - %m0 = mqtref.measure %q0 - %c0 = arith.constant 0 : index - memref.store %m0, %mem[%c0] : memref<2xi1> - %m1 = mqtref.measure %q1 - %c1 = arith.constant 1 : index - memref.store %m1, %mem[%c1] : memref<2xi1> - return - } -} - -// ----- -// This test checks if measure operations are converted correctly that store the classical result on the same index -module { - // CHECK: llvm.mlir.global internal constant @mlir.llvm.nameless_global_0("r0\00") {addr_space = 0 : i32, dso_local} - - // CHECK-LABEL: llvm.func @testMeasureOpOnSameIndex() - func.func @testMeasureOpOnSameIndex() attributes {passthrough = ["entry_point"]} { - // CHECK-DAG: %[[a_0:.*]] = llvm.mlir.addressof @mlir.llvm.nameless_global_0 : !llvm.ptr - // CHECK-DAG: %[[ptr_0:.*]] = llvm.mlir.zero : !llvm.ptr - // CHECK: %[[q_0:.*]] = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - // CHECK: %[[q_1:.*]] = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - // CHECK: llvm.call @__quantum__qis__mz__body(%[[q_0]], %[[ptr_0]]) : (!llvm.ptr, !llvm.ptr) - // CHECK-DAG: %[[c_1:.*]] = llvm.mlir.constant(1 : i64) : i64 - // CHECK-DAG: %[[ptr_1:.*]] = llvm.inttoptr %[[c_1]] : i64 to !llvm.ptr - // CHECK: llvm.call @__quantum__qis__mz__body(%[[q_1]], %[[ptr_1]]) : (!llvm.ptr, !llvm.ptr) - // CHECK: llvm.call @__quantum__rt__result_record_output(%[[ptr_1]], %[[a_0]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__rt__result_record_output(%[[ptr_0]], %[[a_0]]) : (!llvm.ptr, !llvm.ptr) -> () - - %q0 = mqtref.allocQubit - %q1 = mqtref.allocQubit - %mem = memref.alloca() : memref<2xi1> - %m0 = mqtref.measure %q0 - %c0 = arith.constant 0 : index - memref.store %m0, %mem[%c0] : memref<2xi1> - %m1 = mqtref.measure %q1 - memref.store %m1, %mem[%c0] : memref<2xi1> - mqtref.deallocQubit %q0 - mqtref.deallocQubit %q1 - return - } -} - -// ----- -// This test checks if measure operations using static qubits are converted correctly that store the classical result on the same index -module { - // CHECK: llvm.mlir.global internal constant @mlir.llvm.nameless_global_0("r0\00") {addr_space = 0 : i32, dso_local} - - // CHECK-LABEL: llvm.func @testMeasureOpOnSameIndexStatic() - func.func @testMeasureOpOnSameIndexStatic() attributes {passthrough = ["entry_point"]} { - // CHECK-DAG: %[[a_0:.*]] = llvm.mlir.addressof @mlir.llvm.nameless_global_0 : !llvm.ptr - // CHECK: %[[ptr_0:.*]] = llvm.mlir.zero : !llvm.ptr - // CHECK: %[[c_1:.*]] = llvm.mlir.constant(1 : i64) : i64 - // CHECK: %[[ptr_1:.*]] = llvm.inttoptr %[[c_1]] : i64 to !llvm.ptr - // CHECK: llvm.call @__quantum__qis__mz__body(%[[ptr_0]], %[[ptr_0]]) : (!llvm.ptr, !llvm.ptr) - // CHECK: llvm.call @__quantum__qis__mz__body(%[[ptr_1]], %[[ptr_1]]) : (!llvm.ptr, !llvm.ptr) - // CHECK: llvm.call @__quantum__rt__result_record_output(%[[ptr_1]], %[[a_0]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__rt__result_record_output(%[[ptr_0]], %[[a_0]]) : (!llvm.ptr, !llvm.ptr) -> () - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - %mem = memref.alloca() : memref<2xi1> - %m0 = mqtref.measure %q0 - %c0 = arith.constant 0 : index - memref.store %m0, %mem[%c0] : memref<2xi1> - %m1 = mqtref.measure %q1 - memref.store %m1, %mem[%c0] : memref<2xi1> - return - } -} - -// ----- -// This test checks if the single qubit gates are correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertSingleQubitOp() - func.func @testConvertSingleQubitOp() attributes {passthrough = ["entry_point"]} { - // CHECK: llvm.call @__quantum__qis__i__body(%[[q_0:.*]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__h__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__x__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__y__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__z__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__s__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__sdg__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__t__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__tdg__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__v__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__vdg__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__sx__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__sxdg__body(%[[q_0]]) : (!llvm.ptr) -> () - - %q0 = mqtref.allocQubit - mqtref.i() %q0 - mqtref.h() %q0 - mqtref.x() %q0 - mqtref.y() %q0 - mqtref.z() %q0 - mqtref.s() %q0 - mqtref.sdg() %q0 - mqtref.t() %q0 - mqtref.tdg() %q0 - mqtref.v() %q0 - mqtref.vdg() %q0 - mqtref.sx() %q0 - mqtref.sxdg() %q0 - mqtref.deallocQubit %q0 - return - } -} - -// ----- -// This test checks if the single qubit gates using static qubits are correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertSingleQubitOpStatic() - func.func @testConvertSingleQubitOpStatic() attributes {passthrough = ["entry_point"]} { - // CHECK: llvm.call @__quantum__qis__i__body(%[[q_0:.*]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__h__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__x__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__y__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__z__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__s__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__sdg__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__t__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__tdg__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__v__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__vdg__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__sx__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__sxdg__body(%[[q_0]]) : (!llvm.ptr) -> () - - %q0 = mqtref.qubit 0 - mqtref.i() %q0 - mqtref.h() %q0 - mqtref.x() %q0 - mqtref.y() %q0 - mqtref.z() %q0 - mqtref.s() %q0 - mqtref.sdg() %q0 - mqtref.t() %q0 - mqtref.tdg() %q0 - mqtref.v() %q0 - mqtref.vdg() %q0 - mqtref.sx() %q0 - mqtref.sxdg() %q0 - return - } -} - -// ----- -// This test checks if the two target gates are correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertTwoTargetOp() - func.func @testConvertTwoTargetOp() attributes {passthrough = ["entry_point"]} { - // CHECK: llvm.call @__quantum__qis__swap__body(%[[q_0:.*]], %[[q_1:.*]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__iswap__body(%[[q_0]], %[[q_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__iswapdg__body(%[[q_0]], %[[q_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__peres__body(%[[q_0]], %[[q_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__peresdg__body(%[[q_0]], %[[q_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__dcx__body(%[[q_0]], %[[q_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__ecr__body(%[[q_0]], %[[q_1]]) : (!llvm.ptr, !llvm.ptr) -> () - - %q0 = mqtref.allocQubit - %q1 = mqtref.allocQubit - mqtref.swap() %q0, %q1 - mqtref.iswap() %q0, %q1 - mqtref.iswapdg() %q0, %q1 - mqtref.peres() %q0, %q1 - mqtref.peresdg() %q0, %q1 - mqtref.dcx() %q0, %q1 - mqtref.ecr() %q0, %q1 - mqtref.deallocQubit %q0 - mqtref.deallocQubit %q1 - return - } -} - -// ----- -// This test checks if the two target gates using static qubits are correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertTwoTargetOpStatic() - func.func @testConvertTwoTargetOpStatic() attributes {passthrough = ["entry_point"]} { - // CHECK: llvm.call @__quantum__qis__swap__body(%[[q_0:.*]], %[[q_1:.*]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__iswap__body(%[[q_0]], %[[q_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__iswapdg__body(%[[q_0]], %[[q_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__peres__body(%[[q_0]], %[[q_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__peresdg__body(%[[q_0]], %[[q_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__dcx__body(%[[q_0]], %[[q_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__ecr__body(%[[q_0]], %[[q_1]]) : (!llvm.ptr, !llvm.ptr) -> () - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - mqtref.swap() %q0, %q1 - mqtref.iswap() %q0, %q1 - mqtref.iswapdg() %q0, %q1 - mqtref.peres() %q0, %q1 - mqtref.peresdg() %q0, %q1 - mqtref.dcx() %q0, %q1 - mqtref.ecr() %q0, %q1 - return - } -} - -// ----- -// This test checks if the single qubit rotation gates are correctly converted -module { - // CHECK-LABEL: llvm.func @testSingleQubitRotationOp() - func.func @testSingleQubitRotationOp() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: %[[c_1:.*]] = llvm.mlir.constant(1.000000e-01 : f64) : f64 - // CHECK: %[[c_2:.*]] = llvm.mlir.constant(2.000000e-01 : f64) : f64 - // CHECK: llvm.call @__quantum__qis__u3__body(%[[q_0:.*]], %[[c_0]], %[[c_1]], %[[c_2]]) : (!llvm.ptr, f64, f64, f64) -> () - // CHECK: llvm.call @__quantum__qis__u2__body(%[[q_0:.*]], %[[c_0]], %[[c_1]]) : (!llvm.ptr, f64, f64) -> () - // CHECK: llvm.call @__quantum__qis__p__body(%[[q_0]], %[[c_0]]) : (!llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__rx__body(%[[q_0]], %[[c_0]]) : (!llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__ry__body(%[[q_0]], %[[c_0]]) : (!llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__rz__body(%[[q_0]], %[[c_0]]) : (!llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__r__body(%[[q_0:.*]], %[[c_0]], %[[c_1]]) : (!llvm.ptr, f64, f64) -> () - - %c0 = arith.constant 3.000000e-01 : f64 - %c1 = arith.constant 1.000000e-01 : f64 - %c2 = arith.constant 2.000000e-01 : f64 - %q0 = mqtref.allocQubit - mqtref.u(%c0, %c1, %c2) %q0 - mqtref.u2(%c0, %c1) %q0 - mqtref.p(%c0) %q0 - mqtref.rx(%c0) %q0 - mqtref.ry(%c0) %q0 - mqtref.rz(%c0) %q0 - mqtref.r(%c0, %c1) %q0 - return - } -} - -// ----- -// This test checks if the single qubit rotation gates using static qubits are correctly converted -module { - // CHECK-LABEL: llvm.func @testSingleQubitRotationOpStatic() - func.func @testSingleQubitRotationOpStatic() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: %[[c_1:.*]] = llvm.mlir.constant(1.000000e-01 : f64) : f64 - // CHECK: %[[c_2:.*]] = llvm.mlir.constant(2.000000e-01 : f64) : f64 - // CHECK: llvm.call @__quantum__qis__u3__body(%[[q_0:.*]], %[[c_0]], %[[c_1]], %[[c_2]]) : (!llvm.ptr, f64, f64, f64) -> () - // CHECK: llvm.call @__quantum__qis__u2__body(%[[q_0:.*]], %[[c_0]], %[[c_1]]) : (!llvm.ptr, f64, f64) -> () - // CHECK: llvm.call @__quantum__qis__p__body(%[[q_0]], %[[c_0]]) : (!llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__rx__body(%[[q_0]], %[[c_0]]) : (!llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__ry__body(%[[q_0]], %[[c_0]]) : (!llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__rz__body(%[[q_0]], %[[c_0]]) : (!llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__r__body(%[[q_0:.*]], %[[c_0]], %[[c_1]]) : (!llvm.ptr, f64, f64) -> () - - %c0 = arith.constant 3.000000e-01 : f64 - %c1 = arith.constant 1.000000e-01 : f64 - %c2 = arith.constant 2.000000e-01 : f64 - %q0 = mqtref.qubit 0 - mqtref.u(%c0, %c1, %c2) %q0 - mqtref.u2(%c0, %c1) %q0 - mqtref.p(%c0) %q0 - mqtref.rx(%c0) %q0 - mqtref.ry(%c0) %q0 - mqtref.rz(%c0) %q0 - mqtref.r(%c0, %c1) %q0 - return - } -} -// ----- -// This test checks if the multiple qubit rotation gates are correctly converted -module { - // CHECK-LABEL: llvm.func @testMultipleQubitRotationOp() - func.func @testMultipleQubitRotationOp() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: %[[c_1:.*]] = llvm.mlir.constant(1.000000e-01 : f64) : f64 - // CHECK: llvm.call @__quantum__qis__rxx__body(%[[q_0:.*]], %[[q_1:.*]], %[[c_0]]) : (!llvm.ptr, !llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__ryy__body(%[[q_0]], %[[q_1]], %[[c_0]]) : (!llvm.ptr, !llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__rzz__body(%[[q_0]], %[[q_1]], %[[c_0]]) : (!llvm.ptr, !llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__rzx__body(%[[q_0]], %[[q_1]], %[[c_0]]) : (!llvm.ptr, !llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__xx_minus_yy__body(%[[q_0]], %[[q_1]], %[[c_0]], %[[c_1]]) : (!llvm.ptr, !llvm.ptr, f64, f64) -> () - // CHECK: llvm.call @__quantum__qis__xx_plus_yy__body(%[[q_0]], %[[q_1]], %[[c_0]], %[[c_1]]) : (!llvm.ptr, !llvm.ptr, f64, f64) -> () - - %c0 = arith.constant 3.000000e-01 : f64 - %c1 = arith.constant 1.000000e-01 : f64 - %q0 = mqtref.allocQubit - %q1 = mqtref.allocQubit - mqtref.rxx(%c0) %q0, %q1 - mqtref.ryy(%c0) %q0, %q1 - mqtref.rzz(%c0) %q0, %q1 - mqtref.rzx(%c0) %q0, %q1 - mqtref.xx_minus_yy(%c0, %c1) %q0, %q1 - mqtref.xx_plus_yy(%c0, %c1) %q0, %q1 - mqtref.deallocQubit %q0 - mqtref.deallocQubit %q1 - return - } -} - -// ----- -// This test checks if the multiple qubit rotation gates using static qubits are correctly converted -module { - // CHECK-LABEL: llvm.func @testMultipleQubitRotationOpStatic() - func.func @testMultipleQubitRotationOpStatic() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: %[[c_1:.*]] = llvm.mlir.constant(1.000000e-01 : f64) : f64 - // CHECK: llvm.call @__quantum__qis__rxx__body(%[[q_0:.*]], %[[q_1:.*]], %[[c_0]]) : (!llvm.ptr, !llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__ryy__body(%[[q_0]], %[[q_1]], %[[c_0]]) : (!llvm.ptr, !llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__rzz__body(%[[q_0]], %[[q_1]], %[[c_0]]) : (!llvm.ptr, !llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__rzx__body(%[[q_0]], %[[q_1]], %[[c_0]]) : (!llvm.ptr, !llvm.ptr, f64) -> () - // CHECK: llvm.call @__quantum__qis__xx_minus_yy__body(%[[q_0]], %[[q_1]], %[[c_0]], %[[c_1]]) : (!llvm.ptr, !llvm.ptr, f64, f64) -> () - // CHECK: llvm.call @__quantum__qis__xx_plus_yy__body(%[[q_0]], %[[q_1]], %[[c_0]], %[[c_1]]) : (!llvm.ptr, !llvm.ptr, f64, f64) -> () - - %c0 = arith.constant 3.000000e-01 : f64 - %c1 = arith.constant 1.000000e-01 : f64 - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - mqtref.rxx(%c0) %q0, %q1 - mqtref.ryy(%c0) %q0, %q1 - mqtref.rzz(%c0) %q0, %q1 - mqtref.rzx(%c0) %q0, %q1 - mqtref.xx_minus_yy(%c0, %c1) %q0, %q1 - mqtref.xx_plus_yy(%c0, %c1) %q0, %q1 - return - } -} -// ----- -// This test checks if controlled gates are correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertControlledOp() - func.func @testConvertControlledOp() attributes {passthrough = ["entry_point"]} { - // CHECK: llvm.call @__quantum__qis__cx__body(%[[q_1:.*]], %[[q_0:.*]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__ccx__body(%[[q_1]], %[[q_2:.*]], %[[q_0]]) : (!llvm.ptr, !llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__x__body(%[[q_1]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__cz__body(%[[q_1]], %[[q_0]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__x__body(%[[q_1]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__x__body(%[[q_1]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__ccx__body(%[[q_2]], %[[q_1]], %[[q_0]]) : (!llvm.ptr, !llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__x__body(%[[q_1]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__crx__body(%[[q_1]], %[[q_0]], %[[c_0:.*]]) : (!llvm.ptr, !llvm.ptr, f64) -> () - - %c0 = arith.constant 3.000000e-01 : f64 - %q0 = mqtref.allocQubit - %q1 = mqtref.allocQubit - %q2 = mqtref.allocQubit - mqtref.x() %q0 ctrl %q1 - mqtref.x() %q0 ctrl %q1, %q2 - mqtref.z() %q0 nctrl %q1 - mqtref.x() %q0 ctrl %q2 nctrl %q1 - mqtref.rx(%c0) %q0 ctrl %q1 - mqtref.deallocQubit %q0 - mqtref.deallocQubit %q1 - mqtref.deallocQubit %q2 - return - } -} - -// ----- -// This test checks if controlled gates using static qubits are correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertControlledOpStatic() - func.func @testConvertControlledOpStatic() attributes {passthrough = ["entry_point"]} { - // CHECK: llvm.call @__quantum__qis__cx__body(%[[q_1:.*]], %[[q_0:.*]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__ccx__body(%[[q_1]], %[[q_2:.*]], %[[q_0]]) : (!llvm.ptr, !llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__x__body(%[[q_1]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__cz__body(%[[q_1]], %[[q_0]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__x__body(%[[q_1]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__x__body(%[[q_1]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__ccx__body(%[[q_2]], %[[q_1]], %[[q_0]]) : (!llvm.ptr, !llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__x__body(%[[q_1]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__crx__body(%[[q_1]], %[[q_0]], %[[c_0:.*]]) : (!llvm.ptr, !llvm.ptr, f64) -> () - - %c0 = arith.constant 3.000000e-01 : f64 - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - %q2 = mqtref.qubit 2 - mqtref.x() %q0 ctrl %q1 - mqtref.x() %q0 ctrl %q1, %q2 - mqtref.z() %q0 nctrl %q1 - mqtref.x() %q0 ctrl %q2 nctrl %q1 - mqtref.rx(%c0) %q0 ctrl %q1 - return - } -} -// ----- -// This test checks if static params are converted correctly -module { - // CHECK-LABEL: llvm.func @testConvertStaticParams() - func.func @testConvertStaticParams() attributes {passthrough = ["entry_point"]} { - // CHECK-DAG: %[[c_0:.*]] = llvm.mlir.constant(1.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_1:.*]] = llvm.mlir.constant(2.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_2:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_3:.*]] = llvm.mlir.constant(4.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_4:.*]] = llvm.mlir.constant(5.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_5:.*]] = llvm.mlir.constant(6.000000e-01 : f64) : f64 - // CHECK: llvm.call @__quantum__qis__u3__body(%[[q_0:.*]], %[[c_0]], %[[c_1]], %[[c_2]]) : (!llvm.ptr, f64, f64, f64) -> () - // CHECK: llvm.call @__quantum__qis__u3__body(%[[q_0:.*]], %[[c_3]], %[[c_4]], %[[c_5]]) : (!llvm.ptr, f64, f64, f64) -> () - - %q0 = mqtref.allocQubit - mqtref.u(static [1.00000e-01, 2.00000e-01, 3.00000e-01] mask [true, true, true]) %q0 - mqtref.u(static [4.00000e-01, 5.00000e-01, 6.00000e-01]) %q0 - mqtref.deallocQubit %q0 - return - } -} - -// ----- -// This test checks if static params using static qubits are converted correctly -module { - // CHECK-LABEL: llvm.func @testConvertStaticParamsStatic() - func.func @testConvertStaticParamsStatic() attributes {passthrough = ["entry_point"]} { - // CHECK-DAG: %[[c_0:.*]] = llvm.mlir.constant(1.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_1:.*]] = llvm.mlir.constant(2.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_2:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_3:.*]] = llvm.mlir.constant(4.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_4:.*]] = llvm.mlir.constant(5.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_5:.*]] = llvm.mlir.constant(6.000000e-01 : f64) : f64 - // CHECK: llvm.call @__quantum__qis__u3__body(%[[q_0:.*]], %[[c_0]], %[[c_1]], %[[c_2]]) : (!llvm.ptr, f64, f64, f64) -> () - // CHECK: llvm.call @__quantum__qis__u3__body(%[[q_0:.*]], %[[c_3]], %[[c_4]], %[[c_5]]) : (!llvm.ptr, f64, f64, f64) -> () - - %q0 = mqtref.qubit 0 - mqtref.u(static [1.00000e-01, 2.00000e-01, 3.00000e-01] mask [true, true, true]) %q0 - mqtref.u(static [4.00000e-01, 5.00000e-01, 6.00000e-01]) %q0 - return - } -} - -// ----- -// This test checks if mixed static params are converted correctly -module { - // CHECK-LABEL: llvm.func @testConvertMixedParams() - func.func @testConvertMixedParams() attributes {passthrough = ["entry_point"]} { - // CHECK-DAG: %[[c_0:.*]] = llvm.mlir.constant(1.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_1:.*]] = llvm.mlir.constant(2.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_2:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_3:.*]] = llvm.mlir.constant(4.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_4:.*]] = llvm.mlir.constant(5.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_5:.*]] = llvm.mlir.constant(6.000000e-01 : f64) : f64 - // CHECK: llvm.call @__quantum__qis__u3__body(%[[q_0:.*]], %[[c_0]], %[[c_1]], %[[c_2]]) : (!llvm.ptr, f64, f64, f64) -> () - // CHECK: llvm.call @__quantum__qis__u3__body(%[[q_0:.*]], %[[c_3]], %[[c_4]], %[[c_5]]) : (!llvm.ptr, f64, f64, f64) -> () - - %q0 = mqtref.allocQubit - %c0 = arith.constant 1.000000e-01 : f64 - %c1 = arith.constant 3.000000e-01 : f64 - %c2 = arith.constant 4.000000e-01 : f64 - mqtref.u(%c0, %c1 static [2.000000e-01] mask [false, true, false]) %q0 - mqtref.u(%c2 static [5.00000e-01, 6.00000e-01] mask [false, true, true]) %q0 - mqtref.deallocQubit %q0 - return - } -} - -// ----- -// This test checks if mixed static params using static qubits are converted correctly -module { - // CHECK-LABEL: llvm.func @testConvertMixedParams() - func.func @testConvertMixedParams() attributes {passthrough = ["entry_point"]} { - // CHECK-DAG: %[[c_0:.*]] = llvm.mlir.constant(1.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_1:.*]] = llvm.mlir.constant(2.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_2:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_3:.*]] = llvm.mlir.constant(4.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_4:.*]] = llvm.mlir.constant(5.000000e-01 : f64) : f64 - // CHECK-DAG: %[[c_5:.*]] = llvm.mlir.constant(6.000000e-01 : f64) : f64 - // CHECK: llvm.call @__quantum__qis__u3__body(%[[q_0:.*]], %[[c_0]], %[[c_1]], %[[c_2]]) : (!llvm.ptr, f64, f64, f64) -> () - // CHECK: llvm.call @__quantum__qis__u3__body(%[[q_0:.*]], %[[c_3]], %[[c_4]], %[[c_5]]) : (!llvm.ptr, f64, f64, f64) -> () - - %q0 = mqtref.qubit 0 - %c0 = arith.constant 1.000000e-01 : f64 - %c1 = arith.constant 3.000000e-01 : f64 - %c2 = arith.constant 4.000000e-01 : f64 - mqtref.u(%c0, %c1 static [2.000000e-01] mask [false, true, false]) %q0 - mqtref.u(%c2 static [5.00000e-01, 6.00000e-01] mask [false, true, true]) %q0 - return - } -} - -// ----- -// This test checks if the gphase operation is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertGPhaseOp() - func.func @testConvertGPhaseOp() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: llvm.call @__quantum__qis__gphase__body(%[[c_0]]) : (f64) -> () - - %cst = arith.constant 3.000000e-01 : f64 - mqtref.gphase(%cst) - return - } -} -// ----- -// This test checks if the controlled gphase operation is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertGPhaseOpControlled() - func.func @testConvertGPhaseOpControlled() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: llvm.call @__quantum__qis__cgphase__body(%[[ANY:.*]], %[[c_0]]) : (!llvm.ptr, f64) -> () - - %cst = arith.constant 3.000000e-01 : f64 - %q0 = mqtref.allocQubit - mqtref.gphase(%cst) ctrl %q0 - mqtref.deallocQubit %q0 - return - } -} - -// ----- -// This test checks if the controlled gphase operation using static qubits is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertGPhaseOpControlled() - func.func @testConvertGPhaseOpControlled() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: llvm.call @__quantum__qis__cgphase__body(%[[ANY:.*]], %[[c_0]]) : (!llvm.ptr, f64) -> () - - %cst = arith.constant 3.000000e-01 : f64 - %q0 = mqtref.qubit 0 - mqtref.gphase(%cst) ctrl %q0 - return - } -} -// ----- -// This test checks if the barrierOp is converted correctly -module { - // CHECK-LABEL: llvm.func @testConvertBarrierOp() - func.func @testConvertBarrierOp() attributes {passthrough = ["entry_point"]} { - // CHECK: llvm.call @__quantum__qis__barrier__body(%[[ANY:.*]]) : (!llvm.ptr) -> () - - %q0 = mqtref.allocQubit - mqtref.barrier() %q0 - mqtref.deallocQubit %q0 - return - } -} - -// ----- -// This test checks if the barrierOp using static qubits is converted correctly -module { - // CHECK-LABEL: llvm.func @testConvertBarrierOpStatic() - func.func @testConvertBarrierOpStatic() attributes {passthrough = ["entry_point"]} { - // CHECK: llvm.call @__quantum__qis__barrier__body(%[[ANY:.*]]) : (!llvm.ptr) -> () - - %q0 = mqtref.qubit 0 - mqtref.barrier() %q0 - return - } -} - -// ----- -// This test checks if the operations are moved correctly to the correct blocks during the conversion -module { - // CHECK: llvm.mlir.global internal constant @mlir.llvm.nameless_global_0("r0\00") {addr_space = 0 : i32, dso_local} - - // CHECK-LABEL: llvm.func @testOperationMovement() - func.func @testOperationMovement() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[a_0:.*]] = llvm.mlir.addressof @mlir.llvm.nameless_global_0 : !llvm.ptr - // CHECK: %[[ptr_0:.*]] = llvm.mlir.zero : !llvm.ptr - // CHECK: llvm.call @__quantum__rt__initialize(%[[ptr_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.br ^[[main:.*]] - // CHECK: ^[[main]]: - // CHECK: %[[q_0:.*]] = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - // CHECK: %[[q_1:.*]] = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - // CHECK: llvm.call @__quantum__qis__h__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__h__body(%[[q_1]]) : (!llvm.ptr) -> () - // CHECK: llvm.br ^[[main2:.*]] - // CHECK: ^[[main2]]: - // CHECK: llvm.call @__quantum__qis__mz__body(%[[q_0]], %[[ptr_0]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__rt__qubit_release(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__reset__body(%[[q_1]]) : (!llvm.ptr) -> () - // CHECK: llvm.br ^[[end:.*]] - // CHECK: ^[[end]]: - // CHECK: llvm.call @__quantum__rt__result_record_output(%[[ptr_0]], %[[a_0]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.return - - %q0 = mqtref.allocQubit - %q1 = mqtref.allocQubit - %mem = memref.alloca() : memref<1xi1> - mqtref.h() %q0 - %m0 = mqtref.measure %q0 - %c0 = arith.constant 0 : index - memref.store %m0, %mem[%c0] : memref<1xi1> - mqtref.deallocQubit %q0 - mqtref.h() %q1 - mqtref.reset %q1 - mqtref.deallocQubit %q1 - return - } -} - -// ----- -// This test checks if a Bell state is converted correctly. -module { - // CHECK: llvm.mlir.global internal constant @mlir.llvm.nameless_global_1("r1\00") {addr_space = 0 : i32, dso_local} - // CHECK: llvm.mlir.global internal constant @mlir.llvm.nameless_global_0("r0\00") {addr_space = 0 : i32, dso_local} - - // CHECK-LABEL: llvm.func @bellState() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "2"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} - func.func @bellState() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[a_1:.*]] = llvm.mlir.addressof @mlir.llvm.nameless_global_1 : !llvm.ptr - // CHECK: %[[a_0:.*]] = llvm.mlir.addressof @mlir.llvm.nameless_global_0 : !llvm.ptr - // CHECK: %[[ptr_0:.*]] = llvm.mlir.zero : !llvm.ptr - // CHECK: llvm.call @__quantum__rt__initialize(%[[ptr_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.br ^[[main:.*]] - // CHECK: ^[[main]]: - // CHECK: %[[q_0:.*]] = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - // CHECK: %[[q_1:.*]] = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - // CHECK: llvm.call @__quantum__qis__h__body(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__cx__body(%[[q_0]], %[[q_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.br ^[[main2:.*]] - // CHECK: ^[[main2]]: - // CHECK: llvm.call @__quantum__qis__mz__body(%[[q_0]], %[[ptr_0]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(1 : i64) : i64 - // CHECK: %[[ptr_1:.*]] = llvm.inttoptr %[[c_0]] : i64 to !llvm.ptr - // CHECK: llvm.call @__quantum__qis__mz__body(%[[q_1]], %[[ptr_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__rt__qubit_release(%[[q_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__rt__qubit_release(%[[q_1]]) : (!llvm.ptr) -> () - // CHECK: llvm.br ^[[end:.*]] - // CHECK: ^[[end]]: - // CHECK: llvm.call @__quantum__rt__result_record_output(%[[ptr_1]], %[[a_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__rt__result_record_output(%[[ptr_0]], %[[a_0]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.return - - %q0 = mqtref.allocQubit - %q1 = mqtref.allocQubit - %mem = memref.alloca() : memref<2xi1> - mqtref.h() %q0 - mqtref.x() %q1 ctrl %q0 - %m0 = mqtref.measure %q0 - %c0 = arith.constant 0 : index - memref.store %m0, %mem[%c0] : memref<2xi1> - %m1 = mqtref.measure %q1 - %c1 = arith.constant 1 : index - memref.store %m1, %mem[%c1] : memref<2xi1> - mqtref.deallocQubit %q0 - mqtref.deallocQubit %q1 - return - } -} - -// ----- -// This test checks if a Bell state using static qubits is converted correctly. -module { - // CHECK: llvm.mlir.global internal constant @mlir.llvm.nameless_global_1("r1\00") {addr_space = 0 : i32, dso_local} - // CHECK: llvm.mlir.global internal constant @mlir.llvm.nameless_global_0("r0\00") {addr_space = 0 : i32, dso_local} - - // CHECK-LABEL: llvm.func @bellStateStaticQubit() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "2"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "false"], ["dynamic_result_management", "false"]]} - func.func @bellStateStaticQubit() attributes {passthrough = ["entry_point"]} { - // CHECK: %[[a_1:.*]] = llvm.mlir.addressof @mlir.llvm.nameless_global_1 : !llvm.ptr - // CHECK: %[[a_0:.*]] = llvm.mlir.addressof @mlir.llvm.nameless_global_0 : !llvm.ptr - // CHECK: %[[ptr_0:.*]] = llvm.mlir.zero : !llvm.ptr - // CHECK: llvm.call @__quantum__rt__initialize(%[[ptr_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.br ^[[main:.*]] - // CHECK: ^[[main]]: - // CHECK: %[[index_0:.*]] = llvm.mlir.constant(1 : i64) : i64 - // CHECK: %[[ptr_1:.*]] = llvm.inttoptr %[[index_0]] : i64 to !llvm.ptr - // CHECK: llvm.call @__quantum__qis__h__body(%[[ptr_0]]) : (!llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__cx__body(%[[ptr_0]], %[[ptr_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.br ^[[main2:.*]] - // CHECK: ^[[main2]]: - // CHECK: llvm.call @__quantum__qis__mz__body(%[[ptr_0]], %[[ptr_0]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__qis__mz__body(%[[ptr_1]], %[[ptr_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.br ^[[end:.*]] - // CHECK: ^[[end]]: - // CHECK: llvm.call @__quantum__rt__result_record_output(%[[ptr_1]], %[[a_1]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.call @__quantum__rt__result_record_output(%[[ptr_0]], %[[a_0]]) : (!llvm.ptr, !llvm.ptr) -> () - // CHECK: llvm.return - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - %mem = memref.alloca() : memref<2xi1> - mqtref.h() %q0 - mqtref.x() %q1 ctrl %q0 - %m0 = mqtref.measure %q0 - %c0 = arith.constant 0 : index - memref.store %m0, %mem[%c0] : memref<2xi1> - %m1 = mqtref.measure %q1 - %c1 = arith.constant 1 : index - memref.store %m1, %mem[%c1] : memref<2xi1> - return - } -} diff --git a/mlir/test/Conversion/qir-to-mqtref.mlir b/mlir/test/Conversion/qir-to-mqtref.mlir deleted file mode 100644 index 9a2ff1e796..0000000000 --- a/mlir/test/Conversion/qir-to-mqtref.mlir +++ /dev/null @@ -1,1070 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --qir-to-mqtref | FileCheck %s - -// This test checks if the alloc register call is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertAllocRegister() - llvm.func @testConvertAllocRegister() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[Int:.*]] = llvm.mlir.constant(14 : i64) : i64 - // CHECK: %[[Idx:.*]] = arith.index_cast %[[Int]] : i64 to index - // CHECK: %[[r_0:.*]] = memref.alloc(%[[Idx]]) : memref - - %0 = llvm.mlir.zero : !llvm.ptr - %c0 = llvm.mlir.constant(14 : i64) : i64 - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %r = llvm.call @__quantum__rt__qubit_allocate_array(%c0) : (i64) -> !llvm.ptr - llvm.br ^bb2 - ^bb2: - llvm.br ^bb3 - ^bb3: - llvm.return - } - - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate_array(i64) -> !llvm.ptr -} - -// ----- -// This test checks if the dealloc register call is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertDeallocRegister() - llvm.func @testConvertDeallocRegister() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[Int:.*]] = llvm.mlir.constant(14 : i64) : i64 - // CHECK: %[[Idx:.*]] = arith.index_cast %[[Int]] : i64 to index - // CHECK: %[[r_0:.*]] = memref.alloc(%[[Idx]]) : memref - // CHECK: memref.dealloc %[[r_0:.*]] : memref - - %0 = llvm.mlir.zero : !llvm.ptr - %c0 = llvm.mlir.constant(14 : i64) : i64 - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %r0 = llvm.call @__quantum__rt__qubit_allocate_array(%c0) : (i64) -> !llvm.ptr - llvm.call @__quantum__rt__qubit_release_array(%r0) : (!llvm.ptr) -> () - llvm.br ^bb2 - ^bb2: - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__rt__qubit_release_array(!llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate_array(i64) -> !llvm.ptr -} - -// ----- -// This test checks if the extract from register call is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertExtractFromRegister() - llvm.func @testConvertExtractFromRegister() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[Int1:.*]] = llvm.mlir.constant(1 : i64) : i64 - // CHECK: %[[Int0:.*]] = llvm.mlir.constant(0 : i64) : i64 - // CHECK: %[[Idx1:.*]] = arith.index_cast %[[Int1]] : i64 to index - // CHECK: %[[r_0:.*]] = memref.alloc(%[[Idx1]]) : memref - // CHECK: %[[Idx0:.*]] = arith.index_cast %[[Int0]] : i64 to index - // CHECK: %[[q_0:.*]] = memref.load %[[r_0]][%[[Idx0]]] : memref - - %0 = llvm.mlir.zero : !llvm.ptr - %c0 = llvm.mlir.constant(1 : i64) : i64 - %c1 = llvm.mlir.constant(0 : i64) : i64 - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %r0 = llvm.call @__quantum__rt__qubit_allocate_array(%c0) : (i64) -> !llvm.ptr - %ptr1 = llvm.call @__quantum__rt__array_get_element_ptr_1d(%r0, %c1) : (!llvm.ptr, i64) -> !llvm.ptr - %q0 = llvm.load %ptr1 : !llvm.ptr -> !llvm.ptr - llvm.br ^bb2 - ^bb2: - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__rt__array_get_element_ptr_1d(!llvm.ptr, i64) -> !llvm.ptr - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate_array(i64) -> !llvm.ptr -} - -// ----- -// This test checks if the alloc qubit call is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertAllocateQubit() - llvm.func @testConvertAllocateQubit() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: %[[q_1:.*]] = mqtref.allocQubit - - %0 = llvm.mlir.zero : !llvm.ptr - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %q0 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - %q1 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - llvm.br ^bb2 - ^bb2: - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate() -> !llvm.ptr -} - -// ----- -// This test checks if the dealloc qubit call is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertDeallocateQubit() - llvm.func @testConvertDeallocateQubit() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: %[[q_1:.*]] = mqtref.allocQubit - // CHECK: mqtref.deallocQubit %[[q_0]] - // CHECK: mqtref.deallocQubit %[[q_1]] - - %0 = llvm.mlir.zero : !llvm.ptr - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %q0 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - %q1 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__rt__qubit_release(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__rt__qubit_release(%q1) : (!llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__rt__qubit_release(!llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate() -> !llvm.ptr -} - -// ----- -// This test checks if the reset operation is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertResetOp() - llvm.func @testConvertResetOp() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: mqtref.reset %[[q_0]] - - %0 = llvm.mlir.zero : !llvm.ptr - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %q0 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - llvm.call @__quantum__qis__reset__body(%q0) : (!llvm.ptr) -> () - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__rt__qubit_release(%q0) : (!llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__reset__body(!llvm.ptr) - llvm.func @__quantum__rt__qubit_release(!llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate() -> !llvm.ptr -} - -// ----- -// This test checks if the reset operation using static qubits is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertResetOpStatic() - llvm.func @testConvertResetOpStatic() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.qubit 0 - // CHECK: mqtref.reset %[[q_0]] - - %0 = llvm.mlir.zero : !llvm.ptr - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - llvm.call @__quantum__qis__reset__body(%0) : (!llvm.ptr) -> () - llvm.br ^bb2 - ^bb2: - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__reset__body(!llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) -} - -// ----- -// This test checks if the measure operation is correctly converted -module { - llvm.mlir.global internal constant @mlir.llvm.nameless_global_0("r0\00") {addr_space = 0 : i32, dso_local} - // CHECK-LABEL: llvm.func @testConvertMeasure() - llvm.func @testConvertMeasure() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "1"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[mem:.*]] = memref.alloca() : memref<1xi1> - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: %[[m_0:.*]] = mqtref.measure %[[q_0]] - // CHECK: %[[c_0:.*]] = arith.constant 0 : index - // CHECK: memref.store %[[m_0]], %[[mem]][%[[c_0]]] : memref<1xi1> - - %0 = llvm.mlir.zero : !llvm.ptr - %a0 = llvm.mlir.addressof @mlir.llvm.nameless_global_0 : !llvm.ptr - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %q0 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__qis__mz__body(%q0, %0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__rt__qubit_release(%q0) : (!llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.call @__quantum__rt__result_record_output(%0, %a0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.return - } - llvm.func @__quantum__qis__mz__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__result_record_output(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_release(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate() -> !llvm.ptr -} - -// ----- -// This test checks if the measure operation using static qubits is correctly converted -module { - llvm.mlir.global internal constant @mlir.llvm.nameless_global_0("r0\00") {addr_space = 0 : i32, dso_local} - // CHECK-LABEL: llvm.func @testConvertMeasureStatic() - llvm.func @testConvertMeasureStatic() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "1"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[mem:.*]] = memref.alloca() : memref<1xi1> - // CHECK: %[[q_0:.*]] = mqtref.qubit 0 - // CHECK: %[[m_0:.*]] = mqtref.measure %[[q_0]] - // CHECK: %[[c_0:.*]] = arith.constant 0 : index - // CHECK: memref.store %[[m_0]], %[[mem]][%[[c_0]]] : memref<1xi1> - - %0 = llvm.mlir.zero : !llvm.ptr - %a0 = llvm.mlir.addressof @mlir.llvm.nameless_global_0 : !llvm.ptr - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__qis__mz__body(%0, %0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.call @__quantum__rt__result_record_output(%0, %a0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.return - } - llvm.func @__quantum__qis__mz__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__result_record_output(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) -} - -// ----- -// This test checks if the measure operation is correctly converted that store the classical result on the same index -module { - llvm.mlir.global internal constant @mlir.llvm.nameless_global_0("r0\00") {addr_space = 0 : i32, dso_local} - // CHECK-LABEL: llvm.func @testConvertMeasureOnSameIndex() - llvm.func @testConvertMeasureOnSameIndex() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "1"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[mem:.*]] = memref.alloca() : memref<1xi1> - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: %[[q_1:.*]] = mqtref.allocQubit - // CHECK: %[[m_0:.*]] = mqtref.measure %[[q_0]] - // CHECK: %[[m_1:.*]] = mqtref.measure %[[q_1]] - // CHECK: %[[c_0:.*]] = arith.constant 0 : index - // CHECK: memref.store %[[m_0]], %[[mem]][%[[c_0]]] : memref<1xi1> - // CHECK: %[[c_1:.*]] = arith.constant 0 : index - // CHECK: memref.store %[[m_1]], %[[mem]][%[[c_1]]] : memref<1xi1> - - %ptr0 = llvm.mlir.zero : !llvm.ptr - %c0 = llvm.mlir.constant(1 : i64) : i64 - %ptr1 = llvm.inttoptr %c0 : i64 to !llvm.ptr - %a0 = llvm.mlir.addressof @mlir.llvm.nameless_global_0 : !llvm.ptr - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %q0 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - %q1 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__qis__mz__body(%q0, %ptr0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__mz__body(%q1, %ptr1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__rt__qubit_release(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__rt__qubit_release(%q1) : (!llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.call @__quantum__rt__result_record_output(%ptr0, %a0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__rt__result_record_output(%ptr1, %a0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.return - } - llvm.func @__quantum__qis__mz__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__result_record_output(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_release(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate() -> !llvm.ptr -} - -// ----- -// This test checks if the measure operation using static qubits is correctly converted that store the classical result on the same index -module { - llvm.mlir.global internal constant @mlir.llvm.nameless_global_0("r0\00") {addr_space = 0 : i32, dso_local} - // CHECK-LABEL: llvm.func @testConvertMeasureOnSameIndexStatic() - llvm.func @testConvertMeasureOnSameIndexStatic() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "1"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[mem:.*]] = memref.alloca() : memref<1xi1> - // CHECK: %[[q_0:.*]] = mqtref.qubit 0 - // CHECK: %[[q_1:.*]] = mqtref.qubit 1 - // CHECK: %[[m_0:.*]] = mqtref.measure %[[q_0]] - // CHECK: %[[m_1:.*]] = mqtref.measure %[[q_1]] - // CHECK: %[[c_0:.*]] = arith.constant 0 : index - // CHECK: memref.store %[[m_0]], %[[mem]][%[[c_0]]] : memref<1xi1> - // CHECK: %[[c_1:.*]] = arith.constant 0 : index - // CHECK: memref.store %[[m_1]], %[[mem]][%[[c_1]]] : memref<1xi1> - - %ptr0 = llvm.mlir.zero : !llvm.ptr - %c0 = llvm.mlir.constant(1 : i64) : i64 - %ptr1 = llvm.inttoptr %c0 : i64 to !llvm.ptr - %a0 = llvm.mlir.addressof @mlir.llvm.nameless_global_0 : !llvm.ptr - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__qis__mz__body(%ptr0, %ptr0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__mz__body(%ptr1, %ptr1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.call @__quantum__rt__result_record_output(%ptr0, %a0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__rt__result_record_output(%ptr1, %a0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.return - } - llvm.func @__quantum__qis__mz__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__result_record_output(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) -} - -// ----- -// This test checks if the single qubit gates are correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertSingleQubitOp() - llvm.func @testConvertSingleQubitOp() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: mqtref.h() %[[q_0]] - // CHECK: mqtref.i() %[[q_0]] - // CHECK: mqtref.x() %[[q_0]] - // CHECK: mqtref.y() %[[q_0]] - // CHECK: mqtref.z() %[[q_0]] - // CHECK: mqtref.s() %[[q_0]] - // CHECK: mqtref.sdg() %[[q_0]] - // CHECK: mqtref.t() %[[q_0]] - // CHECK: mqtref.tdg() %[[q_0]] - // CHECK: mqtref.v() %[[q_0]] - // CHECK: mqtref.vdg() %[[q_0]] - // CHECK: mqtref.sx() %[[q_0]] - // CHECK: mqtref.sxdg() %[[q_0]] - - %ptr0 = llvm.mlir.zero : !llvm.ptr - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %q0 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - llvm.call @__quantum__qis__h__body(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__i__body(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__x__body(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__y__body(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__z__body(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__s__body(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__sdg__body(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__t__body(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__tdg__body(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__v__body(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__vdg__body(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__sx__body(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__sxdg__body(%q0) : (!llvm.ptr) -> () - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__rt__qubit_release(%q0) : (!llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__h__body(!llvm.ptr) - llvm.func @__quantum__qis__i__body(!llvm.ptr) - llvm.func @__quantum__qis__x__body(!llvm.ptr) - llvm.func @__quantum__qis__y__body(!llvm.ptr) - llvm.func @__quantum__qis__z__body(!llvm.ptr) - llvm.func @__quantum__qis__s__body(!llvm.ptr) - llvm.func @__quantum__qis__sdg__body(!llvm.ptr) - llvm.func @__quantum__qis__t__body(!llvm.ptr) - llvm.func @__quantum__qis__tdg__body(!llvm.ptr) - llvm.func @__quantum__qis__v__body(!llvm.ptr) - llvm.func @__quantum__qis__vdg__body(!llvm.ptr) - llvm.func @__quantum__qis__sx__body(!llvm.ptr) - llvm.func @__quantum__qis__sxdg__body(!llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate() -> !llvm.ptr - llvm.func @__quantum__rt__qubit_release(!llvm.ptr) -> () -} - -// ----- -// This test checks if the single qubit gates using static qubits are correctly -module { - // CHECK-LABEL: llvm.func @testConvertSingleQubitOpStatic() - llvm.func @testConvertSingleQubitOpStatic() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.qubit 0 - // CHECK: mqtref.h() %[[q_0]] - // CHECK: mqtref.i() %[[q_0]] - // CHECK: mqtref.x() %[[q_0]] - // CHECK: mqtref.y() %[[q_0]] - // CHECK: mqtref.z() %[[q_0]] - // CHECK: mqtref.s() %[[q_0]] - // CHECK: mqtref.sdg() %[[q_0]] - // CHECK: mqtref.t() %[[q_0]] - // CHECK: mqtref.tdg() %[[q_0]] - // CHECK: mqtref.v() %[[q_0]] - // CHECK: mqtref.vdg() %[[q_0]] - // CHECK: mqtref.sx() %[[q_0]] - // CHECK: mqtref.sxdg() %[[q_0]] - - %0 = llvm.mlir.zero : !llvm.ptr - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - llvm.call @__quantum__qis__h__body(%0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__i__body(%0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__x__body(%0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__y__body(%0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__z__body(%0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__s__body(%0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__sdg__body(%0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__t__body(%0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__tdg__body(%0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__v__body(%0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__vdg__body(%0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__sx__body(%0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__sxdg__body(%0) : (!llvm.ptr) -> () - llvm.br ^bb2 - ^bb2: - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__h__body(!llvm.ptr) - llvm.func @__quantum__qis__i__body(!llvm.ptr) - llvm.func @__quantum__qis__x__body(!llvm.ptr) - llvm.func @__quantum__qis__y__body(!llvm.ptr) - llvm.func @__quantum__qis__z__body(!llvm.ptr) - llvm.func @__quantum__qis__s__body(!llvm.ptr) - llvm.func @__quantum__qis__sdg__body(!llvm.ptr) - llvm.func @__quantum__qis__t__body(!llvm.ptr) - llvm.func @__quantum__qis__tdg__body(!llvm.ptr) - llvm.func @__quantum__qis__v__body(!llvm.ptr) - llvm.func @__quantum__qis__vdg__body(!llvm.ptr) - llvm.func @__quantum__qis__sx__body(!llvm.ptr) - llvm.func @__quantum__qis__sxdg__body(!llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) -} - -// ----- -// This test checks if the two target gates are correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertTwoTargetOp() - llvm.func @testConvertTwoTargetOp() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: %[[q_1:.*]] = mqtref.allocQubit - // CHECK: mqtref.swap() %[[q_0]], %[[q_1]] - // CHECK: mqtref.iswap() %[[q_0]], %[[q_1]] - // CHECK: mqtref.iswapdg() %[[q_0]], %[[q_1]] - // CHECK: mqtref.peres() %[[q_0]], %[[q_1]] - // CHECK: mqtref.peresdg() %[[q_0]], %[[q_1]] - // CHECK: mqtref.dcx() %[[q_0]], %[[q_1]] - // CHECK: mqtref.ecr() %[[q_0]], %[[q_1]] - - %ptr0 = llvm.mlir.zero : !llvm.ptr - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %q0 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - %q1 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - llvm.call @__quantum__qis__swap__body(%q0, %q1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__iswap__body(%q0, %q1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__iswapdg__body(%q0, %q1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__peres__body(%q0, %q1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__peresdg__body(%q0, %q1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__dcx__body(%q0, %q1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__ecr__body(%q0, %q1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__rt__qubit_release(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__rt__qubit_release(%q1) : (!llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__swap__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__iswap__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__iswapdg__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__peres__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__peresdg__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__dcx__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__ecr__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate() -> !llvm.ptr - llvm.func @__quantum__rt__qubit_release(!llvm.ptr) -> () -} - -// ----- -// This test checks if the two target gates using static qubits are correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertTwoTargetOpStatic() - llvm.func @testConvertTwoTargetOpStatic() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.qubit 0 - // CHECK: %[[q_1:.*]] = mqtref.qubit 1 - // CHECK: mqtref.swap() %[[q_0]], %[[q_1]] - // CHECK: mqtref.iswap() %[[q_0]], %[[q_1]] - // CHECK: mqtref.iswapdg() %[[q_0]], %[[q_1]] - // CHECK: mqtref.peres() %[[q_0]], %[[q_1]] - // CHECK: mqtref.peresdg() %[[q_0]], %[[q_1]] - // CHECK: mqtref.dcx() %[[q_0]], %[[q_1]] - // CHECK: mqtref.ecr() %[[q_0]], %[[q_1]] - - %0 = llvm.mlir.zero : !llvm.ptr - %c0 = llvm.mlir.constant(1 : i64) : i64 - %1 = llvm.inttoptr %c0 : i64 to !llvm.ptr - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - llvm.call @__quantum__qis__swap__body(%0, %1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__iswap__body(%0, %1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__iswapdg__body(%0, %1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__peres__body(%0, %1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__peresdg__body(%0, %1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__dcx__body(%0, %1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__ecr__body(%0, %1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.br ^bb2 - ^bb2: - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__swap__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__iswap__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__iswapdg__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__peres__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__peresdg__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__dcx__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__ecr__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) -} - -// ----- -// This test checks if the single qubit rotation gates are correctly converted -module { - // CHECK-LABEL: llvm.func @testSingleQubitRotationOp() - llvm.func @testSingleQubitRotationOp() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(1.000000e-01 : f64) : f64 - // CHECK: %[[c_1:.*]] = llvm.mlir.constant(2.000000e-01 : f64) : f64 - // CHECK: %[[c_2:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: mqtref.u2(%[[c_0]], %[[c_1]]) %[[q_0]] - // CHECK: mqtref.p(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.u(%[[c_0]], %[[c_1]], %[[c_2]]) %[[q_0]] - // CHECK: mqtref.p(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.rx(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.ry(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.rz(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.r(%[[c_0]], %[[c_1]]) %[[q_0]] - - %0 = llvm.mlir.zero : !llvm.ptr - %c2 = llvm.mlir.constant(1.000000e-01 : f64) : f64 - %c3 = llvm.mlir.constant(2.000000e-01 : f64) : f64 - %c4 = llvm.mlir.constant(3.000000e-01 : f64) : f64 - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %q0 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - llvm.call @__quantum__qis__u2__body(%q0, %c2, %c3) : (!llvm.ptr, f64, f64) -> () - llvm.call @__quantum__qis__u1__body(%q0, %c2) : (!llvm.ptr, f64) -> () - llvm.call @__quantum__qis__u3__body(%q0, %c2, %c3, %c4) : (!llvm.ptr, f64, f64, f64) -> () - llvm.call @__quantum__qis__p__body(%q0, %c2) : (!llvm.ptr, f64) -> () - llvm.call @__quantum__qis__rx__body(%q0, %c2) : (!llvm.ptr, f64) -> () - llvm.call @__quantum__qis__ry__body(%q0, %c2) : (!llvm.ptr, f64) -> () - llvm.call @__quantum__qis__rz__body(%q0, %c2) : (!llvm.ptr, f64) -> () - llvm.call @__quantum__qis__r__body(%q0, %c2, %c3) : (!llvm.ptr, f64, f64) -> () - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__rt__qubit_release(%q0) : (!llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__u1__body(!llvm.ptr, f64) - llvm.func @__quantum__qis__u2__body(!llvm.ptr, f64, f64) - llvm.func @__quantum__qis__u3__body(!llvm.ptr, f64, f64, f64) - llvm.func @__quantum__qis__p__body(!llvm.ptr, f64) - llvm.func @__quantum__qis__rx__body(!llvm.ptr, f64) - llvm.func @__quantum__qis__ry__body(!llvm.ptr, f64) - llvm.func @__quantum__qis__rz__body(!llvm.ptr, f64) - llvm.func @__quantum__qis__r__body(!llvm.ptr, f64, f64) - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate() -> !llvm.ptr - llvm.func @__quantum__rt__qubit_release(!llvm.ptr) -> () -} - -// ----- -// This test checks if the single qubit rotation gates using static qubitss are correctly converted -module { - // CHECK-LABEL: llvm.func @testSingleQubitRotationOpStatic() - llvm.func @testSingleQubitRotationOpStatic() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.qubit 0 - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(1.000000e-01 : f64) : f64 - // CHECK: %[[c_1:.*]] = llvm.mlir.constant(2.000000e-01 : f64) : f64 - // CHECK: %[[c_2:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: mqtref.u2(%[[c_0]], %[[c_1]]) %[[q_0]] - // CHECK: mqtref.p(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.u(%[[c_0]], %[[c_1]], %[[c_2]]) %[[q_0]] - // CHECK: mqtref.p(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.rx(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.ry(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.rz(%[[c_0]]) %[[q_0]] - // CHECK: mqtref.r(%[[c_0]], %[[c_1]]) %[[q_0]] - - %0 = llvm.mlir.zero : !llvm.ptr - %c2 = llvm.mlir.constant(1.000000e-01 : f64) : f64 - %c3 = llvm.mlir.constant(2.000000e-01 : f64) : f64 - %c4 = llvm.mlir.constant(3.000000e-01 : f64) : f64 - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - llvm.call @__quantum__qis__u2__body(%0, %c2, %c3) : (!llvm.ptr, f64, f64) -> () - llvm.call @__quantum__qis__u1__body(%0, %c2) : (!llvm.ptr, f64) -> () - llvm.call @__quantum__qis__u3__body(%0, %c2, %c3, %c4) : (!llvm.ptr, f64, f64, f64) -> () - llvm.call @__quantum__qis__p__body(%0, %c2) : (!llvm.ptr, f64) -> () - llvm.call @__quantum__qis__rx__body(%0, %c2) : (!llvm.ptr, f64) -> () - llvm.call @__quantum__qis__ry__body(%0, %c2) : (!llvm.ptr, f64) -> () - llvm.call @__quantum__qis__rz__body(%0, %c2) : (!llvm.ptr, f64) -> () - llvm.call @__quantum__qis__r__body(%0, %c2, %c3) : (!llvm.ptr, f64, f64) -> () - - llvm.br ^bb2 - ^bb2: - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__u1__body(!llvm.ptr, f64) - llvm.func @__quantum__qis__u2__body(!llvm.ptr, f64, f64) - llvm.func @__quantum__qis__u3__body(!llvm.ptr, f64, f64, f64) - llvm.func @__quantum__qis__p__body(!llvm.ptr, f64) - llvm.func @__quantum__qis__rx__body(!llvm.ptr, f64) - llvm.func @__quantum__qis__ry__body(!llvm.ptr, f64) - llvm.func @__quantum__qis__rz__body(!llvm.ptr, f64) - llvm.func @__quantum__qis__r__body(!llvm.ptr, f64, f64) - llvm.func @__quantum__rt__initialize(!llvm.ptr) -} - -// ----- -// This test checks if the multiple qubit rotation gates are correctly converted -module { - // CHECK-LABEL: llvm.func @testMultipleQubitRotationOp() - llvm.func @testMultipleQubitRotationOp() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(2.000000e-01 : f64) : f64 - // CHECK: %[[c_1:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: %[[q_1:.*]] = mqtref.allocQubit - // CHECK: mqtref.rxx(%[[c_0]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.ryy(%[[c_0]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.rzz(%[[c_0]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.rzx(%[[c_0]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.xx_minus_yy(%[[c_0]], %[[c_1]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.xx_plus_yy(%[[c_0]], %[[c_1]]) %[[q_0]], %[[q_1]] - - %ptr0 = llvm.mlir.zero : !llvm.ptr - %c0 = llvm.mlir.constant(2.000000e-01 : f64) : f64 - %c1 = llvm.mlir.constant(3.000000e-01 : f64) : f64 - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %q0 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - %q1 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - llvm.call @__quantum__qis__rxx__body(%q0, %q1, %c0) : (!llvm.ptr, !llvm.ptr, f64) -> () - llvm.call @__quantum__qis__ryy__body(%q0, %q1, %c0) : (!llvm.ptr, !llvm.ptr, f64) -> () - llvm.call @__quantum__qis__rzz__body(%q0, %q1, %c0) : (!llvm.ptr, !llvm.ptr, f64) -> () - llvm.call @__quantum__qis__rzx__body(%q0, %q1, %c0) : (!llvm.ptr, !llvm.ptr, f64) -> () - llvm.call @__quantum__qis__xx_minus_yy__body(%q0, %q1, %c0, %c1) : (!llvm.ptr, !llvm.ptr, f64, f64) -> () - llvm.call @__quantum__qis__xx_plus_yy__body(%q0, %q1, %c0, %c1) : (!llvm.ptr, !llvm.ptr, f64, f64) -> () - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__rt__qubit_release(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__rt__qubit_release(%q1) : (!llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__rxx__body(!llvm.ptr, !llvm.ptr, f64) - llvm.func @__quantum__qis__ryy__body(!llvm.ptr, !llvm.ptr, f64) - llvm.func @__quantum__qis__rzz__body(!llvm.ptr, !llvm.ptr, f64) - llvm.func @__quantum__qis__rzx__body(!llvm.ptr, !llvm.ptr, f64) - llvm.func @__quantum__qis__xx_minus_yy__body(!llvm.ptr, !llvm.ptr, f64, f64) - llvm.func @__quantum__qis__xx_plus_yy__body(!llvm.ptr, !llvm.ptr, f64, f64) - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate() -> !llvm.ptr - llvm.func @__quantum__rt__qubit_release(!llvm.ptr) -> () -} -// ----- -// This test checks if the multiple qubit rotation gates using static qubits are correctly converted -module { - // CHECK-LABEL: llvm.func @testMultipleQubitRotationOpStatic() - llvm.func @testMultipleQubitRotationOpStatic() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.qubit 0 - // CHECK: %[[q_1:.*]] = mqtref.qubit 1 - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(2.000000e-01 : f64) : f64 - // CHECK: %[[c_1:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: mqtref.rxx(%[[c_0]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.ryy(%[[c_0]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.rzz(%[[c_0]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.rzx(%[[c_0]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.xx_minus_yy(%[[c_0]], %[[c_1]]) %[[q_0]], %[[q_1]] - // CHECK: mqtref.xx_plus_yy(%[[c_0]], %[[c_1]]) %[[q_0]], %[[q_1]] - - %ptr0 = llvm.mlir.zero : !llvm.ptr - %c0 = llvm.mlir.constant(1 : i64) : i64 - %ptr1 = llvm.inttoptr %c0 : i64 to !llvm.ptr - %c1 = llvm.mlir.constant(2.000000e-01 : f64) : f64 - %c2 = llvm.mlir.constant(3.000000e-01 : f64) : f64 - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - llvm.call @__quantum__qis__rxx__body(%ptr0, %ptr1, %c1) : (!llvm.ptr, !llvm.ptr, f64) -> () - llvm.call @__quantum__qis__ryy__body(%ptr0, %ptr1, %c1) : (!llvm.ptr, !llvm.ptr, f64) -> () - llvm.call @__quantum__qis__rzz__body(%ptr0, %ptr1, %c1) : (!llvm.ptr, !llvm.ptr, f64) -> () - llvm.call @__quantum__qis__rzx__body(%ptr0, %ptr1, %c1) : (!llvm.ptr, !llvm.ptr, f64) -> () - llvm.call @__quantum__qis__xx_minus_yy__body(%ptr0, %ptr1, %c1, %c2) : (!llvm.ptr, !llvm.ptr, f64, f64) -> () - llvm.call @__quantum__qis__xx_plus_yy__body(%ptr0, %ptr1, %c1, %c2) : (!llvm.ptr, !llvm.ptr, f64, f64) -> () - llvm.br ^bb2 - ^bb2: - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__rxx__body(!llvm.ptr, !llvm.ptr, f64) - llvm.func @__quantum__qis__ryy__body(!llvm.ptr, !llvm.ptr, f64) - llvm.func @__quantum__qis__rzz__body(!llvm.ptr, !llvm.ptr, f64) - llvm.func @__quantum__qis__rzx__body(!llvm.ptr, !llvm.ptr, f64) - llvm.func @__quantum__qis__xx_minus_yy__body(!llvm.ptr, !llvm.ptr, f64, f64) - llvm.func @__quantum__qis__xx_plus_yy__body(!llvm.ptr, !llvm.ptr, f64, f64) - llvm.func @__quantum__rt__initialize(!llvm.ptr) -} - -// ----- -// This test checks if controlled gates are correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertControlledOp() - llvm.func @testConvertControlledOp() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: %[[q_1:.*]] = mqtref.allocQubit - // CHECK: %[[q_2:.*]] = mqtref.allocQubit - // CHECK: mqtref.x() %[[q_0:.*]] ctrl %[[q_1:.*]] - // CHECK: mqtref.x() %[[q_0]] ctrl %[[q_1]], %[[q_2]] - // CHECK: mqtref.z() %[[q_0]] ctrl %[[q_1]] - // CHECK: mqtref.rx(%[[c_0]]) %[[q_0]] ctrl %[[q_1]] - - %ptr0 = llvm.mlir.zero : !llvm.ptr - %c3 = llvm.mlir.constant(3.000000e-01 : f64) : f64 - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %q0 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - %q1 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - %q2 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - llvm.call @__quantum__qis__cnot__body(%q1, %q0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__ccx__body(%q1, %q2, %q0) : (!llvm.ptr, !llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__cz__body(%q1, %q0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__crx__body(%q1, %q0, %c3) : (!llvm.ptr, !llvm.ptr, f64) -> () - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__rt__qubit_release(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__rt__qubit_release(%q1) : (!llvm.ptr) -> () - llvm.call @__quantum__rt__qubit_release(%q2) : (!llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__cnot__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__ccx__body(!llvm.ptr, !llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__cz__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__crx__body(!llvm.ptr, !llvm.ptr, f64) - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate() -> !llvm.ptr - llvm.func @__quantum__rt__qubit_release(!llvm.ptr) -> () -} - -// ----- -// This test checks if controlled gates using static qubits are correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertControlledOpStatic() - llvm.func @testConvertControlledOpStatic() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.qubit 0 - // CHECK: %[[q_1:.*]] = mqtref.qubit 1 - // CHECK: %[[q_2:.*]] = mqtref.qubit 2 - // CHECK: mqtref.x() %[[q_0:.*]] ctrl %[[q_1:.*]] - // CHECK: mqtref.x() %[[q_0]] ctrl %[[q_1]], %[[q_2]] - // CHECK: mqtref.z() %[[q_0]] ctrl %[[q_1]] - // CHECK: mqtref.rx(%[[ANY:.*]]) %[[q_0]] ctrl %[[q_1]] - - %ptr0 = llvm.mlir.zero : !llvm.ptr - %c0 = llvm.mlir.constant(1 : i64) : i64 - %ptr1 = llvm.inttoptr %c0 : i64 to !llvm.ptr - %c1 = llvm.mlir.constant(2 : i64) : i64 - %ptr2 = llvm.inttoptr %c1 : i64 to !llvm.ptr - %c3 = llvm.mlir.constant(3.000000e-01 : f64) : f64 - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - llvm.call @__quantum__qis__cnot__body(%ptr1, %ptr0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__ccx__body(%ptr1, %ptr2, %ptr0) : (!llvm.ptr, !llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__cz__body(%ptr1, %ptr0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__crx__body(%ptr1, %ptr0, %c3) : (!llvm.ptr, !llvm.ptr, f64) -> () - llvm.br ^bb2 - ^bb2: - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__cnot__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__ccx__body(!llvm.ptr, !llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__cz__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__crx__body(!llvm.ptr, !llvm.ptr, f64) - llvm.func @__quantum__rt__initialize(!llvm.ptr) -} - -// ----- -// This test checks if the gphase operation is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertGPhaseOp() - llvm.func @testConvertGPhaseOp() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: mqtref.gphase(%[[c_0]]) - - %0 = llvm.mlir.zero : !llvm.ptr - %c0 = llvm.mlir.constant(3.000000e-01 : f64) : f64 - llvm.call @__quantum__rt__initialize(%0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - llvm.call @__quantum__qis__gphase__body(%c0) : (f64) -> () - llvm.br ^bb2 - ^bb2: - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__gphase__body(f64) - llvm.func @__quantum__rt__initialize(!llvm.ptr) -} - -// ----- -// This test checks if the controlled gphase operation is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertGPhaseOpControlled() - llvm.func @testConvertGPhaseOpControlled() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: mqtref.gphase(%[[c_0]]) ctrl %[[q_0]] - - %ptr0 = llvm.mlir.zero : !llvm.ptr - %c0 = llvm.mlir.constant(3.000000e-01 : f64) : f64 - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %q0 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - llvm.call @__quantum__qis__cgphase__body(%q0, %c0) : (!llvm.ptr, f64) -> () - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__rt__qubit_release(%q0) : (!llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__cgphase__body(!llvm.ptr, f64) - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate() -> !llvm.ptr - llvm.func @__quantum__rt__qubit_release(!llvm.ptr) -> () -} - -// ----- -// This test checks if the controlled gphase operation using static qubits is correctly converted -module { - // CHECK-LABEL: llvm.func @testConvertGPhaseOpControlledStatic() - llvm.func @testConvertGPhaseOpControlledStatic() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.qubit 0 - // CHECK: %[[c_0:.*]] = llvm.mlir.constant(3.000000e-01 : f64) : f64 - // CHECK: mqtref.gphase(%[[c_0]]) ctrl %[[q_0]] - - %ptr0 = llvm.mlir.zero : !llvm.ptr - %c0 = llvm.mlir.constant(3.000000e-01 : f64) : f64 - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - llvm.call @__quantum__qis__cgphase__body(%ptr0, %c0) : (!llvm.ptr, f64) -> () - llvm.br ^bb2 - ^bb2: - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__cgphase__body(!llvm.ptr, f64) - llvm.func @__quantum__rt__initialize(!llvm.ptr) -} - -// ----- -// This test checks if the barrierOp is converted correctly -module { - // CHECK-LABEL: llvm.func @testConvertBarrierOp() - llvm.func @testConvertBarrierOp() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: mqtref.barrier() %[[q_0]] - - %ptr0 = llvm.mlir.zero : !llvm.ptr - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %q0 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - llvm.call @__quantum__qis__barrier__body(%q0) : (!llvm.ptr) -> () - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__rt__qubit_release(%q0) : (!llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__barrier__body(!llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate() -> !llvm.ptr - llvm.func @__quantum__rt__qubit_release(!llvm.ptr) -> () -} - -// ----- -// This test checks if the barrierOp using static qubits is converted correctly -module { - // CHECK-LABEL: llvm.func @testConvertBarrierOpStatic() - llvm.func @testConvertBarrierOpStatic() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "0"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[q_0:.*]] = mqtref.qubit 0 - // CHECK: mqtref.barrier() %[[q_0]] - - %ptr0 = llvm.mlir.zero : !llvm.ptr - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - llvm.call @__quantum__qis__barrier__body(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb2 - ^bb2: - llvm.br ^bb3 - ^bb3: - llvm.return - } - llvm.func @__quantum__qis__barrier__body(!llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) -} - -// ----- -// This test checks if a Bell state is converted correctly. -module { - llvm.mlir.global internal constant @mlir.llvm.nameless_global_0("r0\00") {addr_space = 0 : i32, dso_local} - llvm.mlir.global internal constant @mlir.llvm.nameless_global_1("r1\00") {addr_space = 0 : i32, dso_local} - - // CHECK-LABEL: llvm.func @bellState() - llvm.func @bellState() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "2"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[mem]] = memref.alloca() : memref<2xi1> - // CHECK: %[[q_0:.*]] = mqtref.allocQubit - // CHECK: %[[q_1:.*]] = mqtref.allocQubit - // CHECK: mqtref.h() %[[q_0]] - // CHECK: mqtref.x() %[[q_1]] ctrl %[[q_0]] - // CHECK: %[[m_0:.*]] = mqtref.measure %[[q_0]] - // CHECK: %[[m_1:.*]] = mqtref.measure %[[q_1]] - // CHECK: mqtref.deallocQubit %[[q_0]] - // CHECK: mqtref.deallocQubit %[[q_1]] - // CHECK: %[[c_0:.*]] = arith.constant 0 : index - // CHECK: memref.store %[[m_0]], %[[mem]][%[[c_0]]] : memref<2xi1> - // CHECK: %[[c_1:.*]] = arith.constant 1 : index - // CHECK: memref.store %[[m_1]], %[[mem]][%[[c_1]]] : memref<2xi1> - - - %ptr0 = llvm.mlir.zero : !llvm.ptr - %a0 = llvm.mlir.addressof @mlir.llvm.nameless_global_0 : !llvm.ptr - %a1 = llvm.mlir.addressof @mlir.llvm.nameless_global_1 : !llvm.ptr - %c1 = llvm.mlir.constant(1 : i64) : i64 - %ptr1 = llvm.inttoptr %c1 : i64 to !llvm.ptr - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - %q0 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - %q1 = llvm.call @__quantum__rt__qubit_allocate() : () -> !llvm.ptr - llvm.call @__quantum__qis__h__body(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__cnot__body(%q0, %q1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__qis__mz__body(%q0, %ptr0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__mz__body(%q1, %ptr1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__rt__qubit_release(%q0) : (!llvm.ptr) -> () - llvm.call @__quantum__rt__qubit_release(%q1) : (!llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.call @__quantum__rt__result_record_output(%ptr0, %a0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__rt__result_record_output(%ptr1, %a1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.return - } - llvm.func @__quantum__qis__mz__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__result_record_output(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__h__body(!llvm.ptr) - llvm.func @__quantum__qis__cnot__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) - llvm.func @__quantum__rt__qubit_release(!llvm.ptr) - llvm.func @__quantum__rt__qubit_allocate() -> !llvm.ptr - -} - -// ----- -// This test checks if a Bell state using static qubits is converted correctly. -module { - llvm.mlir.global internal constant @mlir.llvm.nameless_global_0("r0\00") {addr_space = 0 : i32, dso_local} - llvm.mlir.global internal constant @mlir.llvm.nameless_global_1("r1\00") {addr_space = 0 : i32, dso_local} - - // CHECK-LABEL: llvm.func @bellStateStaticAddressing() - llvm.func @bellStateStaticAddressing() attributes {passthrough = ["entry_point", ["output_labeling_schema", "schema_id"], ["qir_profiles", "base_profile"], ["required_num_qubits", "2"], ["required_num_results", "2"], ["qir_major_version", "1"], ["qir_minor_version", "0"], ["dynamic_qubit_management", "true"], ["dynamic_result_management", "false"]]} { - // CHECK: %[[mem]] = memref.alloca() : memref<2xi1> - // CHECK: %[[q_0:.*]] = mqtref.qubit 0 - // CHECK: %[[q_1:.*]] = mqtref.qubit 1 - // CHECK: mqtref.h() %[[q_0]] - // CHECK: mqtref.x() %[[q_1]] ctrl %[[q_0]] - // CHECK: %[[m_0:.*]] = mqtref.measure %[[q_0]] - // CHECK: %[[m_1:.*]] = mqtref.measure %[[q_1]] - // CHECK: %[[c_0:.*]] = arith.constant 0 : index - // CHECK: memref.store %[[m_0]], %[[mem]][%[[c_0]]] : memref<2xi1> - // CHECK: %[[c_1:.*]] = arith.constant 1 : index - // CHECK: memref.store %[[m_1]], %[[mem]][%[[c_1]]] : memref<2xi1> - - %ptr0 = llvm.mlir.zero : !llvm.ptr - %c0 = llvm.mlir.constant(1 : i64) : i64 - %ptr1 = llvm.inttoptr %c0 : i64 to !llvm.ptr - %a0 = llvm.mlir.addressof @mlir.llvm.nameless_global_0 : !llvm.ptr - %a1 = llvm.mlir.addressof @mlir.llvm.nameless_global_1 : !llvm.ptr - llvm.call @__quantum__rt__initialize(%ptr0) : (!llvm.ptr) -> () - llvm.br ^bb1 - ^bb1: - llvm.call @__quantum__qis__h__body(%ptr0) : (!llvm.ptr) -> () - llvm.call @__quantum__qis__cnot__body(%ptr0, %ptr1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.br ^bb2 - ^bb2: - llvm.call @__quantum__qis__mz__body(%ptr0, %ptr0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__qis__mz__body(%ptr1, %ptr1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.br ^bb3 - ^bb3: - llvm.call @__quantum__rt__result_record_output(%ptr0, %a0) : (!llvm.ptr, !llvm.ptr) -> () - llvm.call @__quantum__rt__result_record_output(%ptr1, %a1) : (!llvm.ptr, !llvm.ptr) -> () - llvm.return - } - llvm.func @__quantum__qis__mz__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__result_record_output(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__qis__h__body(!llvm.ptr) - llvm.func @__quantum__qis__cnot__body(!llvm.ptr, !llvm.ptr) - llvm.func @__quantum__rt__initialize(!llvm.ptr) -} diff --git a/mlir/test/Dialect/MQTOpt/IR/dialect_features.mlir b/mlir/test/Dialect/MQTOpt/IR/dialect_features.mlir deleted file mode 100644 index a4c95c6edb..0000000000 --- a/mlir/test/Dialect/MQTOpt/IR/dialect_features.mlir +++ /dev/null @@ -1,1554 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file -verify-diagnostics | FileCheck %s - -// ----- -// This test checks if the AllocOp is parsed and handled correctly using a static attribute. -module { - // CHECK-LABEL: func.func @testAllocOpAttribute - func.func @testAllocOpAttribute() { - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> - - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if the AllocOp is parsed and handled correctly using a dynamic operand. -module { - // CHECK-LABEL: func.func @testAllocOpOperand - func.func @testAllocOpOperand() { - // CHECK: %[[I2:.*]] = arith.constant 2 : index - // CHECK: %[[Qreg:.*]] = memref.alloc(%[[I2]]) : memref - - %i2 = arith.constant 2 : index - %qreg = memref.alloc(%i2) : memref - - return - } -} - -// ----- -// This test checks if the AllocQubitOp is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testAllocQubitOp - func.func @testAllocQubitOp() { - // CHECK: %[[Q0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1:.*]] = mqtopt.allocQubit - - %q0 = "mqtopt.allocQubit"() : () -> !mqtopt.Qubit - %1 = mqtopt.allocQubit - return - } -} - -// ----- -// This test checks if the DeallocQubitOp is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testDeallocQubitOp - func.func @testDeallocQubitOp() { - // CHECK: %[[Q0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1:.*]] = mqtopt.allocQubit - // CHECK: mqtopt.deallocQubit %[[Q0]] - // CHECK: mqtopt.deallocQubit %[[Q1]] - - %q0 = "mqtopt.allocQubit"() : () -> !mqtopt.Qubit - %1 = mqtopt.allocQubit - - "mqtopt.deallocQubit"(%q0) : (!mqtopt.Qubit) -> () - mqtopt.deallocQubit %1 - return - } -} - -// ----- -// This test checks if the DeallocOp is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testDeallocOp - func.func @testDeallocOp() { - // CHECK: memref.dealloc %[[ANY:.*]] : memref<2x!mqtopt.Qubit> - - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if the LoadOp is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testExtractOpAttribute - func.func @testExtractOpAttribute() { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Q_0:.*]] = memref.load %[[ANY:.*]][%[[I0]]] : memref<1x!mqtopt.Qubit> - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if the InsertOp is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testInsertOpAttribute - func.func @testInsertOpAttribute() { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: memref.store %[[ANY:.*]], %[[ANY:.*]][%[[I0]]] : memref<1x!mqtopt.Qubit> - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.store %q_0, %qreg[%i0] : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that all resources defined in the MQTOpt dialect are parsed and handled correctly using dynamic operands. -module { - // CHECK-LABEL: func.func @testAllResourcesUsingOperands - func.func @testAllResourcesUsingOperands() { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc(%[[I1]]) : memref - // CHECK: %[[Q_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref - // CHECK: memref.store %[[Q_0:.*]], %[[Qreg]][%[[I0]]] : memref - // CHECK: memref.dealloc %[[Qreg:.*]] : memref - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc(%i1) : memref - %q_0 = memref.load %qreg[%i0] : memref - memref.store %q_0, %qreg[%i0] : memref - memref.dealloc %qreg : memref - - return - } -} - -// ----- -// This test checks if the MeasureOp is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMeasureOp - func.func @testMeasureOp() { - // CHECK: %[[Q_1:.*]], [[M0_0:.*]] = mqtopt.measure %[[ANY:.*]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q_1, %m0_0 = mqtopt.measure %q_0 - - memref.store %q_1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if the MeasureOp on a static qubit is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMeasureOpStatic - func.func @testMeasureOpStatic() { - // CHECK: %[[Q_1:.*]], [[M0_0:.*]] = mqtopt.measure %[[ANY:.*]] - - %q_0 = mqtopt.qubit 0 - %q_1, %m0_0 = mqtopt.measure %q_0 - - return - } -} - -// ----- -// This test checks if the ResetOp on a dynamic qubit is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testResetOp - func.func @testResetOp() { - // CHECK: %[[Q_1:.*]] = mqtopt.reset %[[ANY:.*]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q_1 = mqtopt.reset %q_0 - - memref.store %q_1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if the ResetOp on a static qubit is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testResetOpStatic - func.func @testResetOpStatic() { - // CHECK: %[[Q_1:.*]] = mqtopt.reset %[[ANY:.*]] - - %q_0 = mqtopt.qubit 0 - %q_1 = mqtopt.reset %q_0 - - return - } -} - -// ----- -// This test checks if no-target operations without controls are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNoTargetNoControls - func.func @testNoTargetNoControls() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: mqtopt.gphase(%[[C0_F64]]) - // CHECK: mqtopt.gphase(%[[C0_F64]]) - - %c0_f64 = arith.constant 3.000000e-01 : f64 - mqtopt.gphase(%c0_f64) : () - mqtopt.gphase(%c0_f64) - return - } -} - -// ----- -// This test checks if no-target operations with controls are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNoTargetWithControls - func.func @testNoTargetWithControls() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: %[[Q0_1:.*]] = mqtopt.gphase(%[[C0_F64]]) ctrl %[[ANY:.*]] : ctrl !mqtopt.Qubit - // CHECK: %[[Q01:.*]]:2 = mqtopt.gphase(%[[C0_F64]]) ctrl %[[Q0_1]], %[[ANY:.*]] : ctrl !mqtopt.Qubit, !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - %q0_1 = mqtopt.gphase(%c0_f64) ctrl %q0_0 : ctrl !mqtopt.Qubit - %q0_2, %q1_1 = mqtopt.gphase(%c0_f64) ctrl %q0_1, %q1_0 : ctrl !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if no-target operations with positive and negative controls are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNoTargetPositiveNegativeControls - func.func @testNoTargetPositiveNegativeControls() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.gphase(%[[C0_F64]]) ctrl %[[ANY:.*]] : ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - %q0_1, %q1_1 = mqtopt.gphase(%c0_f64) ctrl %q0_0 nctrl %q1_0 : ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - memref.store %q0_1, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if no-target operations with static controls are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNoTargetWithStaticControls - func.func @testNoTargetWithStaticControls() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: %[[Q0_1:.*]] = mqtopt.gphase(%[[C0_F64]]) ctrl %[[ANY:.*]] : ctrl !mqtopt.Qubit - // CHECK: %[[Q01:.*]]:2 = mqtopt.gphase(%[[C0_F64]]) ctrl %[[Q0_1]], %[[ANY:.*]] : ctrl !mqtopt.Qubit, !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - - %c0_f64 = arith.constant 3.000000e-01 : f64 - %q0_1 = mqtopt.gphase(%c0_f64) ctrl %q0_0 : ctrl !mqtopt.Qubit - %q0_2, %q1_1 = mqtopt.gphase(%c0_f64) ctrl %q0_1, %q1_0 : ctrl !mqtopt.Qubit, !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if no-target operations with positive and negative static controls are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNoTargetPositiveNegativeStaticControls - func.func @testNoTargetPositiveNegativeStaticControls() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.gphase(%[[C0_F64]]) ctrl %[[ANY:.*]] : ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - - %c0_f64 = arith.constant 3.000000e-01 : f64 - %q0_1, %q1_1 = mqtopt.gphase(%c0_f64) ctrl %q0_0 nctrl %q1_0 : ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if single qubit gates on dynamic qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testSingleQubitOp - func.func @testSingleQubitOp() { - // CHECK: %[[Q_1:.*]] = mqtopt.i() %[[ANY:.*]] : !mqtopt.Qubit - // CHECK: %[[Q_2:.*]] = mqtopt.h() %[[Q_1]] : !mqtopt.Qubit - // CHECK: %[[Q_3:.*]] = mqtopt.x() %[[Q_2]] : !mqtopt.Qubit - // CHECK: %[[Q_4:.*]] = mqtopt.y() %[[Q_3]] : !mqtopt.Qubit - // CHECK: %[[Q_5:.*]] = mqtopt.z() %[[Q_4]] : !mqtopt.Qubit - // CHECK: %[[Q_6:.*]] = mqtopt.s() %[[Q_5]] : !mqtopt.Qubit - // CHECK: %[[Q_7:.*]] = mqtopt.sdg() %[[Q_6]] : !mqtopt.Qubit - // CHECK: %[[Q_8:.*]] = mqtopt.t() %[[Q_7]] : !mqtopt.Qubit - // CHECK: %[[Q_9:.*]] = mqtopt.tdg() %[[Q_8]] : !mqtopt.Qubit - // CHECK: %[[Q_10:.*]] = mqtopt.v() %[[Q_9]] : !mqtopt.Qubit - // CHECK: %[[Q_11:.*]] = mqtopt.vdg() %[[Q_10]] : !mqtopt.Qubit - // CHECK: %[[Q_12:.*]] = mqtopt.sx() %[[Q_11]] : !mqtopt.Qubit - // CHECK: %[[Q_13:.*]] = mqtopt.sxdg() %[[Q_12]] : !mqtopt.Qubit - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q_1 = mqtopt.i() %q_0 : !mqtopt.Qubit - %q_2 = mqtopt.h() %q_1 : !mqtopt.Qubit - %q_3 = mqtopt.x() %q_2 : !mqtopt.Qubit - %q_4 = mqtopt.y() %q_3 : !mqtopt.Qubit - %q_5 = mqtopt.z() %q_4 : !mqtopt.Qubit - %q_6 = mqtopt.s() %q_5 : !mqtopt.Qubit - %q_7 = mqtopt.sdg() %q_6 : !mqtopt.Qubit - %q_8 = mqtopt.t() %q_7 : !mqtopt.Qubit - %q_9 = mqtopt.tdg() %q_8 : !mqtopt.Qubit - %q_10 = mqtopt.v() %q_9 : !mqtopt.Qubit - %q_11 = mqtopt.vdg() %q_10 : !mqtopt.Qubit - %q_12 = mqtopt.sx() %q_11 : !mqtopt.Qubit - %q_13 = mqtopt.sxdg() %q_12 : !mqtopt.Qubit - - memref.store %q_13, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if single qubit gates on static qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testSingleQubitOpStatic - func.func @testSingleQubitOpStatic() { - // CHECK: %[[Q_1:.*]] = mqtopt.i() %[[ANY:.*]] : !mqtopt.Qubit - // CHECK: %[[Q_2:.*]] = mqtopt.h() %[[Q_1]] : !mqtopt.Qubit - // CHECK: %[[Q_3:.*]] = mqtopt.x() %[[Q_2]] : !mqtopt.Qubit - // CHECK: %[[Q_4:.*]] = mqtopt.y() %[[Q_3]] : !mqtopt.Qubit - // CHECK: %[[Q_5:.*]] = mqtopt.z() %[[Q_4]] : !mqtopt.Qubit - // CHECK: %[[Q_6:.*]] = mqtopt.s() %[[Q_5]] : !mqtopt.Qubit - // CHECK: %[[Q_7:.*]] = mqtopt.sdg() %[[Q_6]] : !mqtopt.Qubit - // CHECK: %[[Q_8:.*]] = mqtopt.t() %[[Q_7]] : !mqtopt.Qubit - // CHECK: %[[Q_9:.*]] = mqtopt.tdg() %[[Q_8]] : !mqtopt.Qubit - // CHECK: %[[Q_10:.*]] = mqtopt.v() %[[Q_9]] : !mqtopt.Qubit - // CHECK: %[[Q_11:.*]] = mqtopt.vdg() %[[Q_10]] : !mqtopt.Qubit - // CHECK: %[[Q_12:.*]] = mqtopt.sx() %[[Q_11]] : !mqtopt.Qubit - // CHECK: %[[Q_13:.*]] = mqtopt.sxdg() %[[Q_12]] : !mqtopt.Qubit - - %q_0 = mqtopt.qubit 0 - - %q_1 = mqtopt.i() %q_0 : !mqtopt.Qubit - %q_2 = mqtopt.h() %q_1 : !mqtopt.Qubit - %q_3 = mqtopt.x() %q_2 : !mqtopt.Qubit - %q_4 = mqtopt.y() %q_3 : !mqtopt.Qubit - %q_5 = mqtopt.z() %q_4 : !mqtopt.Qubit - %q_6 = mqtopt.s() %q_5 : !mqtopt.Qubit - %q_7 = mqtopt.sdg() %q_6 : !mqtopt.Qubit - %q_8 = mqtopt.t() %q_7 : !mqtopt.Qubit - %q_9 = mqtopt.tdg() %q_8 : !mqtopt.Qubit - %q_10 = mqtopt.v() %q_9 : !mqtopt.Qubit - %q_11 = mqtopt.vdg() %q_10 : !mqtopt.Qubit - %q_12 = mqtopt.sx() %q_11 : !mqtopt.Qubit - %q_13 = mqtopt.sxdg() %q_12 : !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if parameterized single qubit gates on dynamic qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testSingleQubitRotationOp - func.func @testSingleQubitRotationOp() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: %[[Q_1:.*]] = mqtopt.u(%[[C0_F64]], %[[C0_F64]], %[[C0_F64]]) %[[ANY:.*]] : !mqtopt.Qubit - // CHECK: %[[Q_2:.*]] = mqtopt.u2(%[[C0_F64]], %[[C0_F64]] static [] mask [false, false]) %[[Q_1]] : !mqtopt.Qubit - // CHECK: %[[Q_3:.*]] = mqtopt.p(%[[C0_F64]]) %[[Q_2]] : !mqtopt.Qubit - // CHECK: %[[Q_4:.*]] = mqtopt.rx(%[[C0_F64]]) %[[Q_3]] : !mqtopt.Qubit - // CHECK: %[[Q_5:.*]] = mqtopt.ry(%[[C0_F64]]) %[[Q_4]] : !mqtopt.Qubit - // CHECK: %[[Q_6:.*]] = mqtopt.rz(%[[C0_F64]]) %[[Q_5]] : !mqtopt.Qubit - // CHECK: %[[Q_7:.*]] = mqtopt.r(%[[C0_F64]], %[[C0_F64]] static [] mask [false, false]) %[[Q_6]] : !mqtopt.Qubit - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - %q_1 = mqtopt.u(%c0_f64, %c0_f64, %c0_f64) %q_0 : !mqtopt.Qubit - %q_2 = mqtopt.u2(%c0_f64, %c0_f64 static [] mask [false, false]) %q_1 : !mqtopt.Qubit - %q_3 = mqtopt.p(%c0_f64) %q_2 : !mqtopt.Qubit - %q_4 = mqtopt.rx(%c0_f64) %q_3 : !mqtopt.Qubit - %q_5 = mqtopt.ry(%c0_f64) %q_4 : !mqtopt.Qubit - %q_6 = mqtopt.rz(%c0_f64) %q_5 : !mqtopt.Qubit - %q_7 = mqtopt.r(%c0_f64, %c0_f64 static [] mask [false, false]) %q_6 : !mqtopt.Qubit - - memref.store %q_7, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if parameterized single qubit gates on static qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testSingleQubitRotationOpStatic - func.func @testSingleQubitRotationOpStatic() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: %[[Q_1:.*]] = mqtopt.u(%[[C0_F64]], %[[C0_F64]], %[[C0_F64]]) %[[ANY:.*]] : !mqtopt.Qubit - // CHECK: %[[Q_2:.*]] = mqtopt.u2(%[[C0_F64]], %[[C0_F64]] static [] mask [false, false]) %[[Q_1]] : !mqtopt.Qubit - // CHECK: %[[Q_3:.*]] = mqtopt.p(%[[C0_F64]]) %[[Q_2]] : !mqtopt.Qubit - // CHECK: %[[Q_4:.*]] = mqtopt.rx(%[[C0_F64]]) %[[Q_3]] : !mqtopt.Qubit - // CHECK: %[[Q_5:.*]] = mqtopt.ry(%[[C0_F64]]) %[[Q_4]] : !mqtopt.Qubit - // CHECK: %[[Q_6:.*]] = mqtopt.rz(%[[C0_F64]]) %[[Q_5]] : !mqtopt.Qubit - // CHECK: %[[Q_7:.*]] = mqtopt.r(%[[C0_F64]], %[[C0_F64]] static [] mask [false, false]) %[[Q_6]] : !mqtopt.Qubit - - %q_0 = mqtopt.qubit 0 - - %c0_f64 = arith.constant 3.000000e-01 : f64 - %q_1 = mqtopt.u(%c0_f64, %c0_f64, %c0_f64) %q_0 : !mqtopt.Qubit - %q_2 = mqtopt.u2(%c0_f64, %c0_f64 static [] mask [false, false]) %q_1 : !mqtopt.Qubit - %q_3 = mqtopt.p(%c0_f64) %q_2 : !mqtopt.Qubit - %q_4 = mqtopt.rx(%c0_f64) %q_3 : !mqtopt.Qubit - %q_5 = mqtopt.ry(%c0_f64) %q_4 : !mqtopt.Qubit - %q_6 = mqtopt.rz(%c0_f64) %q_5 : !mqtopt.Qubit - %q_7 = mqtopt.r(%c0_f64, %c0_f64 static [] mask [false, false]) %q_6 : !mqtopt.Qubit - - return - } -} - - -// ----- -// This test checks if controlled parameterized single qubit gates on dynamic qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testControlledSingleQubitRotationOp - func.func @testControlledSingleQubitRotationOp() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.u(%[[C0_F64]], %[[C0_F64]], %[[C0_F64]]) %[[ANY:.*]] ctrl %[[ANY:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[Q1_2:.*]] = mqtopt.u2(%[[C0_F64]], %[[C0_F64]]) %[[Q0_1]] ctrl %[[Q1_1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_3:.*]], %[[Q1_3:.*]] = mqtopt.p(%[[C0_F64]]) %[[Q0_2]] ctrl %[[Q1_2]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_4:.*]], %[[Q1_4:.*]] = mqtopt.rx(%[[C0_F64]]) %[[Q0_3]] ctrl %[[Q1_3]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_5:.*]], %[[Q1_5:.*]] = mqtopt.ry(%[[C0_F64]]) %[[Q0_4]] ctrl %[[Q1_4]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_6:.*]], %[[Q1_6:.*]] = mqtopt.rz(%[[C0_F64]]) %[[Q0_5]] ctrl %[[Q1_5]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_7:.*]], %[[Q1_7:.*]] = mqtopt.r(%[[C0_F64]], %[[C0_F64]]) %[[Q0_6]] ctrl %[[Q1_6]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - %q0_1, %q1_1 = mqtopt.u(%c0_f64, %c0_f64, %c0_f64) %q0_0 ctrl %q1_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_2, %q1_2 = mqtopt.u2(%c0_f64, %c0_f64) %q0_1 ctrl %q1_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %q1_3 = mqtopt.p(%c0_f64) %q0_2 ctrl %q1_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_4, %q1_4 = mqtopt.rx(%c0_f64) %q0_3 ctrl %q1_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_5, %q1_5 = mqtopt.ry(%c0_f64) %q0_4 ctrl %q1_4 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_6, %q1_6 = mqtopt.rz(%c0_f64) %q0_5 ctrl %q1_5 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_7, %q1_7 = mqtopt.r(%c0_f64, %c0_f64) %q0_6 ctrl %q1_6 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - memref.store %q0_7, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_7, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if controlled parameterized single qubit gates on static qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testControlledSingleQubitRotationOpStatic - func.func @testControlledSingleQubitRotationOpStatic() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.u(%[[C0_F64]], %[[C0_F64]], %[[C0_F64]]) %[[ANY:.*]] ctrl %[[ANY:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[Q1_2:.*]] = mqtopt.u2(%[[C0_F64]], %[[C0_F64]]) %[[Q0_1]] ctrl %[[Q1_1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_3:.*]], %[[Q1_3:.*]] = mqtopt.p(%[[C0_F64]]) %[[Q0_2]] ctrl %[[Q1_2]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_4:.*]], %[[Q1_4:.*]] = mqtopt.rx(%[[C0_F64]]) %[[Q0_3]] ctrl %[[Q1_3]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_5:.*]], %[[Q1_5:.*]] = mqtopt.ry(%[[C0_F64]]) %[[Q0_4]] ctrl %[[Q1_4]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_6:.*]], %[[Q1_6:.*]] = mqtopt.rz(%[[C0_F64]]) %[[Q0_5]] ctrl %[[Q1_5]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_7:.*]], %[[Q1_7:.*]] = mqtopt.r(%[[C0_F64]], %[[C0_F64]]) %[[Q0_6]] ctrl %[[Q1_6]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - - %c0_f64 = arith.constant 3.000000e-01 : f64 - %q0_1, %q1_1 = mqtopt.u(%c0_f64, %c0_f64, %c0_f64) %q0_0 ctrl %q1_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_2, %q1_2 = mqtopt.u2(%c0_f64, %c0_f64) %q0_1 ctrl %q1_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %q1_3 = mqtopt.p(%c0_f64) %q0_2 ctrl %q1_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_4, %q1_4 = mqtopt.rx(%c0_f64) %q0_3 ctrl %q1_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_5, %q1_5 = mqtopt.ry(%c0_f64) %q0_4 ctrl %q1_4 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_6, %q1_6 = mqtopt.rz(%c0_f64) %q0_5 ctrl %q1_5 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_7, %q1_7 = mqtopt.r(%c0_f64, %c0_f64) %q0_6 ctrl %q1_6 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if an CX gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testCXOp - func.func @testCXOp() { - // CHECK: %[[Q1_1:.*]], %[[Q0_1:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q1_1, %q0_1 = mqtopt.x() %q1_0 ctrl %q0_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - memref.store %q0_1, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a negative CX gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNegativeCXOp - func.func @testNegativeCXOp() { - // CHECK: %[[Q1_1:.*]], %[[Q0_1:.*]] = mqtopt.x() %[[ANY:.*]] nctrl %[[ANY:.*]] : !mqtopt.Qubit nctrl !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q1_1, %q0_1 = mqtopt.x() %q1_0 nctrl %q0_0 : !mqtopt.Qubit nctrl !mqtopt.Qubit - - memref.store %q0_1, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if an CX gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testCXOpStatic - func.func @testCXOpStatic() { - // CHECK: %[[Q1_1:.*]], %[[Q0_1:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - - %q1_1, %q0_1 = mqtopt.x() %q1_0 ctrl %q0_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if a negative CX gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNegativeCXOpStatic - func.func @testNegativeCXOpStatic() { - // CHECK: %[[Q1_1:.*]], %[[Q0_1:.*]] = mqtopt.x() %[[ANY:.*]] nctrl %[[ANY:.*]] : !mqtopt.Qubit nctrl !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - - %q1_1, %q0_1 = mqtopt.x() %q1_0 nctrl %q0_0 : !mqtopt.Qubit nctrl !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if an MCX gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMCXOp - func.func @testMCXOp() { - // CHECK: %[[Q1_1:.*]], %[[Q02_1:.*]]:2 = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<3x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<3x!mqtopt.Qubit> - %q2_0 = memref.load %qreg[%i2] : memref<3x!mqtopt.Qubit> - - //===------------------------------------------------------------------===// - // q0_0: ──■── q02_1#0 - // ┌─┴─┐ - // q1_0: ┤ X ├ q1_1 - // └─┬─┘ - // q2_0: ──■── q02_1#1 - //===----------------------------------------------------------------===// - - %q1_1, %q02_1:2 = mqtopt.x() %q1_0 ctrl %q0_0, %q2_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q02_1#0, %qreg[%i0] : memref<3x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<3x!mqtopt.Qubit> - memref.store %q02_1#1, %qreg[%i2] : memref<3x!mqtopt.Qubit> - memref.dealloc %qreg : memref<3x!mqtopt.Qubit> - - return - } -} - - -// ----- -// This test checks if an MCX gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMCXOpStatic - func.func @testMCXOpStatic() { - // CHECK: %[[Q1_1:.*]], %[[Q02_1:.*]]:2 = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - %q2_0 = mqtopt.qubit 2 - - //===------------------------------------------------------------------===// - // q0_0: ──■── q02_1#0 - // ┌─┴─┐ - // q1_0: ┤ X ├ q1_1 - // └─┬─┘ - // q2_0: ──■── q02_1#1 - //===----------------------------------------------------------------===// - - %q1_1, %q02_1:2 = mqtopt.x() %q1_0 ctrl %q0_0, %q2_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if an MCX gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMCXOpAlternativeFormat - func.func @testMCXOpAlternativeFormat() { - // CHECK: %[[Q1_1:.*]], %[[Q02_1:.*]]:2 = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<3x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<3x!mqtopt.Qubit> - %q2_0 = memref.load %qreg[%i2] : memref<3x!mqtopt.Qubit> - - //===------------------------------------------------------------------===// - // q0_0: ──■── q102_1#0 - // ┌─┴─┐ - // q1_0: ┤ X ├ q102_1#1 - // └─┬─┘ - // q2_0: ──■── q102_1#2 - //===----------------------------------------------------------------===// - - %q102_1:3 = mqtopt.x() %q1_0 ctrl %q0_0, %q2_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q102_1#1, %qreg[%i0] : memref<3x!mqtopt.Qubit> - memref.store %q102_1#0, %qreg[%i1] : memref<3x!mqtopt.Qubit> - memref.store %q102_1#1, %qreg[%i2] : memref<3x!mqtopt.Qubit> - memref.dealloc %qreg : memref<3x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if an MCX gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMCXOpAlternativeFormatStatic - func.func @testMCXOpAlternativeFormatStatic() { - // CHECK: %[[Q1_1:.*]], %[[Q02_1:.*]]:2 = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - %q2_0 = mqtopt.qubit 2 - - //===------------------------------------------------------------------===// - // q0_0: ──■── q102_1#0 - // ┌─┴─┐ - // q1_0: ┤ X ├ q102_1#1 - // └─┬─┘ - // q2_0: ──■── q102_1#2 - //===----------------------------------------------------------------===// - - %q102_1:3 = mqtopt.x() %q1_0 ctrl %q0_0, %q2_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if a negative MCX gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNegativeMCXOp - func.func @testNegativeMCXOp() { - // CHECK: %[[Q1_1:.*]], %[[Q02_1:.*]]:2 = mqtopt.x() %[[ANY:.*]] nctrl %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit nctrl !mqtopt.Qubit, !mqtopt.Qubit - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<3x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<3x!mqtopt.Qubit> - %q2_0 = memref.load %qreg[%i2] : memref<3x!mqtopt.Qubit> - - //===------------------------------------------------------------------===// - // q0_0: ──○── q02_1#0 - // ┌─┴─┐ - // q1_0: ┤ X ├ q1_1 - // └─┬─┘ - // q2_0: ──○── q02_1#1 - //===----------------------------------------------------------------===// - - %q1_1, %q02_1:2 = mqtopt.x() %q1_0 nctrl %q0_0, %q2_0 : !mqtopt.Qubit nctrl !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q02_1#1, %qreg[%i0] : memref<3x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<3x!mqtopt.Qubit> - memref.store %q02_1#1, %qreg[%i2] : memref<3x!mqtopt.Qubit> - memref.dealloc %qreg : memref<3x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a negative MCX gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNegativeMCXOpStatic - func.func @testNegativeMCXOpStatic() { - // CHECK: %[[Q1_1:.*]], %[[Q02_1:.*]]:2 = mqtopt.x() %[[ANY:.*]] nctrl %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit nctrl !mqtopt.Qubit, !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - %q2_0 = mqtopt.qubit 2 - - //===------------------------------------------------------------------===// - // q0_0: ──○── q02_1#0 - // ┌─┴─┐ - // q1_0: ┤ X ├ q1_1 - // └─┬─┘ - // q2_0: ──○── q02_1#1 - //===----------------------------------------------------------------===// - - %q1_1, %q02_1:2 = mqtopt.x() %q1_0 nctrl %q0_0, %q2_0 : !mqtopt.Qubit nctrl !mqtopt.Qubit, !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if an MCX gate on dynamic qubits is parsed and handled correctly using different types of controls. -module { - // CHECK-LABEL: func.func @testMixedMCXOp - func.func @testMixedMCXOp() { - // CHECK: %[[Q1_1:.*]], %[[Q0_1:.*]], %[[Q2_1:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] nctrl %[[ANY:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<3x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<3x!mqtopt.Qubit> - %q2_0 = memref.load %qreg[%i2] : memref<3x!mqtopt.Qubit> - - //===------------------------------------------------------------------===// - // q0_0: ──■── q0_1 - // ┌─┴─┐ - // q1_0: ┤ X ├ q1_1 - // └─┬─┘ - // q2_0: ──○── q2_1 - //===----------------------------------------------------------------===// - - %q1_1, %q0_1, %q2_1 = mqtopt.x() %q1_0 ctrl %q0_0 nctrl %q2_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - memref.store %q0_1, %qreg[%i0] : memref<3x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<3x!mqtopt.Qubit> - memref.store %q2_1, %qreg[%i2] : memref<3x!mqtopt.Qubit> - memref.dealloc %qreg : memref<3x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if an MCX gate on static qubits is parsed and handled correctly using different types of controls. -module { - // CHECK-LABEL: func.func @testMixedMCXOpStatic - func.func @testMixedMCXOpStatic() { - // CHECK: %[[Q1_1:.*]], %[[Q0_1:.*]], %[[Q2_1:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] nctrl %[[ANY:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - %q2_0 = mqtopt.qubit 2 - - //===------------------------------------------------------------------===// - // q0_0: ──■── q0_1 - // ┌─┴─┐ - // q1_0: ┤ X ├ q1_1 - // └─┬─┘ - // q2_0: ──○── q2_1 - //===----------------------------------------------------------------===// - - %q1_1, %q0_1, %q2_1 = mqtopt.x() %q1_0 ctrl %q0_0 nctrl %q2_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if two target gates on dynamic qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testTwoTargetOp - func.func @testTwoTargetOp() { - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.swap() %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_2:.*]]:2 = mqtopt.iswap() %[[Q01_1]]#0, %[[Q01_1]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_3:.*]]:2 = mqtopt.iswapdg() %[[Q01_2]]#0, %[[Q01_2]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_4:.*]]:2 = mqtopt.peres() %[[Q01_3]]#0, %[[Q01_3]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_5:.*]]:2 = mqtopt.peresdg() %[[Q01_4]]#0, %[[Q01_4]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_6:.*]]:2 = mqtopt.dcx() %[[Q01_5]]#0, %[[Q01_5]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_7:.*]]:2 = mqtopt.ecr() %[[Q01_6]]#0, %[[Q01_6]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.swap() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_2, %q1_2 = mqtopt.iswap() %q0_1, %q1_1 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_3, %q1_3 = mqtopt.iswapdg() %q0_2, %q1_2 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_4, %q1_4 = mqtopt.peres() %q0_3, %q1_3 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_5, %q1_5 = mqtopt.peresdg() %q0_4, %q1_4 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_6, %q1_6 = mqtopt.dcx() %q0_5, %q1_5 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_7, %q1_7 = mqtopt.ecr() %q0_6, %q1_6 : !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q0_7, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_7, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if two target gates on static qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testTwoTargetOpStatic - func.func @testTwoTargetOpStatic() { - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.swap() %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_2:.*]]:2 = mqtopt.iswap() %[[Q01_1]]#0, %[[Q01_1]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_3:.*]]:2 = mqtopt.iswapdg() %[[Q01_2]]#0, %[[Q01_2]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_4:.*]]:2 = mqtopt.peres() %[[Q01_3]]#0, %[[Q01_3]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_5:.*]]:2 = mqtopt.peresdg() %[[Q01_4]]#0, %[[Q01_4]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_6:.*]]:2 = mqtopt.dcx() %[[Q01_5]]#0, %[[Q01_5]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_7:.*]]:2 = mqtopt.ecr() %[[Q01_6]]#0, %[[Q01_6]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - - %q0_1, %q1_1 = mqtopt.swap() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_2, %q1_2 = mqtopt.iswap() %q0_1, %q1_1 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_3, %q1_3 = mqtopt.iswapdg() %q0_2, %q1_2 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_4, %q1_4 = mqtopt.peres() %q0_3, %q1_3 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_5, %q1_5 = mqtopt.peresdg() %q0_4, %q1_4 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_6, %q1_6 = mqtopt.dcx() %q0_5, %q1_5 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_7, %q1_7 = mqtopt.ecr() %q0_6, %q1_6 : !mqtopt.Qubit, !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if a controlled SWAP gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testControlledSWAPOp - func.func @testControlledSWAPOp() { - // CHECK: %[[Q01_1:.*]]:2, %[[Q2_1:.*]] = mqtopt.swap() %[[ANY:.*]], %[[ANY:.*]] ctrl %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<3x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<3x!mqtopt.Qubit> - %q2_0 = memref.load %qreg[%i2] : memref<3x!mqtopt.Qubit> - - %q0_1, %q1_1, %q2_1 = mqtopt.swap() %q0_0, %q1_0 ctrl %q2_0 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - - memref.store %q0_1, %qreg[%i0] : memref<3x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<3x!mqtopt.Qubit> - memref.store %q2_1, %qreg[%i2] : memref<3x!mqtopt.Qubit> - memref.dealloc %qreg : memref<3x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a negative controlled SWAP gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNegativeControlledSWAPOp - func.func @testNegativeControlledSWAPOp() { - // CHECK: %[[Q01_1:.*]]:2, %[[Q2_1:.*]] = mqtopt.swap() %[[ANY:.*]], %[[ANY:.*]] nctrl %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit nctrl !mqtopt.Qubit - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<3x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<3x!mqtopt.Qubit> - %q2_0 = memref.load %qreg[%i2] : memref<3x!mqtopt.Qubit> - - %q0_1, %q1_1, %q2_1 = mqtopt.swap() %q0_0, %q1_0 nctrl %q2_0 : !mqtopt.Qubit, !mqtopt.Qubit nctrl !mqtopt.Qubit - - memref.store %q0_1, %qreg[%i0] : memref<3x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<3x!mqtopt.Qubit> - memref.store %q2_1, %qreg[%i2] : memref<3x!mqtopt.Qubit> - memref.dealloc %qreg : memref<3x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a controlled SWAP gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testControlledSWAPOpStatic - func.func @testControlledSWAPOpStatic() { - // CHECK: %[[Q01_1:.*]]:2, %[[Q2_1:.*]] = mqtopt.swap() %[[ANY:.*]], %[[ANY:.*]] ctrl %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - %q2_0 = mqtopt.qubit 2 - - %q0_1, %q1_1, %q2_1 = mqtopt.swap() %q0_0, %q1_0 ctrl %q2_0 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if a negative controlled SWAP gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNegativeControlledSWAPOpStatic - func.func @testNegativeControlledSWAPOpStatic() { - // CHECK: %[[Q01_1:.*]]:2, %[[Q2_1:.*]] = mqtopt.swap() %[[ANY:.*]], %[[ANY:.*]] nctrl %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit nctrl !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - %q2_0 = mqtopt.qubit 2 - - %q0_1, %q1_1, %q2_1 = mqtopt.swap() %q0_0, %q1_0 nctrl %q2_0 : !mqtopt.Qubit, !mqtopt.Qubit nctrl !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if a mixed controlled SWAP gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMixedControlledSWAPOp - func.func @testMixedControlledSWAPOp() { - // CHECK: %[[Q01_1:.*]]:2, %[[Q2_1:.*]], %[[Q3_1:.*]] = mqtopt.swap() %[[ANY:.*]], %[[ANY:.*]] ctrl %[[ANY:.*]] nctrl %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - %i3 = arith.constant 3 : index - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<4x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<4x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<4x!mqtopt.Qubit> - %q2_0 = memref.load %qreg[%i2] : memref<4x!mqtopt.Qubit> - %q3_0 = memref.load %qreg[%i3] : memref<4x!mqtopt.Qubit> - - //===------------------------------------------------------------------===// - // ┌──────┐ - // q0_0: ┤ ├ q0_1 - // │ SWAP │ - // q1_0: ┤ ├ q1_1 - // └───┬──┘ - // q2_0: ────■─── q2_1 - // │ - // q3_0: ────■─── q3_1 - //===----------------------------------------------------------------===// - - %q0_1, %q1_1, %q2_1, %q3_1 = mqtopt.swap() %q0_0, %q1_0 ctrl %q2_0 nctrl %q3_0 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - memref.store %q0_1, %qreg[%i0] : memref<4x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<4x!mqtopt.Qubit> - memref.store %q2_1, %qreg[%i2] : memref<4x!mqtopt.Qubit> - memref.store %q3_1, %qreg[%i3] : memref<4x!mqtopt.Qubit> - memref.dealloc %qreg : memref<4x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a mixed controlled SWAP gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMixedControlledSWAPOpStatic - func.func @testMixedControlledSWAPOpStatic() { - // CHECK: %[[Q01_1:.*]]:2, %[[Q2_1:.*]], %[[Q3_1:.*]] = mqtopt.swap() %[[ANY:.*]], %[[ANY:.*]] ctrl %[[ANY:.*]] nctrl %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - %q2_0 = mqtopt.qubit 2 - %q3_0 = mqtopt.qubit 3 - - //===------------------------------------------------------------------===// - // ┌──────┐ - // q0_0: ┤ ├ q0_1 - // │ SWAP │ - // q1_0: ┤ ├ q1_1 - // └───┬──┘ - // q2_0: ────■─── q2_1 - // │ - // q3_0: ────■─── q3_1 - //===----------------------------------------------------------------===// - - %q0_1, %q1_1, %q2_1, %q3_1 = mqtopt.swap() %q0_0, %q1_0 ctrl %q2_0 nctrl %q3_0 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if parameterized multiple qubit gates on dynamic qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMultipleQubitRotationOp - func.func @testMultipleQubitRotationOp() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.rxx(%[[C0_F64]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_2:.*]]:2 = mqtopt.ryy(%[[C0_F64]]) %[[Q01_1]]#0, %[[Q01_1]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_3:.*]]:2 = mqtopt.rzz(%[[C0_F64]]) %[[Q01_2]]#0, %[[Q01_2]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_4:.*]]:2 = mqtopt.rzx(%[[C0_F64]]) %[[Q01_3]]#0, %[[Q01_3]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_5:.*]]:2 = mqtopt.xx_minus_yy(%[[C0_F64]], %[[C0_F64]]) %[[Q01_4]]#0, %[[Q01_4]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_6:.*]]:2 = mqtopt.xx_plus_yy(%[[C0_F64]], %[[C0_F64]]) %[[Q01_5]]#0, %[[Q01_5]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - %q01_1:2 = mqtopt.rxx(%c0_f64) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_2:2 = mqtopt.ryy(%c0_f64) %q01_1#0, %q01_1#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_3:2 = mqtopt.rzz(%c0_f64) %q01_2#0, %q01_2#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_4:2 = mqtopt.rzx(%c0_f64) %q01_3#0, %q01_3#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_5:2 = mqtopt.xx_minus_yy(%c0_f64, %c0_f64) %q01_4#0, %q01_4#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_6:2 = mqtopt.xx_plus_yy(%c0_f64, %c0_f64) %q01_5#0, %q01_5#1 : !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q01_6#0, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q01_6#1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if parameterized multiple qubit gates on static qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMultipleQubitRotationOpStatic - func.func @testMultipleQubitRotationOpStatic() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.rxx(%[[C0_F64]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_2:.*]]:2 = mqtopt.ryy(%[[C0_F64]]) %[[Q01_1]]#0, %[[Q01_1]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_3:.*]]:2 = mqtopt.rzz(%[[C0_F64]]) %[[Q01_2]]#0, %[[Q01_2]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_4:.*]]:2 = mqtopt.rzx(%[[C0_F64]]) %[[Q01_3]]#0, %[[Q01_3]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_5:.*]]:2 = mqtopt.xx_minus_yy(%[[C0_F64]], %[[C0_F64]]) %[[Q01_4]]#0, %[[Q01_4]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_6:.*]]:2 = mqtopt.xx_plus_yy(%[[C0_F64]], %[[C0_F64]]) %[[Q01_5]]#0, %[[Q01_5]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - - %c0_f64 = arith.constant 3.000000e-01 : f64 - %q01_1:2 = mqtopt.rxx(%c0_f64) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_2:2 = mqtopt.ryy(%c0_f64) %q01_1#0, %q01_1#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_3:2 = mqtopt.rzz(%c0_f64) %q01_2#0, %q01_2#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_4:2 = mqtopt.rzx(%c0_f64) %q01_3#0, %q01_3#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_5:2 = mqtopt.xx_minus_yy(%c0_f64, %c0_f64) %q01_4#0, %q01_4#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_6:2 = mqtopt.xx_plus_yy(%c0_f64, %c0_f64) %q01_5#0, %q01_5#1 : !mqtopt.Qubit, !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if parameterized multiple qubit gates on dynamic qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testControlledMultipleQubitRotationOp - func.func @testControlledMultipleQubitRotationOp() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: %[[Q01_1:.*]]:2, %[[Q2_1:.*]] = mqtopt.rxx(%[[C0_F64]]) %[[ANY:.*]], %[[ANY:.*]] ctrl %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q01_2:.*]]:2, %[[Q2_2:.*]] = mqtopt.ryy(%[[C0_F64]]) %[[Q01_1]]#0, %[[Q01_1]]#1 ctrl %[[Q2_1]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q01_3:.*]]:2, %[[Q2_3:.*]] = mqtopt.rzz(%[[C0_F64]]) %[[Q01_2]]#0, %[[Q01_2]]#1 ctrl %[[Q2_2]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q01_4:.*]]:2, %[[Q2_4:.*]] = mqtopt.rzx(%[[C0_F64]]) %[[Q01_3]]#0, %[[Q01_3]]#1 ctrl %[[Q2_3]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q01_5:.*]]:2, %[[Q2_5:.*]] = mqtopt.xx_minus_yy(%[[C0_F64]], %[[C0_F64]]) %[[Q01_4]]#0, %[[Q01_4]]#1 ctrl %[[Q2_4]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q01_6:.*]]:2, %[[Q2_6:.*]] = mqtopt.xx_plus_yy(%[[C0_F64]], %[[C0_F64]]) %[[Q01_5]]#0, %[[Q01_5]]#1 ctrl %[[Q2_5]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<3x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<3x!mqtopt.Qubit> - %q2_0 = memref.load %qreg[%i2] : memref<3x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - %q01_1:2, %q2_1 = mqtopt.rxx(%c0_f64) %q0_0, %q1_0 ctrl %q2_0 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - %q01_2:2, %q2_2 = mqtopt.ryy(%c0_f64) %q01_1#0, %q01_1#1 ctrl %q2_1 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - %q01_3:2, %q2_3 = mqtopt.rzz(%c0_f64) %q01_2#0, %q01_2#1 ctrl %q2_2 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - %q01_4:2, %q2_4 = mqtopt.rzx(%c0_f64) %q01_3#0, %q01_3#1 ctrl %q2_3 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - %q01_5:2, %q2_5 = mqtopt.xx_minus_yy(%c0_f64, %c0_f64) %q01_4#0, %q01_4#1 ctrl %q2_4 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - %q01_6:2, %q2_6 = mqtopt.xx_plus_yy(%c0_f64, %c0_f64) %q01_5#0, %q01_5#1 ctrl %q2_5 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - - memref.store %q01_6#0, %qreg[%i0] : memref<3x!mqtopt.Qubit> - memref.store %q01_6#1, %qreg[%i1] : memref<3x!mqtopt.Qubit> - memref.store %q2_6, %qreg[%i2] : memref<3x!mqtopt.Qubit> - memref.dealloc %qreg : memref<3x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if parameterized multiple qubit gates on static qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testControlledMultipleQubitRotationOp - func.func @testControlledMultipleQubitRotationOp() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: %[[Q01_1:.*]]:2, %[[Q2_1:.*]] = mqtopt.rxx(%[[C0_F64]]) %[[ANY:.*]], %[[ANY:.*]] ctrl %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q01_2:.*]]:2, %[[Q2_2:.*]] = mqtopt.ryy(%[[C0_F64]]) %[[Q01_1]]#0, %[[Q01_1]]#1 ctrl %[[Q2_1]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q01_3:.*]]:2, %[[Q2_3:.*]] = mqtopt.rzz(%[[C0_F64]]) %[[Q01_2]]#0, %[[Q01_2]]#1 ctrl %[[Q2_2]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q01_4:.*]]:2, %[[Q2_4:.*]] = mqtopt.rzx(%[[C0_F64]]) %[[Q01_3]]#0, %[[Q01_3]]#1 ctrl %[[Q2_3]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q01_5:.*]]:2, %[[Q2_5:.*]] = mqtopt.xx_minus_yy(%[[C0_F64]], %[[C0_F64]]) %[[Q01_4]]#0, %[[Q01_4]]#1 ctrl %[[Q2_4]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q01_6:.*]]:2, %[[Q2_6:.*]] = mqtopt.xx_plus_yy(%[[C0_F64]], %[[C0_F64]]) %[[Q01_5]]#0, %[[Q01_5]]#1 ctrl %[[Q2_5]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - %q2_0 = mqtopt.qubit 2 - - %c0_f64 = arith.constant 3.000000e-01 : f64 - %q01_1:2, %q2_1 = mqtopt.rxx(%c0_f64) %q0_0, %q1_0 ctrl %q2_0 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - %q01_2:2, %q2_2 = mqtopt.ryy(%c0_f64) %q01_1#0, %q01_1#1 ctrl %q2_1 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - %q01_3:2, %q2_3 = mqtopt.rzz(%c0_f64) %q01_2#0, %q01_2#1 ctrl %q2_2 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - %q01_4:2, %q2_4 = mqtopt.rzx(%c0_f64) %q01_3#0, %q01_3#1 ctrl %q2_3 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - %q01_5:2, %q2_5 = mqtopt.xx_minus_yy(%c0_f64, %c0_f64) %q01_4#0, %q01_4#1 ctrl %q2_4 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - %q01_6:2, %q2_6 = mqtopt.xx_plus_yy(%c0_f64, %c0_f64) %q01_5#0, %q01_5#1 ctrl %q2_5 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - - return - } -} - -// ----- -// This test expects an error to be thrown when parsing a controlled operation. -module { - func.func @testCtrlOpMismatchInOutputs() { - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - // expected-error@+1 {{operation defines 2 results but was provided 1 to bind}} - %q1_1 = mqtopt.x() %q1_0 ctrl %q0_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - return - } -} - -// ----- -// This test expects an error to be thrown when parsing a controlled operation using an invalid format. -module { - func.func @testCtrlOpInvalidFormat() { - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - // expected-error@+1 {{number of positively-controlling input qubits (0) and positively-controlling output qubits (1) must be the same}} - %q1_1, %q0_1 = mqtopt.x() %q1_0 ctrl : !mqtopt.Qubit ctrl !mqtopt.Qubit - - return - } -} - -// ----- -// This test expects an error to be thrown when parsing a controlled operation using an invalid format. -module { - func.func @testNegCtrlOpInvalidFormat() { - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - // expected-error@+1 {{number of negatively-controlling input qubits (0) and negatively-controlling output qubits (1) must be the same}} - %q1_1, %q0_1 = mqtopt.x() %q1_0 nctrl : !mqtopt.Qubit nctrl !mqtopt.Qubit - - return - } -} - -// ----- -// This test expects an error to be thrown when parsing a parameterised operation. -module { - func.func @testParamOpInvalidFormat() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - - // expected-error@+1 {{operation expects exactly 3 parameters but got 2}} - %q_1 = mqtopt.u(%c0_f64, %c0_f64) %q_0 : !mqtopt.Qubit - - return - } -} - -// ----- -// This test checks if a no-target arity constraint operation detects correctly when a target is provided. -module { - func.func @testNoTargetContainsTarget() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+1 {{number of input qubits (1) must be 0}} - %q_1 = mqtopt.gphase(%c0_f64) %q_0 : !mqtopt.Qubit - - memref.store %q_1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if static parameters for rotation operations are parsed correctly. -module { - // CHECK-LABEL: func.func @testStaticParameters - func.func @testStaticParameters() { - // CHECK: %[[ANY:.*]] = mqtopt.u(static [1.000000e-01, 2.000000e-01, 3.000000e-01]) %[[ANY:.*]] : !mqtopt.Qubit - // CHECK: %[[ANY:.*]] = mqtopt.u(static [1.000000e-01, 2.000000e-01, 3.000000e-01] mask [true, true, true]) %[[ANY:.*]] : !mqtopt.Qubit - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q_1 = mqtopt.u(static [1.00000e-01, 2.00000e-01, 3.00000e-01]) %q_0 : !mqtopt.Qubit - %q_2 = mqtopt.u(static [1.00000e-01, 2.00000e-01, 3.00000e-01] mask [true, true, true]) %q_1 : !mqtopt.Qubit - - memref.store %q_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if static parameters together with dynamic parameters for rotation operations are parsed correctly. -module { - // CHECK-LABEL: func.func @testStaticAndDynamicParameters - func.func @testStaticAndDynamicParameters() { - // CHECK: %[[ANY:.*]] = mqtopt.u(%[[ANY:.*]] static [1.000000e-01, 2.000000e-01] mask [true, false, true]) %[[ANY:.*]] : !mqtopt.Qubit - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - %q_1 = mqtopt.u(%c0_f64 static [1.00000e-01, 2.00000e-01] mask [true, false, true]) %q_0 : !mqtopt.Qubit - - memref.store %q_1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if static parameters and dynamic parameters surpassing the limit of parameters together is detected correctly. -module { - func.func @testTooManyStaticAndDynamicParameters() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+1 {{operation expects exactly 3 parameters but got 4}} - %q_1 = mqtopt.u(%c0_f64, %c0_f64 static [1.00000e-01, 2.00000e-01] mask [true, false, true]) %q_0 : !mqtopt.Qubit - - memref.store %q_1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if static parameters and dynamic parameters being passed without a mask is detected correctly. -module { - func.func @testStaticAndDynamicParametersNoMask() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+1 {{operation has mixed dynamic and static parameters but no parameter mask}} - %q_1 = mqtopt.u(%c0_f64 static [1.00000e-01, 2.00000e-01]) %q_0 : !mqtopt.Qubit - - memref.store %q_1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a static parameter mask with incorrect size is detected correctly. -module { - func.func @testStaticAndDynamicParametersWrongSizeMask() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+1 {{operation expects exactly 3 parameters but has a parameter mask with 2 entries}} - %q_1 = mqtopt.u(%c0_f64 static [1.00000e-01, 2.00000e-01] mask [true, true]) %q_0 : !mqtopt.Qubit - - memref.store %q_1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a static parameter mask with an incorrect number of true entries is detected correctly. -module { - func.func @testStaticAndDynamicParametersIncorrectTrueEntriesInMask() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+1 {{operation has 2 static parameter(s) but has a parameter mask with 3 true entries}} - %q_1 = mqtopt.u(%c0_f64 static [1.00000e-01, 2.00000e-01] mask [true, true, true]) %q_0 : !mqtopt.Qubit - - memref.store %q_1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a static parameter mask with `true` parameters even though the operation has no static parameters is detected correctly. -module { - func.func @testParametersMaskWithTrueEntriesButNoStaticParameters() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+1 {{operation has no static parameter but has a parameter mask with 1 true entries}} - %q_1 = mqtopt.u(%c0_f64, %c0_f64, %c0_f64 static [] mask [true, false, false]) %q_0 : !mqtopt.Qubit - - memref.store %q_1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if a no-control gate being passed a control is detected correctly. -module { - func.func @testNoControlWithControl() { - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - // expected-error@+1 {{'mqtopt.barrier' op Gate marked as NoControl should not have control qubits}} - %q0_1, %q1_1 = mqtopt.barrier() %q0_0 ctrl %q1_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - memref.store %q0_1, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if incorrect output type format is detected correctly when `)` is missing. -module { - func.func @testOutputTypeFormatErrorMissingClosingParenthesis() { - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+1 {{expected ')'}} - mqtopt.gphase(%c0_f64) : ( - - return - } -} - -// ----- -// This test checks if incorrect output type format is detected correctly when the type list ends with a `,`. -module { - func.func @testOutputTypeFormatErrorEndsWithComma() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - // expected-error@+1 {{expected non-function type}} - %q_1 = mqtopt.i() %q_0 : !mqtopt.Qubit, - - memref.store %q_1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if incorrect output type format is detected correctly when the `ctrl` keyword is provided without types. -module { - func.func @testOutputTypeFormatCtrlKeywordWithoutTypes() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - // expected-error@+1 {{expected non-function type}} - %q_1 = mqtopt.i() %q_0 : !mqtopt.Qubit ctrl - // expected-error@+2 {{custom op 'mqtopt.i' expected at least one type after `ctrl` keyword}} - - memref.store %q_1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if incorrect output type format is detected correctly when the `nctrl` keyword is provided without types. -module { - func.func @testOutputTypeFormatNctrlKeywordWithoutTypes() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - // expected-error@+1 {{expected non-function type}} - %q_1 = mqtopt.i() %q_0 : !mqtopt.Qubit nctrl - // expected-error@+2 {{custom op 'mqtopt.i' expected at least one type after `nctrl` keyword}} - - memref.store %q_1, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if incorrect output type format is detected correctly when nothing is provided after `:`. -module { - func.func @testOutputTypeFormatErrorNoTypingInformation() { - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+3 {{custom op 'mqtopt.gphase' expected at least one type after `:`}} - mqtopt.gphase(%c0_f64) : - - return - } -} - -// ----- -// This test checks if a Bell state is parsed and handled correctly by using many instructions tested above. -module { - // CHECK-LABEL: func.func @bellState - func.func @bellState() { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.h() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q1_1:.*]], %[[Q0_2:.*]] = mqtopt.x() %[[Q1_0:.*]] ctrl %[[Q0_1:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_3:.*]], [[M0_0:.*]] = mqtopt.measure %[[Q0_2]] - // CHECK: %[[Q1_2:.*]], %[[M1_0:.*]] = mqtopt.measure %[[Q1_1]] - // CHECK: memref.store %[[Q0_3:.*]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2:.*]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.dealloc %[[Qreg:.*]] : memref<2x!mqtopt.Qubit> - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.x() %q1_0 ctrl %q0_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %m0_0 = mqtopt.measure %q0_2 - %q1_2, %m1_0 = mqtopt.measure %q1_1 - - memref.store %q0_3, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/basics.mlir b/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/basics.mlir deleted file mode 100644 index fb6979f49f..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/basics.mlir +++ /dev/null @@ -1,376 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// Instead of applying checks, the routing verifier pass ensures the validity of this program. - -// RUN: quantum-opt %s -split-input-file --pass-pipeline="builtin.module(placement-sc{strategy=identity arch=MQTTest}, route-naive-sc{arch=MQTTest},verify-routing-sc{arch=MQTTest})" -verify-diagnostics | FileCheck %s -// RUN: quantum-opt %s -split-input-file --pass-pipeline="builtin.module(placement-sc{strategy=identity arch=MQTTest}, route-astar-sc{arch=MQTTest},verify-routing-sc{arch=MQTTest})" -verify-diagnostics | FileCheck %s -// RUN: quantum-opt %s -split-input-file --pass-pipeline="builtin.module(placement-sc{strategy=identity arch=IBMFalcon}, route-naive-sc{arch=IBMFalcon},verify-routing-sc{arch=IBMFalcon})" -verify-diagnostics | FileCheck %s -// RUN: quantum-opt %s -split-input-file --pass-pipeline="builtin.module(placement-sc{strategy=identity arch=IBMFalcon}, route-astar-sc{arch=IBMFalcon},verify-routing-sc{arch=IBMFalcon})" -verify-diagnostics | FileCheck %s - -module { - // CHECK-LABEL: func.func @entrySABRE - func.func @entrySABRE() attributes {passthrough = ["entry_point"]} { - - // - // Figure 4 in SABRE Paper "Tackling the Qubit Mapping Problem for NISQ-Era Quantum Devices". - // - // ┌───┐ - // 0: ───────■────────────┤ 8 ├ - // ┌─┴─┐ └─┬─┘ - // 1: ──■──┤ 3 ├──■─────────┼── - // ┌─┴─┐└───┘┌─┴─┐ │ - // 2: ┤ 1 ├──■──┤ 5 ├───────┼── - // └───┘┌─┴─┐└───┘ │ - // 3: ─────┤ 4 ├──■────■────■── - // └───┘ │ ┌─┴─┐ - // 4: ──■─────────┼──┤ 7 ├───── - // ┌─┴─┐ ┌─┴─┐└───┘ - // 5: ┤ 2 ├─────┤ 6 ├────────── - // └───┘ └───┘ - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - %q3_0 = mqtopt.allocQubit - %q4_0 = mqtopt.allocQubit - %q5_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q1_1 = mqtopt.h() %q1_0 : !mqtopt.Qubit - %q4_1 = mqtopt.h() %q4_0 : !mqtopt.Qubit - - %q0_2 = mqtopt.z() %q0_1 : !mqtopt.Qubit - %q2_1, %q1_2 = mqtopt.x() %q2_0 ctrl %q1_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g1 - %q5_1, %q4_2 = mqtopt.x() %q5_0 ctrl %q4_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g2 - - %q1_3, %q0_3 = mqtopt.x() %q1_2 ctrl %q0_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g3 - %q3_1, %q2_2 = mqtopt.x() %q3_0 ctrl %q2_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g4 - - %q2_3 = mqtopt.h() %q2_2 : !mqtopt.Qubit - %q3_2 = mqtopt.h() %q3_1 : !mqtopt.Qubit - - %q2_4, %q1_4 = mqtopt.x() %q2_3 ctrl %q1_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g5 - %q5_2, %q3_3 = mqtopt.x() %q5_1 ctrl %q3_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g6 - - %q3_4 = mqtopt.z() %q3_3 : !mqtopt.Qubit - - %q3_5, %q4_3 = mqtopt.x() %q3_4 ctrl %q4_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g7 - - %q0_4, %q3_6 = mqtopt.x() %q0_3 ctrl %q3_5 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g8 - - mqtopt.deallocQubit %q0_4 - mqtopt.deallocQubit %q1_4 - mqtopt.deallocQubit %q2_4 - mqtopt.deallocQubit %q3_6 - mqtopt.deallocQubit %q4_3 - mqtopt.deallocQubit %q5_2 - - return - } - - // CHECK-LABEL: func.func @entryBell - func.func @entryBell() attributes {passthrough = ["entry_point"]} { - - // - // The bell state. - // - // This test shows that alloc's don't have to be grouped. - // - // ┌───┐ - // 0:─┤ H ├────■─── - // └───┘ ┌─┴─┐ - // 1:────────┤ X ├─ - // └───┘ - - %q0_0 = mqtopt.allocQubit - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - - %q1_0 = mqtopt.allocQubit - %q1_1, %q0_2 = mqtopt.x() %q1_0 ctrl %q0_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_3, %m0_0 = "mqtopt.measure"(%q0_2) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - %q1_2, %m1_0 = "mqtopt.measure"(%q1_1) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - - %q0_4, %q1_3 = mqtopt.barrier() %q0_3, %q1_2 : !mqtopt.Qubit, !mqtopt.Qubit - - mqtopt.deallocQubit %q0_4 - mqtopt.deallocQubit %q1_3 - - return - } - - // CHECK-LABEL: func.func @entryBellLoop - func.func @entryBellLoop() attributes {passthrough = ["entry_point"]} { - - // - // Bell in a loop. - // - // This test shows that the routing algorithm can handle - // alloc statements inside the loop body. - // - // ┌ ┌───┐ ┐^1000 - // │ 0:─┤ H ├────■─── │ - // │ └───┘ ┌─┴─┐ │ - // │ 1:────────┤ X ├─ │ - // └ └───┘ ┘ - - %lb = index.constant 0 - %ub = index.constant 1000 - %step = index.constant 1 - - scf.for %iv = %lb to %ub step %step { - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.x() %q1_0 ctrl %q0_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_3, %m0_0 = "mqtopt.measure"(%q0_2) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - %q1_2, %m1_0 = "mqtopt.measure"(%q1_1) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_2 - } - - return - } - - // CHECK-LABEL: func.func @entryGHZ - func.func @entryGHZ() attributes {passthrough = ["entry_point"]} { - - // - // GHZ in a loop. - // - // This test shows that the routing algorithm can handle - // loop-carried qubit values. - // - // ┌ ┌───┐ ┐^1000 - // │ 0:─┤ H ├────■─────────── │ - // │ └───┘ ┌─┴─┐ │ - // │ 1:────────┤ X ├────■──── │ - // │ └───┘ ┌─┴─┐ │ - // │ 2:───────────────┤ X ├── │ - // └ └───┘ ┘ - - %lb = index.constant 0 - %ub = index.constant 1000 - %step = index.constant 1 - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q0_1, %q1_1, %q2_1 = scf.for %iv = %lb to %ub step %step - iter_args(%q0_i_0 = %q0_0, %q1_i_0 = %q1_0, %q2_i_0 = %q2_0) -> (!mqtopt.Qubit, !mqtopt.Qubit, !mqtopt.Qubit) { - %q0_i_1 = "mqtopt.reset"(%q0_i_0) : (!mqtopt.Qubit) -> !mqtopt.Qubit - %q1_i_1 = "mqtopt.reset"(%q1_i_0) : (!mqtopt.Qubit) -> !mqtopt.Qubit - %q2_i_1 = "mqtopt.reset"(%q2_i_0) : (!mqtopt.Qubit) -> !mqtopt.Qubit - - %q0_i_2 = mqtopt.h() %q0_i_1 : !mqtopt.Qubit - %q1_i_2, %q0_i_3 = mqtopt.x() %q1_i_1 ctrl %q0_i_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q2_i_2, %q1_i_3 = mqtopt.x() %q2_i_1 ctrl %q1_i_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_i_4, %m0 = "mqtopt.measure"(%q0_i_3) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - %q1_i_4, %m1 = "mqtopt.measure"(%q1_i_3) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - %q2_i_3, %m2 = "mqtopt.measure"(%q2_i_2) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - - scf.yield %q0_i_4, %q1_i_4, %q2_i_3 : !mqtopt.Qubit, !mqtopt.Qubit, !mqtopt.Qubit - } - - mqtopt.deallocQubit %q0_1 - mqtopt.deallocQubit %q1_1 - mqtopt.deallocQubit %q2_1 - - return - } - - // CHECK-LABEL: func.func @entryBranching - func.func @entryBranching() attributes {passthrough = ["entry_point"]} { - - // - // This test shows that the routing algorithm can handle - // classical feedforward and control flow. - // - // ┌───┐ ┌───┐ - // 0:─┤ H ├──┤ M ├─────────■─── - // └───┘ └─╦─┘ ┌───┐ ┌─┴─┐ - // 1:──────────║───┤ X ├─┤ X ├─ - // ║ └─┬─┘ └───┘ - // m:══════════▼═════●═════════ - // - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q0_2, %m = "mqtopt.measure"(%q0_1) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - - %q0_3, %q1_2 = scf.if %m -> (!mqtopt.Qubit, !mqtopt.Qubit) { - %q1_1 = mqtopt.x() %q1_0 : !mqtopt.Qubit - %q1_2, %q0_3 = mqtopt.x() %q1_1 ctrl %q0_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - scf.yield %q0_3, %q1_2 : !mqtopt.Qubit, !mqtopt.Qubit - } else { - %q1_1 = mqtopt.i() %q1_0 : !mqtopt.Qubit - %q1_2, %q0_3 = mqtopt.x() %q1_1 ctrl %q0_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - scf.yield %q0_3, %q1_2 : !mqtopt.Qubit, !mqtopt.Qubit - } - - %q0_4 = mqtopt.x() %q0_3 : !mqtopt.Qubit - - mqtopt.deallocQubit %q0_4 - mqtopt.deallocQubit %q1_2 - - return - } - - // CHECK-LABEL: func.func @entryAll - func.func @entryAll() attributes {passthrough = ["entry_point"]} { - - // - // All of the above quantum computations in a single entry point. - // This test shows that the algorithm can handle multiple computations - // in a single function. - - // - // SABRE - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - %q3_0 = mqtopt.allocQubit - %q4_0 = mqtopt.allocQubit - %q5_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q1_1 = mqtopt.h() %q1_0 : !mqtopt.Qubit - %q4_1 = mqtopt.h() %q4_0 : !mqtopt.Qubit - - %q0_2 = mqtopt.z() %q0_1 : !mqtopt.Qubit - %q2_1, %q1_2 = mqtopt.x() %q2_0 ctrl %q1_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g1 - %q5_1, %q4_2 = mqtopt.x() %q5_0 ctrl %q4_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g2 - - %q1_3, %q0_3 = mqtopt.x() %q1_2 ctrl %q0_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g3 - %q3_1, %q2_2 = mqtopt.x() %q3_0 ctrl %q2_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g4 - - %q2_3 = mqtopt.h() %q2_2 : !mqtopt.Qubit - %q3_2 = mqtopt.h() %q3_1 : !mqtopt.Qubit - - %q2_4, %q1_4 = mqtopt.x() %q2_3 ctrl %q1_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g5 - %q5_2, %q3_3 = mqtopt.x() %q5_1 ctrl %q3_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g6 - - %q3_4 = mqtopt.z() %q3_3 : !mqtopt.Qubit - - %q3_5, %q4_3 = mqtopt.x() %q3_4 ctrl %q4_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g7 - - %q0_4, %q3_6 = mqtopt.x() %q0_3 ctrl %q3_5 : !mqtopt.Qubit ctrl !mqtopt.Qubit // g8 - - mqtopt.deallocQubit %q0_4 - mqtopt.deallocQubit %q1_4 - mqtopt.deallocQubit %q2_4 - mqtopt.deallocQubit %q3_6 - mqtopt.deallocQubit %q4_3 - mqtopt.deallocQubit %q5_2 - - // - // The bell state. - - %q0_0_bell = mqtopt.allocQubit - %q0_1_bell = mqtopt.h() %q0_0_bell : !mqtopt.Qubit - - %q1_0_bell = mqtopt.allocQubit - %q1_1_bell, %q0_2_bell = mqtopt.x() %q1_0_bell ctrl %q0_1_bell : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_3_bell, %m0_0_bell = "mqtopt.measure"(%q0_2_bell) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - %q1_2_bell, %m1_0_bell = "mqtopt.measure"(%q1_1_bell) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - - mqtopt.deallocQubit %q0_3_bell - mqtopt.deallocQubit %q1_2_bell - - // - // Bell in a loop. - - %lb = index.constant 0 - %ub = index.constant 1000 - %step = index.constant 1 - - scf.for %iv = %lb to %ub step %step { - %q0_0_bell1000 = mqtopt.allocQubit - %q1_0_bell1000 = mqtopt.allocQubit - - %q0_1_bell1000 = mqtopt.h() %q0_0_bell1000 : !mqtopt.Qubit - %q1_1_bell1000, %q0_2_bell1000 = mqtopt.x() %q1_0_bell1000 ctrl %q0_1_bell1000 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_3_bell1000, %m0_0_bell1000 = "mqtopt.measure"(%q0_2_bell1000) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - %q1_2_bell1000, %m1_0_bell1000 = "mqtopt.measure"(%q1_1_bell1000) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - - mqtopt.deallocQubit %q0_3_bell1000 - mqtopt.deallocQubit %q1_2_bell1000 - } - - // - // GHZ in a loop. - - %q0_0_ghz1000 = mqtopt.allocQubit - %q1_0_ghz1000 = mqtopt.allocQubit - %q2_0_ghz1000 = mqtopt.allocQubit - - %q0_1_ghz1000, %q1_1_ghz1000, %q2_1_ghz1000 = scf.for %iv = %lb to %ub step %step - iter_args(%q0_i_0 = %q0_0_ghz1000, %q1_i_0 = %q1_0_ghz1000, %q2_i_0 = %q2_0_ghz1000) -> (!mqtopt.Qubit, !mqtopt.Qubit, !mqtopt.Qubit) { - %q0_i_1 = "mqtopt.reset"(%q0_i_0) : (!mqtopt.Qubit) -> !mqtopt.Qubit - %q1_i_1 = "mqtopt.reset"(%q1_i_0) : (!mqtopt.Qubit) -> !mqtopt.Qubit - %q2_i_1 = "mqtopt.reset"(%q2_i_0) : (!mqtopt.Qubit) -> !mqtopt.Qubit - - %q0_i_2 = mqtopt.h() %q0_i_1 : !mqtopt.Qubit - %q1_i_2, %q0_i_3 = mqtopt.x() %q1_i_1 ctrl %q0_i_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q2_i_2, %q1_i_3 = mqtopt.x() %q2_i_1 ctrl %q1_i_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_i_4, %m0 = "mqtopt.measure"(%q0_i_3) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - %q1_i_4, %m1 = "mqtopt.measure"(%q1_i_3) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - %q2_i_3, %m2 = "mqtopt.measure"(%q2_i_2) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - - scf.yield %q0_i_4, %q1_i_4, %q2_i_3 : !mqtopt.Qubit, !mqtopt.Qubit, !mqtopt.Qubit - } - - mqtopt.deallocQubit %q0_1_ghz1000 - mqtopt.deallocQubit %q1_1_ghz1000 - mqtopt.deallocQubit %q2_1_ghz1000 - - %q0_0_branch = mqtopt.allocQubit - %q1_0_branch = mqtopt.allocQubit - - %q0_1_branch = mqtopt.h() %q0_0_branch : !mqtopt.Qubit - %q0_2_branch, %m = "mqtopt.measure"(%q0_1_branch) : (!mqtopt.Qubit) -> (!mqtopt.Qubit, i1) - - %q0_3_branch, %q1_2_branch = scf.if %m -> (!mqtopt.Qubit, !mqtopt.Qubit) { - %q1_1_branch = mqtopt.x() %q1_0_branch : !mqtopt.Qubit - %q1_2_branch, %q0_3_branch = mqtopt.x() %q1_1_branch nctrl %q0_2_branch : !mqtopt.Qubit nctrl !mqtopt.Qubit - - scf.yield %q0_3_branch, %q1_2_branch : !mqtopt.Qubit, !mqtopt.Qubit - } else { - %q1_1_branch = mqtopt.i() %q1_0_branch : !mqtopt.Qubit - %q0_3_branch, %q1_2_branch = mqtopt.swap() %q1_1_branch, %q0_2_branch : !mqtopt.Qubit, !mqtopt.Qubit - - scf.yield %q0_3_branch, %q1_2_branch : !mqtopt.Qubit, !mqtopt.Qubit - } - - mqtopt.deallocQubit %q0_3_branch - mqtopt.deallocQubit %q1_2_branch - - return - } - - // CHECK-LABEL: func.func @noEntryPoint - func.func @noEntryPoint() { - // CHECK: %[[ANY:.*]] = mqtopt.allocQubit - %q0 = mqtopt.allocQubit - return - } -} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/grover_5.mlir b/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/grover_5.mlir deleted file mode 100644 index e500b282ed..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/grover_5.mlir +++ /dev/null @@ -1,687 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// Instead of applying checks, the routing verifier pass ensures the validity of this program. - -// RUN: quantum-opt %s -split-input-file --pass-pipeline="builtin.module(placement-sc{strategy=identity arch=MQTTest}, route-naive-sc{arch=MQTTest},verify-routing-sc{arch=MQTTest})" -verify-diagnostics | FileCheck %s -// RUN: quantum-opt %s -split-input-file --pass-pipeline="builtin.module(placement-sc{strategy=identity arch=MQTTest}, route-astar-sc{arch=MQTTest},verify-routing-sc{arch=MQTTest})" -verify-diagnostics | FileCheck %s -// RUN: quantum-opt %s -split-input-file --pass-pipeline="builtin.module(placement-sc{strategy=identity arch=IBMFalcon}, route-naive-sc{arch=IBMFalcon},verify-routing-sc{arch=IBMFalcon})" -verify-diagnostics | FileCheck %s -// RUN: quantum-opt %s -split-input-file --pass-pipeline="builtin.module(placement-sc{strategy=identity arch=IBMFalcon}, route-astar-sc{arch=IBMFalcon},verify-routing-sc{arch=IBMFalcon})" -verify-diagnostics | FileCheck %s - -module { - // CHECK-LABEL: func.func @main - func.func @main() attributes {passthrough = ["entry_point"]} { - %0 = mqtopt.allocQubit - %1 = mqtopt.allocQubit - %2 = mqtopt.allocQubit - %3 = mqtopt.allocQubit - %4 = mqtopt.allocQubit - - %alloca = memref.alloca() : memref<5xi1> - %out_qubits = mqtopt.rz(static [1.570796e+00]) %0 : !mqtopt.Qubit - %out_qubits_2 = mqtopt.sx() %out_qubits : !mqtopt.Qubit - %out_qubits_3 = mqtopt.rz(static [1.570796e+00]) %out_qubits_2 : !mqtopt.Qubit - %out_qubits_4 = mqtopt.rz(static [1.570796e+00]) %1 : !mqtopt.Qubit - %out_qubits_5 = mqtopt.sx() %out_qubits_4 : !mqtopt.Qubit - %out_qubits_6 = mqtopt.rz(static [1.570796e+00]) %out_qubits_5 : !mqtopt.Qubit - %out_qubits_7 = mqtopt.rz(static [1.570796e+00]) %2 : !mqtopt.Qubit - %out_qubits_8 = mqtopt.sx() %out_qubits_7 : !mqtopt.Qubit - %out_qubits_9 = mqtopt.rz(static [1.570796e+00]) %out_qubits_8 : !mqtopt.Qubit - %out_qubits_10 = mqtopt.rz(static [1.570796e+00]) %3 : !mqtopt.Qubit - %out_qubits_11 = mqtopt.sx() %out_qubits_10 : !mqtopt.Qubit - %out_qubits_12 = mqtopt.rz(static [1.570796e+00]) %out_qubits_11 : !mqtopt.Qubit - %out_qubits_13 = mqtopt.x() %4 : !mqtopt.Qubit - %out_qubits_14 = mqtopt.rz(static [1.570796e+00]) %out_qubits_13 : !mqtopt.Qubit - %out_qubits_15 = mqtopt.sx() %out_qubits_14 : !mqtopt.Qubit - %out_qubits_16 = mqtopt.rz(static [1.570796e+00]) %out_qubits_15 : !mqtopt.Qubit - %out_qubits_17, %pos_ctrl_out_qubits = mqtopt.x() %out_qubits_16 ctrl %out_qubits_6 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_18 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_17 : !mqtopt.Qubit - %out_qubits_19, %pos_ctrl_out_qubits_20 = mqtopt.x() %out_qubits_18 ctrl %out_qubits_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_21 = mqtopt.rz(static [7.853982e-01]) %out_qubits_19 : !mqtopt.Qubit - %out_qubits_22, %pos_ctrl_out_qubits_23 = mqtopt.x() %out_qubits_21 ctrl %pos_ctrl_out_qubits : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_24 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_23 : !mqtopt.Qubit - %out_qubits_25 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_22 : !mqtopt.Qubit - %out_qubits_26, %pos_ctrl_out_qubits_27 = mqtopt.x() %out_qubits_25 ctrl %pos_ctrl_out_qubits_20 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_28, %pos_ctrl_out_qubits_29 = mqtopt.x() %out_qubits_24 ctrl %pos_ctrl_out_qubits_27 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_30 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_29 : !mqtopt.Qubit - %out_qubits_31 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_28 : !mqtopt.Qubit - %out_qubits_32, %pos_ctrl_out_qubits_33 = mqtopt.x() %out_qubits_31 ctrl %out_qubits_30 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_34 = mqtopt.rz(static [7.853982e-01]) %out_qubits_26 : !mqtopt.Qubit - %out_qubits_35 = mqtopt.rz(static [1.570796e+00]) %out_qubits_34 : !mqtopt.Qubit - %out_qubits_36 = mqtopt.sx() %out_qubits_35 : !mqtopt.Qubit - %out_qubits_37 = mqtopt.rz(static [1.570796e+00]) %out_qubits_36 : !mqtopt.Qubit - %out_qubits_38 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_37 : !mqtopt.Qubit - %out_qubits_39 = mqtopt.rz(static [1.570796e+00]) %out_qubits_38 : !mqtopt.Qubit - %out_qubits_40 = mqtopt.sx() %out_qubits_39 : !mqtopt.Qubit - %out_qubits_41 = mqtopt.rz(static [1.570796e+00]) %out_qubits_40 : !mqtopt.Qubit - %out_qubits_42, %pos_ctrl_out_qubits_43 = mqtopt.x() %out_qubits_41 ctrl %out_qubits_12 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_44 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_42 : !mqtopt.Qubit - %out_qubits_45, %pos_ctrl_out_qubits_46 = mqtopt.x() %out_qubits_44 ctrl %out_qubits_9 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_47 = mqtopt.rz(static [7.853982e-01]) %out_qubits_45 : !mqtopt.Qubit - %out_qubits_48, %pos_ctrl_out_qubits_49 = mqtopt.x() %out_qubits_47 ctrl %pos_ctrl_out_qubits_43 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_50 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_49 : !mqtopt.Qubit - %out_qubits_51 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_48 : !mqtopt.Qubit - %out_qubits_52, %pos_ctrl_out_qubits_53 = mqtopt.x() %out_qubits_51 ctrl %pos_ctrl_out_qubits_46 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_54, %pos_ctrl_out_qubits_55 = mqtopt.x() %out_qubits_50 ctrl %pos_ctrl_out_qubits_53 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_56 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_55 : !mqtopt.Qubit - %out_qubits_57 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_54 : !mqtopt.Qubit - %out_qubits_58, %pos_ctrl_out_qubits_59 = mqtopt.x() %out_qubits_57 ctrl %out_qubits_56 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_60 = mqtopt.rz(static [7.853982e-01]) %out_qubits_52 : !mqtopt.Qubit - %out_qubits_61 = mqtopt.rz(static [1.570796e+00]) %out_qubits_60 : !mqtopt.Qubit - %out_qubits_62 = mqtopt.sx() %out_qubits_61 : !mqtopt.Qubit - %out_qubits_63 = mqtopt.rz(static [1.570796e+00]) %out_qubits_62 : !mqtopt.Qubit - %out_qubits_64 = mqtopt.rz(static [7.853982e-01]) %out_qubits_63 : !mqtopt.Qubit - %out_qubits_65 = mqtopt.rz(static [1.570796e+00]) %out_qubits_64 : !mqtopt.Qubit - %out_qubits_66 = mqtopt.sx() %out_qubits_65 : !mqtopt.Qubit - %out_qubits_67 = mqtopt.rz(static [1.570796e+00]) %out_qubits_66 : !mqtopt.Qubit - %out_qubits_68, %pos_ctrl_out_qubits_69 = mqtopt.x() %out_qubits_67 ctrl %out_qubits_32 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_70 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_68 : !mqtopt.Qubit - %out_qubits_71, %pos_ctrl_out_qubits_72 = mqtopt.x() %out_qubits_70 ctrl %pos_ctrl_out_qubits_33 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_73 = mqtopt.rz(static [7.853982e-01]) %out_qubits_71 : !mqtopt.Qubit - %out_qubits_74, %pos_ctrl_out_qubits_75 = mqtopt.x() %out_qubits_73 ctrl %pos_ctrl_out_qubits_69 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_76 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_75 : !mqtopt.Qubit - %out_qubits_77 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_74 : !mqtopt.Qubit - %out_qubits_78, %pos_ctrl_out_qubits_79 = mqtopt.x() %out_qubits_77 ctrl %pos_ctrl_out_qubits_72 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_80, %pos_ctrl_out_qubits_81 = mqtopt.x() %out_qubits_76 ctrl %pos_ctrl_out_qubits_79 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_82 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_81 : !mqtopt.Qubit - %out_qubits_83 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_80 : !mqtopt.Qubit - %out_qubits_84, %pos_ctrl_out_qubits_85 = mqtopt.x() %out_qubits_83 ctrl %out_qubits_82 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_86 = mqtopt.rz(static [7.853982e-01]) %out_qubits_78 : !mqtopt.Qubit - %out_qubits_87 = mqtopt.rz(static [1.570796e+00]) %out_qubits_86 : !mqtopt.Qubit - %out_qubits_88 = mqtopt.sx() %out_qubits_87 : !mqtopt.Qubit - %out_qubits_89 = mqtopt.rz(static [1.570796e+00]) %out_qubits_88 : !mqtopt.Qubit - %out_qubits_90 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_89 : !mqtopt.Qubit - %out_qubits_91 = mqtopt.rz(static [1.570796e+00]) %out_qubits_90 : !mqtopt.Qubit - %out_qubits_92 = mqtopt.sx() %out_qubits_91 : !mqtopt.Qubit - %out_qubits_93 = mqtopt.rz(static [1.570796e+00]) %out_qubits_92 : !mqtopt.Qubit - %out_qubits_94, %pos_ctrl_out_qubits_95 = mqtopt.x() %out_qubits_93 ctrl %out_qubits_58 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_96 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_94 : !mqtopt.Qubit - %out_qubits_97, %pos_ctrl_out_qubits_98 = mqtopt.x() %out_qubits_96 ctrl %pos_ctrl_out_qubits_59 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_99 = mqtopt.rz(static [7.853982e-01]) %out_qubits_97 : !mqtopt.Qubit - %out_qubits_100, %pos_ctrl_out_qubits_101 = mqtopt.x() %out_qubits_99 ctrl %pos_ctrl_out_qubits_95 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_102 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_101 : !mqtopt.Qubit - %out_qubits_103 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_100 : !mqtopt.Qubit - %out_qubits_104, %pos_ctrl_out_qubits_105 = mqtopt.x() %out_qubits_103 ctrl %pos_ctrl_out_qubits_98 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_106, %pos_ctrl_out_qubits_107 = mqtopt.x() %out_qubits_102 ctrl %pos_ctrl_out_qubits_105 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_108 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_107 : !mqtopt.Qubit - %out_qubits_109 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_106 : !mqtopt.Qubit - %out_qubits_110, %pos_ctrl_out_qubits_111 = mqtopt.x() %out_qubits_109 ctrl %out_qubits_108 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_112 = mqtopt.rz(static [1.570796e+00]) %out_qubits_110 : !mqtopt.Qubit - %out_qubits_113 = mqtopt.sx() %out_qubits_112 : !mqtopt.Qubit - %out_qubits_114 = mqtopt.rz(static [1.570796e+00]) %out_qubits_113 : !mqtopt.Qubit - %out_qubits_115, %pos_ctrl_out_qubits_116 = mqtopt.x() %out_qubits_114 ctrl %out_qubits_84 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_117 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_115 : !mqtopt.Qubit - %out_qubits_118, %pos_ctrl_out_qubits_119 = mqtopt.x() %out_qubits_117 ctrl %pos_ctrl_out_qubits_85 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_120 = mqtopt.rz(static [7.853982e-01]) %out_qubits_118 : !mqtopt.Qubit - %out_qubits_121, %pos_ctrl_out_qubits_122 = mqtopt.x() %out_qubits_120 ctrl %pos_ctrl_out_qubits_116 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_123 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_122 : !mqtopt.Qubit - %out_qubits_124 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_121 : !mqtopt.Qubit - %out_qubits_125, %pos_ctrl_out_qubits_126 = mqtopt.x() %out_qubits_124 ctrl %pos_ctrl_out_qubits_119 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_127, %pos_ctrl_out_qubits_128 = mqtopt.x() %out_qubits_123 ctrl %pos_ctrl_out_qubits_126 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_129 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_128 : !mqtopt.Qubit - %out_qubits_130 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_127 : !mqtopt.Qubit - %out_qubits_131, %pos_ctrl_out_qubits_132 = mqtopt.x() %out_qubits_130 ctrl %out_qubits_129 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_133 = mqtopt.rz(static [7.853982e-01]) %out_qubits_125 : !mqtopt.Qubit - %out_qubits_134 = mqtopt.rz(static [1.570796e+00]) %out_qubits_133 : !mqtopt.Qubit - %out_qubits_135 = mqtopt.sx() %out_qubits_134 : !mqtopt.Qubit - %out_qubits_136 = mqtopt.rz(static [1.570796e+00]) %out_qubits_135 : !mqtopt.Qubit - %out_qubits_137 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_136 : !mqtopt.Qubit - %out_qubits_138, %pos_ctrl_out_qubits_139 = mqtopt.x() %out_qubits_137 ctrl %pos_ctrl_out_qubits_111 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_140 = mqtopt.rz(static [3.926991e-01]) %out_qubits_138 : !mqtopt.Qubit - %out_qubits_141 = mqtopt.rz(static [1.570796e+00]) %out_qubits_140 : !mqtopt.Qubit - %out_qubits_142 = mqtopt.sx() %out_qubits_141 : !mqtopt.Qubit - %out_qubits_143 = mqtopt.rz(static [1.570796e+00]) %out_qubits_142 : !mqtopt.Qubit - %out_qubits_144, %pos_ctrl_out_qubits_145 = mqtopt.x() %out_qubits_143 ctrl %out_qubits_131 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_146 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_144 : !mqtopt.Qubit - %out_qubits_147, %pos_ctrl_out_qubits_148 = mqtopt.x() %out_qubits_146 ctrl %pos_ctrl_out_qubits_132 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_149 = mqtopt.rz(static [7.853982e-01]) %out_qubits_147 : !mqtopt.Qubit - %out_qubits_150, %pos_ctrl_out_qubits_151 = mqtopt.x() %out_qubits_149 ctrl %pos_ctrl_out_qubits_145 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_152 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_151 : !mqtopt.Qubit - %out_qubits_153 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_150 : !mqtopt.Qubit - %out_qubits_154, %pos_ctrl_out_qubits_155 = mqtopt.x() %out_qubits_153 ctrl %pos_ctrl_out_qubits_148 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_156, %pos_ctrl_out_qubits_157 = mqtopt.x() %out_qubits_152 ctrl %pos_ctrl_out_qubits_155 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_158 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_157 : !mqtopt.Qubit - %out_qubits_159 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_156 : !mqtopt.Qubit - %out_qubits_160, %pos_ctrl_out_qubits_161 = mqtopt.x() %out_qubits_159 ctrl %out_qubits_158 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_162 = mqtopt.rz(static [7.853982e-01]) %out_qubits_154 : !mqtopt.Qubit - %out_qubits_163 = mqtopt.rz(static [1.570796e+00]) %out_qubits_162 : !mqtopt.Qubit - %out_qubits_164 = mqtopt.sx() %out_qubits_163 : !mqtopt.Qubit - %out_qubits_165 = mqtopt.rz(static [1.570796e+00]) %out_qubits_164 : !mqtopt.Qubit - %out_qubits_166 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_165 : !mqtopt.Qubit - %out_qubits_167, %pos_ctrl_out_qubits_168 = mqtopt.x() %out_qubits_166 ctrl %pos_ctrl_out_qubits_139 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_169, %pos_ctrl_out_qubits_170 = mqtopt.x() %pos_ctrl_out_qubits_168 ctrl %pos_ctrl_out_qubits_161 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_171 = mqtopt.rz(static [-1.963495e-01]) %out_qubits_169 : !mqtopt.Qubit - %out_qubits_172, %pos_ctrl_out_qubits_173 = mqtopt.x() %out_qubits_171 ctrl %out_qubits_160 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_174 = mqtopt.rz(static [1.963495e-01]) %out_qubits_172 : !mqtopt.Qubit - %out_qubits_175, %pos_ctrl_out_qubits_176 = mqtopt.x() %out_qubits_174 ctrl %pos_ctrl_out_qubits_170 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_177 = mqtopt.rz(static [-1.963495e-01]) %out_qubits_175 : !mqtopt.Qubit - %out_qubits_178, %pos_ctrl_out_qubits_179 = mqtopt.x() %out_qubits_177 ctrl %pos_ctrl_out_qubits_173 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_180 = mqtopt.rz(static [1.963495e-01]) %pos_ctrl_out_qubits_179 : !mqtopt.Qubit - %out_qubits_181, %pos_ctrl_out_qubits_182 = mqtopt.x() %out_qubits_180 ctrl %pos_ctrl_out_qubits_176 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_183 = mqtopt.rz(static [-1.963495e-01]) %out_qubits_181 : !mqtopt.Qubit - %out_qubits_184, %pos_ctrl_out_qubits_185 = mqtopt.x() %out_qubits_183 ctrl %pos_ctrl_out_qubits_182 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_186 = mqtopt.rz(static [1.963495e-01]) %pos_ctrl_out_qubits_185 : !mqtopt.Qubit - %out_qubits_187 = mqtopt.rz(static [1.570796e+00]) %out_qubits_186 : !mqtopt.Qubit - %out_qubits_188 = mqtopt.sx() %out_qubits_187 : !mqtopt.Qubit - %out_qubits_189 = mqtopt.rz(static [1.570796e+00]) %out_qubits_188 : !mqtopt.Qubit - %out_qubits_190 = mqtopt.x() %out_qubits_189 : !mqtopt.Qubit - %out_qubits_191 = mqtopt.rz(static [3.926991e-01]) %out_qubits_190 : !mqtopt.Qubit - %out_qubits_192 = mqtopt.rz(static [1.570796e+00]) %out_qubits_184 : !mqtopt.Qubit - %out_qubits_193 = mqtopt.sx() %out_qubits_192 : !mqtopt.Qubit - %out_qubits_194 = mqtopt.rz(static [1.570796e+00]) %out_qubits_193 : !mqtopt.Qubit - %out_qubits_195 = mqtopt.x() %out_qubits_194 : !mqtopt.Qubit - %out_qubits_196 = mqtopt.rz(static [3.926991e-01]) %out_qubits_195 : !mqtopt.Qubit - %out_qubits_197, %pos_ctrl_out_qubits_198 = mqtopt.x() %out_qubits_196 ctrl %out_qubits_191 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_199 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_197 : !mqtopt.Qubit - %out_qubits_200, %pos_ctrl_out_qubits_201 = mqtopt.x() %out_qubits_199 ctrl %pos_ctrl_out_qubits_198 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_202 = mqtopt.rz(static [1.963495e-01]) %out_qubits_178 : !mqtopt.Qubit - %out_qubits_203 = mqtopt.rz(static [1.570796e+00]) %out_qubits_202 : !mqtopt.Qubit - %out_qubits_204 = mqtopt.sx() %out_qubits_203 : !mqtopt.Qubit - %out_qubits_205 = mqtopt.rz(static [1.570796e+00]) %out_qubits_204 : !mqtopt.Qubit - %out_qubits_206 = mqtopt.x() %out_qubits_205 : !mqtopt.Qubit - %out_qubits_207 = mqtopt.rz(static [3.926991e-01]) %out_qubits_206 : !mqtopt.Qubit - %out_qubits_208, %pos_ctrl_out_qubits_209 = mqtopt.x() %out_qubits_207 ctrl %out_qubits_200 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_210 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_208 : !mqtopt.Qubit - %out_qubits_211, %pos_ctrl_out_qubits_212 = mqtopt.x() %out_qubits_210 ctrl %pos_ctrl_out_qubits_201 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_213 = mqtopt.rz(static [3.926991e-01]) %out_qubits_211 : !mqtopt.Qubit - %out_qubits_214, %pos_ctrl_out_qubits_215 = mqtopt.x() %out_qubits_213 ctrl %pos_ctrl_out_qubits_209 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_216 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_214 : !mqtopt.Qubit - %out_qubits_217, %pos_ctrl_out_qubits_218 = mqtopt.x() %out_qubits_216 ctrl %pos_ctrl_out_qubits_212 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_219 = mqtopt.rz(static [3.926991e-01]) %out_qubits_167 : !mqtopt.Qubit - %out_qubits_220 = mqtopt.rz(static [1.570796e+00]) %out_qubits_219 : !mqtopt.Qubit - %out_qubits_221 = mqtopt.sx() %out_qubits_220 : !mqtopt.Qubit - %out_qubits_222 = mqtopt.rz(static [1.570796e+00]) %out_qubits_221 : !mqtopt.Qubit - %out_qubits_223 = mqtopt.x() %out_qubits_222 : !mqtopt.Qubit - %out_qubits_224 = mqtopt.rz(static [1.570796e+00]) %out_qubits_223 : !mqtopt.Qubit - %out_qubits_225 = mqtopt.sx() %out_qubits_224 : !mqtopt.Qubit - %out_qubits_226 = mqtopt.rz(static [1.570796e+00]) %out_qubits_225 : !mqtopt.Qubit - %out_qubits_227 = mqtopt.rz(static [1.570796e+00]) %out_qubits_226 : !mqtopt.Qubit - %out_qubits_228 = mqtopt.sx() %out_qubits_227 : !mqtopt.Qubit - %out_qubits_229 = mqtopt.rz(static [1.570796e+00]) %out_qubits_228 : !mqtopt.Qubit - %out_qubits_230 = mqtopt.rz(static [3.926991e-01]) %out_qubits_229 : !mqtopt.Qubit - %out_qubits_231, %pos_ctrl_out_qubits_232 = mqtopt.x() %out_qubits_230 ctrl %out_qubits_217 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_233 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_231 : !mqtopt.Qubit - %out_qubits_234, %pos_ctrl_out_qubits_235 = mqtopt.x() %out_qubits_233 ctrl %pos_ctrl_out_qubits_215 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_236 = mqtopt.rz(static [3.926991e-01]) %out_qubits_234 : !mqtopt.Qubit - %out_qubits_237, %pos_ctrl_out_qubits_238 = mqtopt.x() %out_qubits_236 ctrl %pos_ctrl_out_qubits_232 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_239 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_237 : !mqtopt.Qubit - %out_qubits_240, %pos_ctrl_out_qubits_241 = mqtopt.x() %out_qubits_239 ctrl %pos_ctrl_out_qubits_218 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_242 = mqtopt.rz(static [3.926991e-01]) %out_qubits_240 : !mqtopt.Qubit - %out_qubits_243, %pos_ctrl_out_qubits_244 = mqtopt.x() %out_qubits_242 ctrl %pos_ctrl_out_qubits_238 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_245 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_243 : !mqtopt.Qubit - %out_qubits_246, %pos_ctrl_out_qubits_247 = mqtopt.x() %out_qubits_245 ctrl %pos_ctrl_out_qubits_235 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_248 = mqtopt.x() %pos_ctrl_out_qubits_247 : !mqtopt.Qubit - %out_qubits_249 = mqtopt.rz(static [1.570796e+00]) %out_qubits_248 : !mqtopt.Qubit - %out_qubits_250 = mqtopt.sx() %out_qubits_249 : !mqtopt.Qubit - %out_qubits_251 = mqtopt.rz(static [1.570796e+00]) %out_qubits_250 : !mqtopt.Qubit - %out_qubits_252 = mqtopt.rz(static [3.926991e-01]) %out_qubits_246 : !mqtopt.Qubit - %out_qubits_253, %pos_ctrl_out_qubits_254 = mqtopt.x() %out_qubits_252 ctrl %pos_ctrl_out_qubits_244 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_255 = mqtopt.x() %pos_ctrl_out_qubits_254 : !mqtopt.Qubit - %out_qubits_256 = mqtopt.rz(static [1.570796e+00]) %out_qubits_255 : !mqtopt.Qubit - %out_qubits_257 = mqtopt.sx() %out_qubits_256 : !mqtopt.Qubit - %out_qubits_258 = mqtopt.rz(static [1.570796e+00]) %out_qubits_257 : !mqtopt.Qubit - %out_qubits_259 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_253 : !mqtopt.Qubit - %out_qubits_260, %pos_ctrl_out_qubits_261 = mqtopt.x() %out_qubits_259 ctrl %pos_ctrl_out_qubits_241 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_262 = mqtopt.x() %pos_ctrl_out_qubits_261 : !mqtopt.Qubit - %out_qubits_263 = mqtopt.rz(static [1.570796e+00]) %out_qubits_262 : !mqtopt.Qubit - %out_qubits_264 = mqtopt.sx() %out_qubits_263 : !mqtopt.Qubit - %out_qubits_265 = mqtopt.rz(static [1.570796e+00]) %out_qubits_264 : !mqtopt.Qubit - %out_qubits_266 = mqtopt.rz(static [1.570796e+00]) %out_qubits_260 : !mqtopt.Qubit - %out_qubits_267 = mqtopt.sx() %out_qubits_266 : !mqtopt.Qubit - %out_qubits_268 = mqtopt.rz(static [1.570796e+00]) %out_qubits_267 : !mqtopt.Qubit - %out_qubits_269 = mqtopt.rz(static [1.570796e+00]) %out_qubits_268 : !mqtopt.Qubit - %out_qubits_270 = mqtopt.sx() %out_qubits_269 : !mqtopt.Qubit - %out_qubits_271 = mqtopt.rz(static [1.570796e+00]) %out_qubits_270 : !mqtopt.Qubit - %out_qubits_272 = mqtopt.x() %out_qubits_271 : !mqtopt.Qubit - %out_qubits_273 = mqtopt.rz(static [1.570796e+00]) %out_qubits_272 : !mqtopt.Qubit - %out_qubits_274 = mqtopt.sx() %out_qubits_273 : !mqtopt.Qubit - %out_qubits_275 = mqtopt.rz(static [1.570796e+00]) %out_qubits_274 : !mqtopt.Qubit - %out_qubits_276 = mqtopt.rz(static [7.853982e-01]) %out_qubits_104 : !mqtopt.Qubit - %out_qubits_277 = mqtopt.rz(static [1.570796e+00]) %out_qubits_276 : !mqtopt.Qubit - %out_qubits_278 = mqtopt.sx() %out_qubits_277 : !mqtopt.Qubit - %out_qubits_279 = mqtopt.rz(static [1.570796e+00]) %out_qubits_278 : !mqtopt.Qubit - %out_qubits_280 = mqtopt.rz(static [7.853982e-01]) %out_qubits_279 : !mqtopt.Qubit - %out_qubits_281 = mqtopt.rz(static [1.570796e+00]) %out_qubits_280 : !mqtopt.Qubit - %out_qubits_282 = mqtopt.sx() %out_qubits_281 : !mqtopt.Qubit - %out_qubits_283 = mqtopt.rz(static [1.570796e+00]) %out_qubits_282 : !mqtopt.Qubit - %out_qubits_284, %pos_ctrl_out_qubits_285 = mqtopt.x() %out_qubits_283 ctrl %out_qubits_251 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_286 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_284 : !mqtopt.Qubit - %out_qubits_287, %pos_ctrl_out_qubits_288 = mqtopt.x() %out_qubits_286 ctrl %out_qubits_265 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_289 = mqtopt.rz(static [7.853982e-01]) %out_qubits_287 : !mqtopt.Qubit - %out_qubits_290, %pos_ctrl_out_qubits_291 = mqtopt.x() %out_qubits_289 ctrl %pos_ctrl_out_qubits_285 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_292 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_291 : !mqtopt.Qubit - %out_qubits_293 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_290 : !mqtopt.Qubit - %out_qubits_294, %pos_ctrl_out_qubits_295 = mqtopt.x() %out_qubits_293 ctrl %pos_ctrl_out_qubits_288 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_296, %pos_ctrl_out_qubits_297 = mqtopt.x() %out_qubits_292 ctrl %pos_ctrl_out_qubits_295 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_298 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_297 : !mqtopt.Qubit - %out_qubits_299 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_296 : !mqtopt.Qubit - %out_qubits_300, %pos_ctrl_out_qubits_301 = mqtopt.x() %out_qubits_299 ctrl %out_qubits_298 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_302 = mqtopt.rz(static [7.853982e-01]) %out_qubits_294 : !mqtopt.Qubit - %out_qubits_303 = mqtopt.rz(static [1.570796e+00]) %out_qubits_302 : !mqtopt.Qubit - %out_qubits_304 = mqtopt.sx() %out_qubits_303 : !mqtopt.Qubit - %out_qubits_305 = mqtopt.rz(static [1.570796e+00]) %out_qubits_304 : !mqtopt.Qubit - %out_qubits_306 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_305 : !mqtopt.Qubit - %out_qubits_307 = mqtopt.rz(static [1.570796e+00]) %out_qubits_306 : !mqtopt.Qubit - %out_qubits_308 = mqtopt.sx() %out_qubits_307 : !mqtopt.Qubit - %out_qubits_309 = mqtopt.rz(static [1.570796e+00]) %out_qubits_308 : !mqtopt.Qubit - %out_qubits_310, %pos_ctrl_out_qubits_311 = mqtopt.x() %out_qubits_309 ctrl %out_qubits_275 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_312 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_310 : !mqtopt.Qubit - %out_qubits_313, %pos_ctrl_out_qubits_314 = mqtopt.x() %out_qubits_312 ctrl %out_qubits_258 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_315 = mqtopt.rz(static [7.853982e-01]) %out_qubits_313 : !mqtopt.Qubit - %out_qubits_316, %pos_ctrl_out_qubits_317 = mqtopt.x() %out_qubits_315 ctrl %pos_ctrl_out_qubits_311 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_318 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_317 : !mqtopt.Qubit - %out_qubits_319 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_316 : !mqtopt.Qubit - %out_qubits_320, %pos_ctrl_out_qubits_321 = mqtopt.x() %out_qubits_319 ctrl %pos_ctrl_out_qubits_314 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_322, %pos_ctrl_out_qubits_323 = mqtopt.x() %out_qubits_318 ctrl %pos_ctrl_out_qubits_321 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_324 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_323 : !mqtopt.Qubit - %out_qubits_325 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_322 : !mqtopt.Qubit - %out_qubits_326, %pos_ctrl_out_qubits_327 = mqtopt.x() %out_qubits_325 ctrl %out_qubits_324 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_328 = mqtopt.rz(static [7.853982e-01]) %out_qubits_320 : !mqtopt.Qubit - %out_qubits_329 = mqtopt.rz(static [1.570796e+00]) %out_qubits_328 : !mqtopt.Qubit - %out_qubits_330 = mqtopt.sx() %out_qubits_329 : !mqtopt.Qubit - %out_qubits_331 = mqtopt.rz(static [1.570796e+00]) %out_qubits_330 : !mqtopt.Qubit - %out_qubits_332 = mqtopt.rz(static [7.853982e-01]) %out_qubits_331 : !mqtopt.Qubit - %out_qubits_333 = mqtopt.rz(static [1.570796e+00]) %out_qubits_332 : !mqtopt.Qubit - %out_qubits_334 = mqtopt.sx() %out_qubits_333 : !mqtopt.Qubit - %out_qubits_335 = mqtopt.rz(static [1.570796e+00]) %out_qubits_334 : !mqtopt.Qubit - %out_qubits_336, %pos_ctrl_out_qubits_337 = mqtopt.x() %out_qubits_335 ctrl %out_qubits_300 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_338 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_336 : !mqtopt.Qubit - %out_qubits_339, %pos_ctrl_out_qubits_340 = mqtopt.x() %out_qubits_338 ctrl %pos_ctrl_out_qubits_301 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_341 = mqtopt.rz(static [7.853982e-01]) %out_qubits_339 : !mqtopt.Qubit - %out_qubits_342, %pos_ctrl_out_qubits_343 = mqtopt.x() %out_qubits_341 ctrl %pos_ctrl_out_qubits_337 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_344 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_343 : !mqtopt.Qubit - %out_qubits_345 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_342 : !mqtopt.Qubit - %out_qubits_346, %pos_ctrl_out_qubits_347 = mqtopt.x() %out_qubits_345 ctrl %pos_ctrl_out_qubits_340 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_348, %pos_ctrl_out_qubits_349 = mqtopt.x() %out_qubits_344 ctrl %pos_ctrl_out_qubits_347 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_350 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_349 : !mqtopt.Qubit - %out_qubits_351 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_348 : !mqtopt.Qubit - %out_qubits_352, %pos_ctrl_out_qubits_353 = mqtopt.x() %out_qubits_351 ctrl %out_qubits_350 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_354 = mqtopt.rz(static [7.853982e-01]) %out_qubits_346 : !mqtopt.Qubit - %out_qubits_355 = mqtopt.rz(static [1.570796e+00]) %out_qubits_354 : !mqtopt.Qubit - %out_qubits_356 = mqtopt.sx() %out_qubits_355 : !mqtopt.Qubit - %out_qubits_357 = mqtopt.rz(static [1.570796e+00]) %out_qubits_356 : !mqtopt.Qubit - %out_qubits_358 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_357 : !mqtopt.Qubit - %out_qubits_359 = mqtopt.rz(static [1.570796e+00]) %out_qubits_358 : !mqtopt.Qubit - %out_qubits_360 = mqtopt.sx() %out_qubits_359 : !mqtopt.Qubit - %out_qubits_361 = mqtopt.rz(static [1.570796e+00]) %out_qubits_360 : !mqtopt.Qubit - %out_qubits_362, %pos_ctrl_out_qubits_363 = mqtopt.x() %out_qubits_361 ctrl %out_qubits_326 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_364 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_362 : !mqtopt.Qubit - %out_qubits_365, %pos_ctrl_out_qubits_366 = mqtopt.x() %out_qubits_364 ctrl %pos_ctrl_out_qubits_327 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_367 = mqtopt.rz(static [7.853982e-01]) %out_qubits_365 : !mqtopt.Qubit - %out_qubits_368, %pos_ctrl_out_qubits_369 = mqtopt.x() %out_qubits_367 ctrl %pos_ctrl_out_qubits_363 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_370 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_369 : !mqtopt.Qubit - %out_qubits_371 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_368 : !mqtopt.Qubit - %out_qubits_372, %pos_ctrl_out_qubits_373 = mqtopt.x() %out_qubits_371 ctrl %pos_ctrl_out_qubits_366 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_374, %pos_ctrl_out_qubits_375 = mqtopt.x() %out_qubits_370 ctrl %pos_ctrl_out_qubits_373 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_376 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_375 : !mqtopt.Qubit - %out_qubits_377 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_374 : !mqtopt.Qubit - %out_qubits_378, %pos_ctrl_out_qubits_379 = mqtopt.x() %out_qubits_377 ctrl %out_qubits_376 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_380 = mqtopt.rz(static [1.570796e+00]) %out_qubits_378 : !mqtopt.Qubit - %out_qubits_381 = mqtopt.sx() %out_qubits_380 : !mqtopt.Qubit - %out_qubits_382 = mqtopt.rz(static [1.570796e+00]) %out_qubits_381 : !mqtopt.Qubit - %out_qubits_383, %pos_ctrl_out_qubits_384 = mqtopt.x() %out_qubits_382 ctrl %out_qubits_352 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_385 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_383 : !mqtopt.Qubit - %out_qubits_386, %pos_ctrl_out_qubits_387 = mqtopt.x() %out_qubits_385 ctrl %pos_ctrl_out_qubits_353 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_388 = mqtopt.rz(static [7.853982e-01]) %out_qubits_386 : !mqtopt.Qubit - %out_qubits_389, %pos_ctrl_out_qubits_390 = mqtopt.x() %out_qubits_388 ctrl %pos_ctrl_out_qubits_384 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_391 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_390 : !mqtopt.Qubit - %out_qubits_392 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_389 : !mqtopt.Qubit - %out_qubits_393, %pos_ctrl_out_qubits_394 = mqtopt.x() %out_qubits_392 ctrl %pos_ctrl_out_qubits_387 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_395, %pos_ctrl_out_qubits_396 = mqtopt.x() %out_qubits_391 ctrl %pos_ctrl_out_qubits_394 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_397 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_396 : !mqtopt.Qubit - %out_qubits_398 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_395 : !mqtopt.Qubit - %out_qubits_399, %pos_ctrl_out_qubits_400 = mqtopt.x() %out_qubits_398 ctrl %out_qubits_397 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_401 = mqtopt.rz(static [7.853982e-01]) %out_qubits_393 : !mqtopt.Qubit - %out_qubits_402 = mqtopt.rz(static [1.570796e+00]) %out_qubits_401 : !mqtopt.Qubit - %out_qubits_403 = mqtopt.sx() %out_qubits_402 : !mqtopt.Qubit - %out_qubits_404 = mqtopt.rz(static [1.570796e+00]) %out_qubits_403 : !mqtopt.Qubit - %out_qubits_405 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_404 : !mqtopt.Qubit - %out_qubits_406, %pos_ctrl_out_qubits_407 = mqtopt.x() %out_qubits_405 ctrl %pos_ctrl_out_qubits_379 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_408 = mqtopt.rz(static [3.926991e-01]) %out_qubits_406 : !mqtopt.Qubit - %out_qubits_409 = mqtopt.rz(static [1.570796e+00]) %out_qubits_408 : !mqtopt.Qubit - %out_qubits_410 = mqtopt.sx() %out_qubits_409 : !mqtopt.Qubit - %out_qubits_411 = mqtopt.rz(static [1.570796e+00]) %out_qubits_410 : !mqtopt.Qubit - %out_qubits_412, %pos_ctrl_out_qubits_413 = mqtopt.x() %out_qubits_411 ctrl %out_qubits_399 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_414 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_412 : !mqtopt.Qubit - %out_qubits_415, %pos_ctrl_out_qubits_416 = mqtopt.x() %out_qubits_414 ctrl %pos_ctrl_out_qubits_400 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_417 = mqtopt.rz(static [7.853982e-01]) %out_qubits_415 : !mqtopt.Qubit - %out_qubits_418, %pos_ctrl_out_qubits_419 = mqtopt.x() %out_qubits_417 ctrl %pos_ctrl_out_qubits_413 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_420 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_419 : !mqtopt.Qubit - %out_qubits_421 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_418 : !mqtopt.Qubit - %out_qubits_422, %pos_ctrl_out_qubits_423 = mqtopt.x() %out_qubits_421 ctrl %pos_ctrl_out_qubits_416 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_424, %pos_ctrl_out_qubits_425 = mqtopt.x() %out_qubits_420 ctrl %pos_ctrl_out_qubits_423 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_426 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_425 : !mqtopt.Qubit - %out_qubits_427 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_424 : !mqtopt.Qubit - %out_qubits_428, %pos_ctrl_out_qubits_429 = mqtopt.x() %out_qubits_427 ctrl %out_qubits_426 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_430 = mqtopt.rz(static [7.853982e-01]) %out_qubits_422 : !mqtopt.Qubit - %out_qubits_431 = mqtopt.rz(static [1.570796e+00]) %out_qubits_430 : !mqtopt.Qubit - %out_qubits_432 = mqtopt.sx() %out_qubits_431 : !mqtopt.Qubit - %out_qubits_433 = mqtopt.rz(static [1.570796e+00]) %out_qubits_432 : !mqtopt.Qubit - %out_qubits_434 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_433 : !mqtopt.Qubit - %out_qubits_435, %pos_ctrl_out_qubits_436 = mqtopt.x() %out_qubits_434 ctrl %pos_ctrl_out_qubits_407 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_437, %pos_ctrl_out_qubits_438 = mqtopt.x() %pos_ctrl_out_qubits_436 ctrl %pos_ctrl_out_qubits_429 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_439 = mqtopt.rz(static [-1.963495e-01]) %out_qubits_437 : !mqtopt.Qubit - %out_qubits_440, %pos_ctrl_out_qubits_441 = mqtopt.x() %out_qubits_439 ctrl %out_qubits_428 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_442 = mqtopt.rz(static [1.963495e-01]) %out_qubits_440 : !mqtopt.Qubit - %out_qubits_443, %pos_ctrl_out_qubits_444 = mqtopt.x() %out_qubits_442 ctrl %pos_ctrl_out_qubits_438 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_445 = mqtopt.rz(static [-1.963495e-01]) %out_qubits_443 : !mqtopt.Qubit - %out_qubits_446, %pos_ctrl_out_qubits_447 = mqtopt.x() %out_qubits_445 ctrl %pos_ctrl_out_qubits_441 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_448 = mqtopt.rz(static [1.963495e-01]) %pos_ctrl_out_qubits_447 : !mqtopt.Qubit - %out_qubits_449, %pos_ctrl_out_qubits_450 = mqtopt.x() %out_qubits_448 ctrl %pos_ctrl_out_qubits_444 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_451 = mqtopt.rz(static [-1.963495e-01]) %out_qubits_449 : !mqtopt.Qubit - %out_qubits_452, %pos_ctrl_out_qubits_453 = mqtopt.x() %out_qubits_451 ctrl %pos_ctrl_out_qubits_450 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_454 = mqtopt.rz(static [1.963495e-01]) %pos_ctrl_out_qubits_453 : !mqtopt.Qubit - %out_qubits_455 = mqtopt.rz(static [1.570796e+00]) %out_qubits_454 : !mqtopt.Qubit - %out_qubits_456 = mqtopt.sx() %out_qubits_455 : !mqtopt.Qubit - %out_qubits_457 = mqtopt.rz(static [1.570796e+00]) %out_qubits_456 : !mqtopt.Qubit - %out_qubits_458 = mqtopt.x() %out_qubits_457 : !mqtopt.Qubit - %out_qubits_459 = mqtopt.rz(static [3.926991e-01]) %out_qubits_458 : !mqtopt.Qubit - %out_qubits_460 = mqtopt.rz(static [1.570796e+00]) %out_qubits_452 : !mqtopt.Qubit - %out_qubits_461 = mqtopt.sx() %out_qubits_460 : !mqtopt.Qubit - %out_qubits_462 = mqtopt.rz(static [1.570796e+00]) %out_qubits_461 : !mqtopt.Qubit - %out_qubits_463 = mqtopt.x() %out_qubits_462 : !mqtopt.Qubit - %out_qubits_464 = mqtopt.rz(static [3.926991e-01]) %out_qubits_463 : !mqtopt.Qubit - %out_qubits_465, %pos_ctrl_out_qubits_466 = mqtopt.x() %out_qubits_464 ctrl %out_qubits_459 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_467 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_465 : !mqtopt.Qubit - %out_qubits_468, %pos_ctrl_out_qubits_469 = mqtopt.x() %out_qubits_467 ctrl %pos_ctrl_out_qubits_466 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_470 = mqtopt.rz(static [1.963495e-01]) %out_qubits_446 : !mqtopt.Qubit - %out_qubits_471 = mqtopt.rz(static [1.570796e+00]) %out_qubits_470 : !mqtopt.Qubit - %out_qubits_472 = mqtopt.sx() %out_qubits_471 : !mqtopt.Qubit - %out_qubits_473 = mqtopt.rz(static [1.570796e+00]) %out_qubits_472 : !mqtopt.Qubit - %out_qubits_474 = mqtopt.x() %out_qubits_473 : !mqtopt.Qubit - %out_qubits_475 = mqtopt.rz(static [3.926991e-01]) %out_qubits_474 : !mqtopt.Qubit - %out_qubits_476, %pos_ctrl_out_qubits_477 = mqtopt.x() %out_qubits_475 ctrl %out_qubits_468 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_478 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_476 : !mqtopt.Qubit - %out_qubits_479, %pos_ctrl_out_qubits_480 = mqtopt.x() %out_qubits_478 ctrl %pos_ctrl_out_qubits_469 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_481 = mqtopt.rz(static [3.926991e-01]) %out_qubits_479 : !mqtopt.Qubit - %out_qubits_482, %pos_ctrl_out_qubits_483 = mqtopt.x() %out_qubits_481 ctrl %pos_ctrl_out_qubits_477 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_484 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_482 : !mqtopt.Qubit - %out_qubits_485, %pos_ctrl_out_qubits_486 = mqtopt.x() %out_qubits_484 ctrl %pos_ctrl_out_qubits_480 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_487 = mqtopt.rz(static [3.926991e-01]) %out_qubits_435 : !mqtopt.Qubit - %out_qubits_488 = mqtopt.rz(static [1.570796e+00]) %out_qubits_487 : !mqtopt.Qubit - %out_qubits_489 = mqtopt.sx() %out_qubits_488 : !mqtopt.Qubit - %out_qubits_490 = mqtopt.rz(static [1.570796e+00]) %out_qubits_489 : !mqtopt.Qubit - %out_qubits_491 = mqtopt.x() %out_qubits_490 : !mqtopt.Qubit - %out_qubits_492 = mqtopt.rz(static [1.570796e+00]) %out_qubits_491 : !mqtopt.Qubit - %out_qubits_493 = mqtopt.sx() %out_qubits_492 : !mqtopt.Qubit - %out_qubits_494 = mqtopt.rz(static [1.570796e+00]) %out_qubits_493 : !mqtopt.Qubit - %out_qubits_495 = mqtopt.rz(static [1.570796e+00]) %out_qubits_494 : !mqtopt.Qubit - %out_qubits_496 = mqtopt.sx() %out_qubits_495 : !mqtopt.Qubit - %out_qubits_497 = mqtopt.rz(static [1.570796e+00]) %out_qubits_496 : !mqtopt.Qubit - %out_qubits_498 = mqtopt.rz(static [3.926991e-01]) %out_qubits_497 : !mqtopt.Qubit - %out_qubits_499, %pos_ctrl_out_qubits_500 = mqtopt.x() %out_qubits_498 ctrl %out_qubits_485 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_501 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_499 : !mqtopt.Qubit - %out_qubits_502, %pos_ctrl_out_qubits_503 = mqtopt.x() %out_qubits_501 ctrl %pos_ctrl_out_qubits_483 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_504 = mqtopt.rz(static [3.926991e-01]) %out_qubits_502 : !mqtopt.Qubit - %out_qubits_505, %pos_ctrl_out_qubits_506 = mqtopt.x() %out_qubits_504 ctrl %pos_ctrl_out_qubits_500 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_507 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_505 : !mqtopt.Qubit - %out_qubits_508, %pos_ctrl_out_qubits_509 = mqtopt.x() %out_qubits_507 ctrl %pos_ctrl_out_qubits_486 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_510 = mqtopt.rz(static [3.926991e-01]) %out_qubits_508 : !mqtopt.Qubit - %out_qubits_511, %pos_ctrl_out_qubits_512 = mqtopt.x() %out_qubits_510 ctrl %pos_ctrl_out_qubits_506 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_513 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_511 : !mqtopt.Qubit - %out_qubits_514, %pos_ctrl_out_qubits_515 = mqtopt.x() %out_qubits_513 ctrl %pos_ctrl_out_qubits_503 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_516 = mqtopt.x() %pos_ctrl_out_qubits_515 : !mqtopt.Qubit - %out_qubits_517 = mqtopt.rz(static [1.570796e+00]) %out_qubits_516 : !mqtopt.Qubit - %out_qubits_518 = mqtopt.sx() %out_qubits_517 : !mqtopt.Qubit - %out_qubits_519 = mqtopt.rz(static [1.570796e+00]) %out_qubits_518 : !mqtopt.Qubit - %out_qubits_520 = mqtopt.rz(static [3.926991e-01]) %out_qubits_514 : !mqtopt.Qubit - %out_qubits_521, %pos_ctrl_out_qubits_522 = mqtopt.x() %out_qubits_520 ctrl %pos_ctrl_out_qubits_512 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_523 = mqtopt.x() %pos_ctrl_out_qubits_522 : !mqtopt.Qubit - %out_qubits_524 = mqtopt.rz(static [1.570796e+00]) %out_qubits_523 : !mqtopt.Qubit - %out_qubits_525 = mqtopt.sx() %out_qubits_524 : !mqtopt.Qubit - %out_qubits_526 = mqtopt.rz(static [1.570796e+00]) %out_qubits_525 : !mqtopt.Qubit - %out_qubits_527 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_521 : !mqtopt.Qubit - %out_qubits_528, %pos_ctrl_out_qubits_529 = mqtopt.x() %out_qubits_527 ctrl %pos_ctrl_out_qubits_509 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_530 = mqtopt.x() %pos_ctrl_out_qubits_529 : !mqtopt.Qubit - %out_qubits_531 = mqtopt.rz(static [1.570796e+00]) %out_qubits_530 : !mqtopt.Qubit - %out_qubits_532 = mqtopt.sx() %out_qubits_531 : !mqtopt.Qubit - %out_qubits_533 = mqtopt.rz(static [1.570796e+00]) %out_qubits_532 : !mqtopt.Qubit - %out_qubits_534 = mqtopt.rz(static [1.570796e+00]) %out_qubits_528 : !mqtopt.Qubit - %out_qubits_535 = mqtopt.sx() %out_qubits_534 : !mqtopt.Qubit - %out_qubits_536 = mqtopt.rz(static [1.570796e+00]) %out_qubits_535 : !mqtopt.Qubit - %out_qubits_537 = mqtopt.rz(static [1.570796e+00]) %out_qubits_536 : !mqtopt.Qubit - %out_qubits_538 = mqtopt.sx() %out_qubits_537 : !mqtopt.Qubit - %out_qubits_539 = mqtopt.rz(static [1.570796e+00]) %out_qubits_538 : !mqtopt.Qubit - %out_qubits_540 = mqtopt.x() %out_qubits_539 : !mqtopt.Qubit - %out_qubits_541 = mqtopt.rz(static [1.570796e+00]) %out_qubits_540 : !mqtopt.Qubit - %out_qubits_542 = mqtopt.sx() %out_qubits_541 : !mqtopt.Qubit - %out_qubits_543 = mqtopt.rz(static [1.570796e+00]) %out_qubits_542 : !mqtopt.Qubit - %out_qubits_544 = mqtopt.rz(static [7.853982e-01]) %out_qubits_372 : !mqtopt.Qubit - %out_qubits_545 = mqtopt.rz(static [1.570796e+00]) %out_qubits_544 : !mqtopt.Qubit - %out_qubits_546 = mqtopt.sx() %out_qubits_545 : !mqtopt.Qubit - %out_qubits_547 = mqtopt.rz(static [1.570796e+00]) %out_qubits_546 : !mqtopt.Qubit - %out_qubits_548 = mqtopt.rz(static [7.853982e-01]) %out_qubits_547 : !mqtopt.Qubit - %out_qubits_549 = mqtopt.rz(static [1.570796e+00]) %out_qubits_548 : !mqtopt.Qubit - %out_qubits_550 = mqtopt.sx() %out_qubits_549 : !mqtopt.Qubit - %out_qubits_551 = mqtopt.rz(static [1.570796e+00]) %out_qubits_550 : !mqtopt.Qubit - %out_qubits_552, %pos_ctrl_out_qubits_553 = mqtopt.x() %out_qubits_551 ctrl %out_qubits_519 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_554 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_552 : !mqtopt.Qubit - %out_qubits_555, %pos_ctrl_out_qubits_556 = mqtopt.x() %out_qubits_554 ctrl %out_qubits_533 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_557 = mqtopt.rz(static [7.853982e-01]) %out_qubits_555 : !mqtopt.Qubit - %out_qubits_558, %pos_ctrl_out_qubits_559 = mqtopt.x() %out_qubits_557 ctrl %pos_ctrl_out_qubits_553 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_560 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_559 : !mqtopt.Qubit - %out_qubits_561 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_558 : !mqtopt.Qubit - %out_qubits_562, %pos_ctrl_out_qubits_563 = mqtopt.x() %out_qubits_561 ctrl %pos_ctrl_out_qubits_556 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_564, %pos_ctrl_out_qubits_565 = mqtopt.x() %out_qubits_560 ctrl %pos_ctrl_out_qubits_563 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_566 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_565 : !mqtopt.Qubit - %out_qubits_567 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_564 : !mqtopt.Qubit - %out_qubits_568, %pos_ctrl_out_qubits_569 = mqtopt.x() %out_qubits_567 ctrl %out_qubits_566 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_570 = mqtopt.rz(static [7.853982e-01]) %out_qubits_562 : !mqtopt.Qubit - %out_qubits_571 = mqtopt.rz(static [1.570796e+00]) %out_qubits_570 : !mqtopt.Qubit - %out_qubits_572 = mqtopt.sx() %out_qubits_571 : !mqtopt.Qubit - %out_qubits_573 = mqtopt.rz(static [1.570796e+00]) %out_qubits_572 : !mqtopt.Qubit - %out_qubits_574 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_573 : !mqtopt.Qubit - %out_qubits_575 = mqtopt.rz(static [1.570796e+00]) %out_qubits_574 : !mqtopt.Qubit - %out_qubits_576 = mqtopt.sx() %out_qubits_575 : !mqtopt.Qubit - %out_qubits_577 = mqtopt.rz(static [1.570796e+00]) %out_qubits_576 : !mqtopt.Qubit - %out_qubits_578, %pos_ctrl_out_qubits_579 = mqtopt.x() %out_qubits_577 ctrl %out_qubits_543 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_580 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_578 : !mqtopt.Qubit - %out_qubits_581, %pos_ctrl_out_qubits_582 = mqtopt.x() %out_qubits_580 ctrl %out_qubits_526 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_583 = mqtopt.rz(static [7.853982e-01]) %out_qubits_581 : !mqtopt.Qubit - %out_qubits_584, %pos_ctrl_out_qubits_585 = mqtopt.x() %out_qubits_583 ctrl %pos_ctrl_out_qubits_579 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_586 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_585 : !mqtopt.Qubit - %out_qubits_587 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_584 : !mqtopt.Qubit - %out_qubits_588, %pos_ctrl_out_qubits_589 = mqtopt.x() %out_qubits_587 ctrl %pos_ctrl_out_qubits_582 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_590, %pos_ctrl_out_qubits_591 = mqtopt.x() %out_qubits_586 ctrl %pos_ctrl_out_qubits_589 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_592 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_591 : !mqtopt.Qubit - %out_qubits_593 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_590 : !mqtopt.Qubit - %out_qubits_594, %pos_ctrl_out_qubits_595 = mqtopt.x() %out_qubits_593 ctrl %out_qubits_592 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_596 = mqtopt.rz(static [7.853982e-01]) %out_qubits_588 : !mqtopt.Qubit - %out_qubits_597 = mqtopt.rz(static [1.570796e+00]) %out_qubits_596 : !mqtopt.Qubit - %out_qubits_598 = mqtopt.sx() %out_qubits_597 : !mqtopt.Qubit - %out_qubits_599 = mqtopt.rz(static [1.570796e+00]) %out_qubits_598 : !mqtopt.Qubit - %out_qubits_600 = mqtopt.rz(static [7.853982e-01]) %out_qubits_599 : !mqtopt.Qubit - %out_qubits_601 = mqtopt.rz(static [1.570796e+00]) %out_qubits_600 : !mqtopt.Qubit - %out_qubits_602 = mqtopt.sx() %out_qubits_601 : !mqtopt.Qubit - %out_qubits_603 = mqtopt.rz(static [1.570796e+00]) %out_qubits_602 : !mqtopt.Qubit - %out_qubits_604, %pos_ctrl_out_qubits_605 = mqtopt.x() %out_qubits_603 ctrl %out_qubits_568 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_606 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_604 : !mqtopt.Qubit - %out_qubits_607, %pos_ctrl_out_qubits_608 = mqtopt.x() %out_qubits_606 ctrl %pos_ctrl_out_qubits_569 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_609 = mqtopt.rz(static [7.853982e-01]) %out_qubits_607 : !mqtopt.Qubit - %out_qubits_610, %pos_ctrl_out_qubits_611 = mqtopt.x() %out_qubits_609 ctrl %pos_ctrl_out_qubits_605 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_612 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_611 : !mqtopt.Qubit - %out_qubits_613 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_610 : !mqtopt.Qubit - %out_qubits_614, %pos_ctrl_out_qubits_615 = mqtopt.x() %out_qubits_613 ctrl %pos_ctrl_out_qubits_608 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_616, %pos_ctrl_out_qubits_617 = mqtopt.x() %out_qubits_612 ctrl %pos_ctrl_out_qubits_615 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_618 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_617 : !mqtopt.Qubit - %out_qubits_619 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_616 : !mqtopt.Qubit - %out_qubits_620, %pos_ctrl_out_qubits_621 = mqtopt.x() %out_qubits_619 ctrl %out_qubits_618 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_622 = mqtopt.rz(static [7.853982e-01]) %out_qubits_614 : !mqtopt.Qubit - %out_qubits_623 = mqtopt.rz(static [1.570796e+00]) %out_qubits_622 : !mqtopt.Qubit - %out_qubits_624 = mqtopt.sx() %out_qubits_623 : !mqtopt.Qubit - %out_qubits_625 = mqtopt.rz(static [1.570796e+00]) %out_qubits_624 : !mqtopt.Qubit - %out_qubits_626 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_625 : !mqtopt.Qubit - %out_qubits_627 = mqtopt.rz(static [1.570796e+00]) %out_qubits_626 : !mqtopt.Qubit - %out_qubits_628 = mqtopt.sx() %out_qubits_627 : !mqtopt.Qubit - %out_qubits_629 = mqtopt.rz(static [1.570796e+00]) %out_qubits_628 : !mqtopt.Qubit - %out_qubits_630, %pos_ctrl_out_qubits_631 = mqtopt.x() %out_qubits_629 ctrl %out_qubits_594 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_632 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_630 : !mqtopt.Qubit - %out_qubits_633, %pos_ctrl_out_qubits_634 = mqtopt.x() %out_qubits_632 ctrl %pos_ctrl_out_qubits_595 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_635 = mqtopt.rz(static [7.853982e-01]) %out_qubits_633 : !mqtopt.Qubit - %out_qubits_636, %pos_ctrl_out_qubits_637 = mqtopt.x() %out_qubits_635 ctrl %pos_ctrl_out_qubits_631 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_638 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_637 : !mqtopt.Qubit - %out_qubits_639 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_636 : !mqtopt.Qubit - %out_qubits_640, %pos_ctrl_out_qubits_641 = mqtopt.x() %out_qubits_639 ctrl %pos_ctrl_out_qubits_634 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_642, %pos_ctrl_out_qubits_643 = mqtopt.x() %out_qubits_638 ctrl %pos_ctrl_out_qubits_641 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_644 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_643 : !mqtopt.Qubit - %out_qubits_645 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_642 : !mqtopt.Qubit - %out_qubits_646, %pos_ctrl_out_qubits_647 = mqtopt.x() %out_qubits_645 ctrl %out_qubits_644 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_648 = mqtopt.rz(static [1.570796e+00]) %out_qubits_646 : !mqtopt.Qubit - %out_qubits_649 = mqtopt.sx() %out_qubits_648 : !mqtopt.Qubit - %out_qubits_650 = mqtopt.rz(static [1.570796e+00]) %out_qubits_649 : !mqtopt.Qubit - %out_qubits_651, %pos_ctrl_out_qubits_652 = mqtopt.x() %out_qubits_650 ctrl %out_qubits_620 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_653 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_651 : !mqtopt.Qubit - %out_qubits_654, %pos_ctrl_out_qubits_655 = mqtopt.x() %out_qubits_653 ctrl %pos_ctrl_out_qubits_621 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_656 = mqtopt.rz(static [7.853982e-01]) %out_qubits_654 : !mqtopt.Qubit - %out_qubits_657, %pos_ctrl_out_qubits_658 = mqtopt.x() %out_qubits_656 ctrl %pos_ctrl_out_qubits_652 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_659 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_658 : !mqtopt.Qubit - %out_qubits_660 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_657 : !mqtopt.Qubit - %out_qubits_661, %pos_ctrl_out_qubits_662 = mqtopt.x() %out_qubits_660 ctrl %pos_ctrl_out_qubits_655 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_663, %pos_ctrl_out_qubits_664 = mqtopt.x() %out_qubits_659 ctrl %pos_ctrl_out_qubits_662 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_665 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_664 : !mqtopt.Qubit - %out_qubits_666 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_663 : !mqtopt.Qubit - %out_qubits_667, %pos_ctrl_out_qubits_668 = mqtopt.x() %out_qubits_666 ctrl %out_qubits_665 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_669 = mqtopt.rz(static [7.853982e-01]) %out_qubits_661 : !mqtopt.Qubit - %out_qubits_670 = mqtopt.rz(static [1.570796e+00]) %out_qubits_669 : !mqtopt.Qubit - %out_qubits_671 = mqtopt.sx() %out_qubits_670 : !mqtopt.Qubit - %out_qubits_672 = mqtopt.rz(static [1.570796e+00]) %out_qubits_671 : !mqtopt.Qubit - %out_qubits_673 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_672 : !mqtopt.Qubit - %out_qubits_674, %pos_ctrl_out_qubits_675 = mqtopt.x() %out_qubits_673 ctrl %pos_ctrl_out_qubits_647 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_676 = mqtopt.rz(static [3.926991e-01]) %out_qubits_674 : !mqtopt.Qubit - %out_qubits_677 = mqtopt.rz(static [1.570796e+00]) %out_qubits_676 : !mqtopt.Qubit - %out_qubits_678 = mqtopt.sx() %out_qubits_677 : !mqtopt.Qubit - %out_qubits_679 = mqtopt.rz(static [1.570796e+00]) %out_qubits_678 : !mqtopt.Qubit - %out_qubits_680, %pos_ctrl_out_qubits_681 = mqtopt.x() %out_qubits_679 ctrl %out_qubits_667 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_682 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_680 : !mqtopt.Qubit - %out_qubits_683, %pos_ctrl_out_qubits_684 = mqtopt.x() %out_qubits_682 ctrl %pos_ctrl_out_qubits_668 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_685 = mqtopt.rz(static [7.853982e-01]) %out_qubits_683 : !mqtopt.Qubit - %out_qubits_686, %pos_ctrl_out_qubits_687 = mqtopt.x() %out_qubits_685 ctrl %pos_ctrl_out_qubits_681 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_688 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_687 : !mqtopt.Qubit - %out_qubits_689 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_686 : !mqtopt.Qubit - %out_qubits_690, %pos_ctrl_out_qubits_691 = mqtopt.x() %out_qubits_689 ctrl %pos_ctrl_out_qubits_684 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_692, %pos_ctrl_out_qubits_693 = mqtopt.x() %out_qubits_688 ctrl %pos_ctrl_out_qubits_691 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_694 = mqtopt.rz(static [7.853982e-01]) %pos_ctrl_out_qubits_693 : !mqtopt.Qubit - %out_qubits_695 = mqtopt.rz(static [-7.853982e-01]) %out_qubits_692 : !mqtopt.Qubit - %out_qubits_696, %pos_ctrl_out_qubits_697 = mqtopt.x() %out_qubits_695 ctrl %out_qubits_694 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_698 = mqtopt.rz(static [7.853982e-01]) %out_qubits_690 : !mqtopt.Qubit - %out_qubits_699 = mqtopt.rz(static [1.570796e+00]) %out_qubits_698 : !mqtopt.Qubit - %out_qubits_700 = mqtopt.sx() %out_qubits_699 : !mqtopt.Qubit - %out_qubits_701 = mqtopt.rz(static [1.570796e+00]) %out_qubits_700 : !mqtopt.Qubit - %out_qubits_702 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_701 : !mqtopt.Qubit - %out_qubits_703, %pos_ctrl_out_qubits_704 = mqtopt.x() %out_qubits_702 ctrl %pos_ctrl_out_qubits_675 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_705, %pos_ctrl_out_qubits_706 = mqtopt.x() %pos_ctrl_out_qubits_704 ctrl %pos_ctrl_out_qubits_697 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_707 = mqtopt.rz(static [-1.963495e-01]) %out_qubits_705 : !mqtopt.Qubit - %out_qubits_708, %pos_ctrl_out_qubits_709 = mqtopt.x() %out_qubits_707 ctrl %out_qubits_696 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_710 = mqtopt.rz(static [1.963495e-01]) %out_qubits_708 : !mqtopt.Qubit - %out_qubits_711, %pos_ctrl_out_qubits_712 = mqtopt.x() %out_qubits_710 ctrl %pos_ctrl_out_qubits_706 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_713 = mqtopt.rz(static [-1.963495e-01]) %out_qubits_711 : !mqtopt.Qubit - %out_qubits_714, %pos_ctrl_out_qubits_715 = mqtopt.x() %out_qubits_713 ctrl %pos_ctrl_out_qubits_709 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_716 = mqtopt.rz(static [1.963495e-01]) %pos_ctrl_out_qubits_715 : !mqtopt.Qubit - %out_qubits_717, %pos_ctrl_out_qubits_718 = mqtopt.x() %out_qubits_716 ctrl %pos_ctrl_out_qubits_712 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_719 = mqtopt.rz(static [-1.963495e-01]) %out_qubits_717 : !mqtopt.Qubit - %out_qubits_720, %pos_ctrl_out_qubits_721 = mqtopt.x() %out_qubits_719 ctrl %pos_ctrl_out_qubits_718 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_722 = mqtopt.rz(static [1.963495e-01]) %pos_ctrl_out_qubits_721 : !mqtopt.Qubit - %out_qubits_723 = mqtopt.rz(static [1.570796e+00]) %out_qubits_722 : !mqtopt.Qubit - %out_qubits_724 = mqtopt.sx() %out_qubits_723 : !mqtopt.Qubit - %out_qubits_725 = mqtopt.rz(static [1.570796e+00]) %out_qubits_724 : !mqtopt.Qubit - %out_qubits_726 = mqtopt.x() %out_qubits_725 : !mqtopt.Qubit - %out_qubits_727 = mqtopt.rz(static [3.926991e-01]) %out_qubits_726 : !mqtopt.Qubit - %out_qubits_728 = mqtopt.rz(static [1.570796e+00]) %out_qubits_720 : !mqtopt.Qubit - %out_qubits_729 = mqtopt.sx() %out_qubits_728 : !mqtopt.Qubit - %out_qubits_730 = mqtopt.rz(static [1.570796e+00]) %out_qubits_729 : !mqtopt.Qubit - %out_qubits_731 = mqtopt.x() %out_qubits_730 : !mqtopt.Qubit - %out_qubits_732 = mqtopt.rz(static [3.926991e-01]) %out_qubits_731 : !mqtopt.Qubit - %out_qubits_733, %pos_ctrl_out_qubits_734 = mqtopt.x() %out_qubits_732 ctrl %out_qubits_727 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_735 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_733 : !mqtopt.Qubit - %out_qubits_736, %pos_ctrl_out_qubits_737 = mqtopt.x() %out_qubits_735 ctrl %pos_ctrl_out_qubits_734 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_738 = mqtopt.rz(static [1.963495e-01]) %out_qubits_714 : !mqtopt.Qubit - %out_qubits_739 = mqtopt.rz(static [1.570796e+00]) %out_qubits_738 : !mqtopt.Qubit - %out_qubits_740 = mqtopt.sx() %out_qubits_739 : !mqtopt.Qubit - %out_qubits_741 = mqtopt.rz(static [1.570796e+00]) %out_qubits_740 : !mqtopt.Qubit - %out_qubits_742 = mqtopt.x() %out_qubits_741 : !mqtopt.Qubit - %out_qubits_743 = mqtopt.rz(static [3.926991e-01]) %out_qubits_742 : !mqtopt.Qubit - %out_qubits_744, %pos_ctrl_out_qubits_745 = mqtopt.x() %out_qubits_743 ctrl %out_qubits_736 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_746 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_744 : !mqtopt.Qubit - %out_qubits_747, %pos_ctrl_out_qubits_748 = mqtopt.x() %out_qubits_746 ctrl %pos_ctrl_out_qubits_737 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_749 = mqtopt.rz(static [3.926991e-01]) %out_qubits_747 : !mqtopt.Qubit - %out_qubits_750, %pos_ctrl_out_qubits_751 = mqtopt.x() %out_qubits_749 ctrl %pos_ctrl_out_qubits_745 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_752 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_750 : !mqtopt.Qubit - %out_qubits_753, %pos_ctrl_out_qubits_754 = mqtopt.x() %out_qubits_752 ctrl %pos_ctrl_out_qubits_748 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_755 = mqtopt.rz(static [3.926991e-01]) %out_qubits_703 : !mqtopt.Qubit - %out_qubits_756 = mqtopt.rz(static [1.570796e+00]) %out_qubits_755 : !mqtopt.Qubit - %out_qubits_757 = mqtopt.sx() %out_qubits_756 : !mqtopt.Qubit - %out_qubits_758 = mqtopt.rz(static [1.570796e+00]) %out_qubits_757 : !mqtopt.Qubit - %out_qubits_759 = mqtopt.x() %out_qubits_758 : !mqtopt.Qubit - %out_qubits_760 = mqtopt.rz(static [1.570796e+00]) %out_qubits_759 : !mqtopt.Qubit - %out_qubits_761 = mqtopt.sx() %out_qubits_760 : !mqtopt.Qubit - %out_qubits_762 = mqtopt.rz(static [1.570796e+00]) %out_qubits_761 : !mqtopt.Qubit - %out_qubits_763 = mqtopt.rz(static [1.570796e+00]) %out_qubits_762 : !mqtopt.Qubit - %out_qubits_764 = mqtopt.sx() %out_qubits_763 : !mqtopt.Qubit - %out_qubits_765 = mqtopt.rz(static [1.570796e+00]) %out_qubits_764 : !mqtopt.Qubit - %out_qubits_766 = mqtopt.rz(static [3.926991e-01]) %out_qubits_765 : !mqtopt.Qubit - %out_qubits_767, %pos_ctrl_out_qubits_768 = mqtopt.x() %out_qubits_766 ctrl %out_qubits_753 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_769 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_767 : !mqtopt.Qubit - %out_qubits_770, %pos_ctrl_out_qubits_771 = mqtopt.x() %out_qubits_769 ctrl %pos_ctrl_out_qubits_751 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_772 = mqtopt.rz(static [3.926991e-01]) %out_qubits_770 : !mqtopt.Qubit - %out_qubits_773, %pos_ctrl_out_qubits_774 = mqtopt.x() %out_qubits_772 ctrl %pos_ctrl_out_qubits_768 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_775 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_773 : !mqtopt.Qubit - %out_qubits_776, %pos_ctrl_out_qubits_777 = mqtopt.x() %out_qubits_775 ctrl %pos_ctrl_out_qubits_754 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_778 = mqtopt.rz(static [3.926991e-01]) %out_qubits_776 : !mqtopt.Qubit - %out_qubits_779, %pos_ctrl_out_qubits_780 = mqtopt.x() %out_qubits_778 ctrl %pos_ctrl_out_qubits_774 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_781 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_779 : !mqtopt.Qubit - %out_qubits_782, %pos_ctrl_out_qubits_783 = mqtopt.x() %out_qubits_781 ctrl %pos_ctrl_out_qubits_771 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_784 = mqtopt.x() %pos_ctrl_out_qubits_783 : !mqtopt.Qubit - %out_qubits_785 = mqtopt.rz(static [1.570796e+00]) %out_qubits_784 : !mqtopt.Qubit - %out_qubits_786 = mqtopt.sx() %out_qubits_785 : !mqtopt.Qubit - %out_qubits_787 = mqtopt.rz(static [1.570796e+00]) %out_qubits_786 : !mqtopt.Qubit - %out_qubits_788 = mqtopt.rz(static [3.926991e-01]) %out_qubits_782 : !mqtopt.Qubit - %out_qubits_789, %pos_ctrl_out_qubits_790 = mqtopt.x() %out_qubits_788 ctrl %pos_ctrl_out_qubits_780 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_791 = mqtopt.x() %pos_ctrl_out_qubits_790 : !mqtopt.Qubit - %out_qubits_792 = mqtopt.rz(static [1.570796e+00]) %out_qubits_791 : !mqtopt.Qubit - %out_qubits_793 = mqtopt.sx() %out_qubits_792 : !mqtopt.Qubit - %out_qubits_794 = mqtopt.rz(static [1.570796e+00]) %out_qubits_793 : !mqtopt.Qubit - %out_qubits_795 = mqtopt.rz(static [-3.926991e-01]) %out_qubits_789 : !mqtopt.Qubit - %out_qubits_796, %pos_ctrl_out_qubits_797 = mqtopt.x() %out_qubits_795 ctrl %pos_ctrl_out_qubits_777 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_798 = mqtopt.x() %pos_ctrl_out_qubits_797 : !mqtopt.Qubit - %out_qubits_799 = mqtopt.rz(static [1.570796e+00]) %out_qubits_798 : !mqtopt.Qubit - %out_qubits_800 = mqtopt.sx() %out_qubits_799 : !mqtopt.Qubit - %out_qubits_801 = mqtopt.rz(static [1.570796e+00]) %out_qubits_800 : !mqtopt.Qubit - %out_qubits_802 = mqtopt.rz(static [1.570796e+00]) %out_qubits_796 : !mqtopt.Qubit - %out_qubits_803 = mqtopt.sx() %out_qubits_802 : !mqtopt.Qubit - %out_qubits_804 = mqtopt.rz(static [1.570796e+00]) %out_qubits_803 : !mqtopt.Qubit - %out_qubits_805 = mqtopt.rz(static [1.570796e+00]) %out_qubits_804 : !mqtopt.Qubit - %out_qubits_806 = mqtopt.sx() %out_qubits_805 : !mqtopt.Qubit - %out_qubits_807 = mqtopt.rz(static [1.570796e+00]) %out_qubits_806 : !mqtopt.Qubit - %out_qubits_808 = mqtopt.x() %out_qubits_807 : !mqtopt.Qubit - %out_qubits_809 = mqtopt.rz(static [1.570796e+00]) %out_qubits_808 : !mqtopt.Qubit - %out_qubits_810 = mqtopt.sx() %out_qubits_809 : !mqtopt.Qubit - %out_qubits_811 = mqtopt.rz(static [1.570796e+00]) %out_qubits_810 : !mqtopt.Qubit - %out_qubits_812 = mqtopt.rz(static [7.853982e-01]) %out_qubits_640 : !mqtopt.Qubit - %out_qubits_813 = mqtopt.rz(static [1.570796e+00]) %out_qubits_812 : !mqtopt.Qubit - %out_qubits_814 = mqtopt.sx() %out_qubits_813 : !mqtopt.Qubit - %out_qubits_815 = mqtopt.rz(static [1.570796e+00]) %out_qubits_814 : !mqtopt.Qubit - %out_qubits_816 = mqtopt.rz(static [7.853982e-01]) %out_qubits_815 : !mqtopt.Qubit - %out_qubits_817:5 = mqtopt.barrier() %out_qubits_801, %out_qubits_787, %out_qubits_794, %out_qubits_811, %out_qubits_816 : !mqtopt.Qubit, !mqtopt.Qubit, !mqtopt.Qubit, !mqtopt.Qubit, !mqtopt.Qubit - %out_qubit, %out_bit = mqtopt.measure %out_qubits_817#0 - %c0_818 = arith.constant 0 : index - memref.store %out_bit, %alloca[%c0_818] : memref<5xi1> - %out_qubit_819, %out_bit_820 = mqtopt.measure %out_qubits_817#1 - %c1_821 = arith.constant 1 : index - memref.store %out_bit_820, %alloca[%c1_821] : memref<5xi1> - %out_qubit_822, %out_bit_823 = mqtopt.measure %out_qubits_817#2 - %c2_824 = arith.constant 2 : index - memref.store %out_bit_823, %alloca[%c2_824] : memref<5xi1> - %out_qubit_825, %out_bit_826 = mqtopt.measure %out_qubits_817#3 - %c3_827 = arith.constant 3 : index - memref.store %out_bit_826, %alloca[%c3_827] : memref<5xi1> - %out_qubit_828, %out_bit_829 = mqtopt.measure %out_qubits_817#4 - %c4 = arith.constant 4 : index - memref.store %out_bit_829, %alloca[%c4] : memref<5xi1> - return - } -} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/invalid-arch-option.mlir b/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/invalid-arch-option.mlir deleted file mode 100644 index 83f87198c3..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/invalid-arch-option.mlir +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// Instead of applying checks, the routing verifier pass ensures the validity of this program. - -// RUN: quantum-opt %s --placement-sc="arch=invalid-127" -verify-diagnostics -// RUN: quantum-opt %s --route-naive-sc="arch=invalid-127" -verify-diagnostics -// RUN: quantum-opt %s --route-astar-sc="arch=invalid-127" -verify-diagnostics -// RUN: quantum-opt %s --verify-routing-sc="arch=invalid-127" -verify-diagnostics - -// expected-error@unknown {{unsupported architecture}} -module {} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/missing-arch-option.mlir b/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/missing-arch-option.mlir deleted file mode 100644 index 68476237ff..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/missing-arch-option.mlir +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// Instead of applying checks, the routing verifier pass ensures the validity of this program. - -// RUN: quantum-opt %s --placement-sc -verify-diagnostics -// RUN: quantum-opt %s --route-naive-sc -verify-diagnostics -// RUN: quantum-opt %s --route-astar-sc -verify-diagnostics -// RUN: quantum-opt %s --verify-routing-sc -verify-diagnostics - -// expected-error@unknown {{required option 'arch' not provided}} -module {} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/routing-placement.mlir b/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/routing-placement.mlir deleted file mode 100644 index 80eafd16fd..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/routing-placement.mlir +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --placement-sc="strategy=identity arch=MQTTest" -verify-diagnostics - - -module { - func.func @entryTooManyQubits() attributes {passthrough = ["entry_point"]} { - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - %q3_0 = mqtopt.allocQubit - %q4_0 = mqtopt.allocQubit - %q5_0 = mqtopt.allocQubit - - // expected-error@+1 {{'mqtopt.allocQubit' op requires one too many qubits for the targeted architecture}} - %q6_0 = mqtopt.allocQubit - - mqtopt.deallocQubit %q0_0 - mqtopt.deallocQubit %q1_0 - mqtopt.deallocQubit %q2_0 - mqtopt.deallocQubit %q3_0 - mqtopt.deallocQubit %q4_0 - mqtopt.deallocQubit %q5_0 - mqtopt.deallocQubit %q6_0 - - return - } -} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/routing-verification.mlir b/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/routing-verification.mlir deleted file mode 100644 index 70e1be116d..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/Transpilation/routing-verification.mlir +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --verify-routing-sc="arch=MQTTest" -verify-diagnostics - -module { - func.func @gateNotExecutable() attributes {passthrough = ["entry_point"]} { - %q0_0 = mqtopt.qubit 0 - %q4_0 = mqtopt.qubit 4 - - // expected-error@+1 {{'mqtopt.x' op (0,4) is not executable on target architecture 'MQT-Test'}} - %q0_1, %q4_1 = mqtopt.x() %q0_0 ctrl %q4_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - return - } -} - -// ----- - -module { - func.func @layoutsDontMatch() attributes {passthrough = ["entry_point"]} { - %q0_0 = mqtopt.qubit 0 - %q1_0 = mqtopt.qubit 1 - %c0 = arith.constant 0 : i1 - - %q0_1, %q1_1 = scf.if %c0 -> (!mqtopt.Qubit, !mqtopt.Qubit) { - %q0_1, %q1_1 = mqtopt.swap() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - // expected-error@+1 {{'scf.yield' op layouts must match after restoration}} - scf.yield %q0_1, %q1_1 : !mqtopt.Qubit, !mqtopt.Qubit - } else { - scf.yield %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - } - - return - } -} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/branch-opt.mlir b/mlir/test/Dialect/MQTOpt/Transforms/branch-opt.mlir deleted file mode 100644 index 9ddf9e6385..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/branch-opt.mlir +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --quantum-sink | FileCheck %s - - -// ----- -// The X-Gate applied in the main block can be pushed down into the two child blocks -// This process also eliminates unneeded block parameters for the `then` and `else` blocks. - -module { - func.func @testPushIntoChildren() { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - // CHECK-NOT: %[[ANY:.*]] = mqtopt.x() - %q1_1 = mqtopt.x() %q1_0 : !mqtopt.Qubit - - // CHECK: %[[Q0_1:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_0]] - %q0_1, %c0_0 = mqtopt.measure %q0_0 - // CHECK: cf.cond_br %[[C0_0]], ^[[THEN:.*]], ^[[ELSE:.*]] - cf.cond_br %c0_0, ^then(%q1_1 : !mqtopt.Qubit), ^else(%q1_1 : !mqtopt.Qubit) - - // CHECK: ^[[THEN]]: - ^then(%q1_2then : !mqtopt.Qubit): - // CHECK: %[[Q1_1T:.*]] = mqtopt.x() %[[Q1_0]] - // CHECK: cf.br ^[[CONTINUE:.*]](%[[Q1_1T]] : !mqtopt.Qubit) - cf.br ^continue(%q1_2then : !mqtopt.Qubit) - - // CHECK: ^[[ELSE]]: - ^else(%q1_2else : !mqtopt.Qubit): - // CHECK: %[[Q1_1E:.*]] = mqtopt.x() %[[Q1_0]] - // CHECK: %[[Q1_2E:.*]] = mqtopt.x() %[[Q1_1E]] - %q1_3 = mqtopt.x() %q1_2else : !mqtopt.Qubit - // CHECK: cf.br ^[[CONTINUE]](%[[Q1_2E]] : !mqtopt.Qubit) - cf.br ^continue(%q1_3 : !mqtopt.Qubit) - - ^continue(%q1_4 : !mqtopt.Qubit): - memref.store %q0_1, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_4, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - - -// ----- -// The X-Gate applied in the main block can be pushed down into the two child blocks -// This test does not use block parameters to pass the state of the qubit to the blocks, but the -// `quantum-sink` pass should still be able to work with them. -// The result of this pass should be the same as for the `branch-opt.mlir` test. - -module { - func.func @testPushIntoChildrenNoBlockParameters() { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - // CHECK-NOT: %[[ANY:.*]] = mqtopt.x() - %q1_1 = mqtopt.x() %q1_0 : !mqtopt.Qubit - - // CHECK: %[[Q0_1:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_0]] - %q0_1, %c0_0 = mqtopt.measure %q0_0 - // CHECK: cf.cond_br %[[C0_0]], ^[[THEN:.*]], ^[[ELSE:.*]] - cf.cond_br %c0_0, ^then, ^else - - // CHECK: ^[[THEN]]: - ^then: - // CHECK: %[[Q1_1T:.*]] = mqtopt.x() %[[Q1_0]] - // CHECK: cf.br ^[[CONTINUE:.*]](%[[Q1_1T]] : !mqtopt.Qubit) - cf.br ^continue(%q1_1 : !mqtopt.Qubit) - - // CHECK: ^[[ELSE]]: - ^else: - // CHECK: %[[Q1_1E:.*]] = mqtopt.x() %[[Q1_0]] - // CHECK: %[[Q1_2E:.*]] = mqtopt.x() %[[Q1_1E]] - %q1_3 = mqtopt.x() %q1_1 : !mqtopt.Qubit - // CHECK: cf.br ^[[CONTINUE]](%[[Q1_2E]] : !mqtopt.Qubit) - cf.br ^continue(%q1_3 : !mqtopt.Qubit) - - ^continue(%q1_4 : !mqtopt.Qubit): - memref.store %q0_1, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_4, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - - -// ----- -// The X-Gate applied in the main block can be pushed down into the final `continue` block -// which is not a direct successor of the main block. -// This process also eliminates unneeded block parameters for the `then` and `else` blocks. - -module { - func.func @main() { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - // CHECK-NOT: %[[ANY:.*]] = mqtopt.x() - %q1_1 = mqtopt.x() %q1_0 : !mqtopt.Qubit - - // CHECK: %[[Q0_1:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_0]] - %q0_1, %c0_0 = mqtopt.measure %q0_0 - cf.cond_br %c0_0, ^then(%q0_1 : !mqtopt.Qubit), ^else(%q0_1 : !mqtopt.Qubit) - - ^then(%q0_1then : !mqtopt.Qubit): - %q0_2then = mqtopt.x() %q0_1then : !mqtopt.Qubit - cf.br ^continue(%q0_2then : !mqtopt.Qubit) - - ^else(%q0_1else : !mqtopt.Qubit): - %q0_2else = mqtopt.y() %q0_1else : !mqtopt.Qubit - cf.br ^continue(%q0_2else : !mqtopt.Qubit) - - ^continue(%q0_2 : !mqtopt.Qubit): - // CHECK: %[[Q1_1:.*]] = mqtopt.x() %[[Q1_0]] - // CHECK: %[[Q1_2:.*]] = mqtopt.x() %[[Q1_1]] - %q1_2 = mqtopt.x() %q1_1 : !mqtopt.Qubit - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - - -// ----- -// This test checks the `quantum-sink` pass in a more complex case that contains a critical edge. -// `main` is succeeded by `then` and `continue`, while `continue` has two predecessors; `main` and `then`. -// Therefore, to push the X-Gate down, the critical edge must be broken by introducing a new block `else`. - -module { - func.func @testWithCriticalEdge() { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - // CHECK-NOT: %[[ANY:.*]] = mqtopt.x() - %q1_1 = mqtopt.x() %q1_0 : !mqtopt.Qubit - - // CHECK: %[[Q0_1:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_0]] - %q0_1, %c0_0 = mqtopt.measure %q0_0 - // CHECK: cf.cond_br %[[C0_0]], ^[[THEN:.*]], ^[[ELSE:.*]] - cf.cond_br %c0_0, ^then(%q1_1 : !mqtopt.Qubit), ^continue(%q1_1 : !mqtopt.Qubit) - - // CHECK: ^[[THEN]]: - ^then(%q1_2then : !mqtopt.Qubit): - // CHECK: %[[Q1_1T:.*]] = mqtopt.x() %[[Q1_0]] - // CHECK: %[[Q1_2T:.*]] = mqtopt.x() %[[Q1_1T]] - %q1_3 = mqtopt.x() %q1_2then : !mqtopt.Qubit - // CHECK: cf.br ^[[CONTINUE:.*]](%[[Q1_2T]] : !mqtopt.Qubit) - cf.br ^continue(%q1_3 : !mqtopt.Qubit) - - // CHECK: ^[[CONTINUE]](%[[Q1_4:.*]]: !mqtopt.Qubit): - ^continue(%q1_4 : !mqtopt.Qubit): - memref.store %q0_1, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_4, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } - - // CHECK: ^[[ELSE]]: - // CHECK: %[[Q1_1E:.*]] = mqtopt.x() %[[Q1_0]] - // CHECK: cf.br ^[[CONTINUE]](%[[Q1_1E]] : !mqtopt.Qubit) -} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/gate-elimination.mlir b/mlir/test/Dialect/MQTOpt/Transforms/gate-elimination.mlir deleted file mode 100644 index e7a01d24b9..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/gate-elimination.mlir +++ /dev/null @@ -1,374 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --gate-elimination | FileCheck %s - -// ----- -// This test checks if single-qubit consecutive self-inverses are canceled correctly. -// In this example, most operations should be canceled including cases where: -// - The operations are directly consecutive -// - There are operations on other qubits interleaved between them -// - There are operations on the same qubits interleaved between them that will also get canceled, -// allowing the outer consecutive pair to be canceled as well - -module { - func.func @testCancelSingleQubitGates() { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - // ========================== Check for operations that should not be canceled ========================== - // CHECK: %[[Q0_1:.*]] = mqtopt.z() %[[Q0_0]] : !mqtopt.Qubit - - // ========================== Check for operations that should be canceled ============================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] : !mqtopt.Qubit - // CHECK-NOT: %[[ANY:.*]] = mqtopt.z() %[[ANY:.*]] : !mqtopt.Qubit - - %q1_1 = mqtopt.x() %q1_0 : !mqtopt.Qubit - %q1_2 = mqtopt.x() %q1_1 : !mqtopt.Qubit - %q1_3 = mqtopt.x() %q1_2 : !mqtopt.Qubit - %q1_4 = mqtopt.z() %q1_3 : !mqtopt.Qubit - %q0_1 = mqtopt.z() %q0_0 : !mqtopt.Qubit - %q0_2 = mqtopt.x() %q0_1 : !mqtopt.Qubit - %q0_3 = mqtopt.x() %q0_2 : !mqtopt.Qubit - %q1_5 = mqtopt.z() %q1_4 : !mqtopt.Qubit - %q1_6 = mqtopt.x() %q1_5 : !mqtopt.Qubit - - // CHECK: memref.store %[[Q0_1]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - memref.store %q0_3, %qreg[%i0] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_0]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - memref.store %q1_6, %qreg[%i1] : memref<2x!mqtopt.Qubit> - // CHECK: memref.dealloc %[[Qreg]] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks if two-qubit consecutive self-inverses are canceled correctly. -// For this, the operations must involve exactly the same qubits. - -module { - func.func @testCancelMultiQubitGates() { - // CHECK: %[[I2:.*]] = arith.constant 2 : index - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<3x!mqtopt.Qubit> - %qreg = memref.alloc() : memref<3x!mqtopt.Qubit> - - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<3x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<3x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<3x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<3x!mqtopt.Qubit> - // CHECK: %[[Q2_0:.*]] = memref.load %[[Qreg]][%[[I2]]] : memref<3x!mqtopt.Qubit> - %q2_0 = memref.load %qreg[%i2] : memref<3x!mqtopt.Qubit> - - //===----------------------------------------------------------------===// - // INPUT OUTPUT - // q_0: ──■────■──────────── >>> ────────── - // ┌─┴─┐┌─┴─┐┌───┐ >>> ┌───┐ - // q_1: ┤ X ├┤ X ├┤ X ├──■── >>> ┤ X ├──■── - // └───┘└───┘└─┬─┘┌─┴─┐ >>> └─┬─┘┌─┴─┐ - // q_2: ────────────■──┤ X ├ >>> ──■──┤ X ├ - // └───┘ >>> └───┘ - //===----------------------------------------------------------------===// - // Check for operations that should not be cancelled - //===----------------------------------------------------------------===// - // CHECK: %[[Q1_1:.*]], %[[Q2_1:.*]] = mqtopt.x() %[[Q1_0]] ctrl %[[Q2_0]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q2_2:.*]], %[[Q1_2:.*]] = mqtopt.x() %[[Q2_1]] ctrl %[[Q1_1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - - //===----------------------------------------------------------------===// - // Check for operations that should be canceled - //===----------------------------------------------------------------===// - // CHECK-NOT: %[[ANY:.*]], %[[ANY:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[Q0_0]] : !mqtopt.Qubit, !mqtopt.Qubit - - %q1_1, %q0_1 = mqtopt.x() %q1_0 ctrl %q0_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_2, %q0_2 = mqtopt.x() %q1_1 ctrl %q0_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_3, %q2_1 = mqtopt.x() %q1_2 ctrl %q2_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q2_2, %q1_4 = mqtopt.x() %q2_1 ctrl %q1_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - // CHECK: memref.store %[[Q0_0]], %[[Qreg]][%[[I0]]] : memref<3x!mqtopt.Qubit> - memref.store %q0_2, %qreg[%i0] : memref<3x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<3x!mqtopt.Qubit> - memref.store %q1_4, %qreg[%i1] : memref<3x!mqtopt.Qubit> - // CHECK: memref.store %[[Q2_2]], %[[Qreg]][%[[I2]]] : memref<3x!mqtopt.Qubit> - memref.store %q2_2, %qreg[%i2] : memref<3x!mqtopt.Qubit> - // CHECK: memref.dealloc %[[Qreg]] : memref<3x!mqtopt.Qubit> - memref.dealloc %qreg : memref<3x!mqtopt.Qubit> - - return - } -} - -// ----- -// Checks if `dagger` gates correctly cancel their inverses, too - -module { - func.func @testCancelMultiQubitGates() { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - %i0 = arith.constant 0 : index - - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<1x!mqtopt.Qubit> - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - - // CHECK: %[[Q_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - %q_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - // ========================== Check for operations that should not be canceled ========================== - // CHECK: %[[Q_1:.*]] = mqtopt.sx() %[[Q_0]] - // CHECK: %[[Q_2:.*]] = mqtopt.sx() %[[Q_1]] - - // ========================== Check for operations that should be canceled ============================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.s() - // CHECK-NOT: %[[ANY:.*]] = mqtopt.tdg() - // CHECK-NOT: %[[ANY:.*]] = mqtopt.t() - // CHECK-NOT: %[[ANY:.*]] = mqtopt.sdg() - - %q_1 = mqtopt.s() %q_0 : !mqtopt.Qubit - %q_2 = mqtopt.tdg() %q_1 : !mqtopt.Qubit - %q_3 = mqtopt.t() %q_2 : !mqtopt.Qubit - %q_4 = mqtopt.sdg() %q_3 : !mqtopt.Qubit - %q_5 = mqtopt.sx() %q_4 : !mqtopt.Qubit - %q_6 = mqtopt.sx() %q_5 : !mqtopt.Qubit - - memref.store %q_6, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - - -// ----- -// Checks that controlled gates with different control polarities are not canceled - -module { - func.func @testDontCancelDifferingControlPolarities() { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - // CHECK: %[[Q1_1:.*]], %[[Q0_1:.*]] = mqtopt.x() %[[Q1_0]] ctrl %[[Q0_0]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q1_2:.*]], %[[Q2_2:.*]] = mqtopt.x() %[[Q1_1]] nctrl %[[Q2_1]] : !mqtopt.Qubit nctrl !mqtopt.Qubit - - %q1_1, %q0_1 = mqtopt.x() %q1_0 ctrl %q0_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_2, %q0_2 = mqtopt.x() %q1_1 nctrl %q0_1 : !mqtopt.Qubit nctrl !mqtopt.Qubit - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - - -// ----- -// Checks that controlled gates with different numbers of controls are not canceled - -module { - func.func @testDontCancelDifferentNumberOfQubits() { - // CHECK: %[[I2:.*]] = arith.constant 2 : index - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<3x!mqtopt.Qubit> - %qreg = memref.alloc() : memref<3x!mqtopt.Qubit> - - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<3x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<3x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<3x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<3x!mqtopt.Qubit> - // CHECK: %[[Q2_0:.*]] = memref.load %[[Qreg]][%[[I2]]] : memref<3x!mqtopt.Qubit> - %q2_0 = memref.load %qreg[%i2] : memref<3x!mqtopt.Qubit> - - //===------------------------------------------------------------------===// - // INPUT OUTPUT - // q_0: ──■────■── >>> ──■────■── - // ┌─┴─┐┌─┴─┐ >>> ┌─┴─┐┌─┴─┐ - // q_1: ┤ X ├┤ X ├ >>> ┤ X ├┤ X ├ - // └───┘└─┬─┘ >>> └───┘└─┬─┘ - // q_2: ───────■── >>> ───────■── - //===----------------------------------------------------------------===// - // Check for operations that should not be cancelled - //===----------------------------------------------------------------===// - // CHECK: %[[Q1_1:.*]], %[[Q0_1:.*]] = mqtopt.x() %[[Q1_0]] ctrl %[[Q0_0]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q1_2:.*]], %[[Q02_2:.*]]:2 = mqtopt.x() %[[Q1_1]] ctrl %[[Q0_1]], %[[Q2_0]] : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - - %q1_1, %q0_1 = mqtopt.x() %q1_0 ctrl %q0_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_2, %q02_1:2 = mqtopt.x() %q1_1 ctrl %q0_1, %q2_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q02_1#0, %qreg[%i0] : memref<3x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<3x!mqtopt.Qubit> - memref.store %q02_1#1, %qreg[%i2] : memref<3x!mqtopt.Qubit> - memref.dealloc %qreg : memref<3x!mqtopt.Qubit> - - return - } -} - - -// ----- -// This test checks if a single identity operation is removed correctly. - -module { - // CHECK-LABEL: func @testRemoveSingleIdentity() - func.func @testRemoveSingleIdentity() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - - // ========================== Check for operations that should not be canceled ========================== - // CHECK: %[[Q0_1:.*]] = mqtopt.x() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_3:.*]] = mqtopt.z() %[[Q0_1]] : !mqtopt.Qubit - - // ========================== Check for operations that should be canceled ============================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.i() %[[ANY:.*]] : !mqtopt.Qubit - - // CHECK: mqtopt.deallocQubit %[[Q0_3]] - - %q0_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.x() %q0_0 : !mqtopt.Qubit - %q0_2 = mqtopt.i() %q0_1 : !mqtopt.Qubit - %q0_3 = mqtopt.z() %q0_2 : !mqtopt.Qubit - - mqtopt.deallocQubit %q0_3 - return - } -} - - -// ----- -// This test checks if consecutive identity operations are removed correctly. - -module { - // CHECK-LABEL: func @testRemoveConsecutiveIdentities() - func.func @testRemoveConsecutiveIdentities() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - - // ========================== Check for operations that should not be canceled ========================== - // CHECK: %[[Q0_1:.*]] = mqtopt.x() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_7:.*]] = mqtopt.z() %[[Q0_1]] : !mqtopt.Qubit - - // ========================== Check for operations that should be canceled ============================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.i() %[[ANY:.*]] : !mqtopt.Qubit - - // CHECK: mqtopt.deallocQubit %[[Q0_7]] - - %q0_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.x() %q0_0 : !mqtopt.Qubit - %q0_2 = mqtopt.i() %q0_1 : !mqtopt.Qubit - %q0_3 = mqtopt.i() %q0_2 : !mqtopt.Qubit - %q0_4 = mqtopt.i() %q0_3 : !mqtopt.Qubit - %q0_5 = mqtopt.i() %q0_4 : !mqtopt.Qubit - %q0_6 = mqtopt.i() %q0_5 : !mqtopt.Qubit - %q0_7 = mqtopt.z() %q0_6 : !mqtopt.Qubit - - mqtopt.deallocQubit %q0_7 - return - } -} - - -// ----- -// This test checks if controlled identity operations are removed correctly. - -module { - // CHECK-LABEL: func @testRemoveControlledIdentities() - func.func @testRemoveControlledIdentities() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // ========================== Check for operations that should not be canceled ========================== - // CHECK: %[[Q0_1:.*]] = mqtopt.x() %[[Q0_0]] : !mqtopt.Qubit - - // ========================== Check for operations that should be canceled ============================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.i() %[[ANY:.*]] ctrl %[[ANY:.*]] : !mqtopt.Qubit - - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_0]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.x() %q0_0 : !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.i() %q1_0 ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_2 - mqtopt.deallocQubit %q1_1 - return - } -} - - -// ----- -// This test checks if all identity operations are removed correctly. - -module { - // CHECK-LABEL: func @testRemoveAllIdentities() - func.func @testRemoveAllIdentities() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - - // ========================== Check for operations that should not be canceled ========================== - // CHECK: %[[Q0_1:.*]] = mqtopt.x() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q1_4:.*]] = mqtopt.z() %[[Q1_0]] : !mqtopt.Qubit - - // ========================== Check for operations that should be canceled ============================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.i() %[[ANY:.*]] : !mqtopt.Qubit - - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_4]] - // CHECK: mqtopt.deallocQubit %[[Q2_0]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.x() %q0_0 : !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.i() %q1_0 ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3 = mqtopt.i() %q0_2 : !mqtopt.Qubit - - %q1_2 = mqtopt.i() %q1_1 : !mqtopt.Qubit - %q1_3 = mqtopt.i() %q1_2 : !mqtopt.Qubit - %q1_4 = mqtopt.z() %q1_3 : !mqtopt.Qubit - %q2_1, %q1_5, %q0_4 = mqtopt.i() %q2_0 ctrl %q1_4, %q0_3: !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - - mqtopt.deallocQubit %q0_4 - mqtopt.deallocQubit %q1_5 - mqtopt.deallocQubit %q2_1 - return - } -} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir b/mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir deleted file mode 100644 index 8722a240a7..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir +++ /dev/null @@ -1,421 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --lift-measurements | FileCheck %s - -// =================================================== Lift over Controls ========================================================================= -// ----- -// This test checks that measurements can be lifted over controlled gates if the measurement target is a positive control and the only control of the gate. -// In this case, the controlled gate is replaced by a conditional gate that uses the measurement outcome as a condition. - -module { - // CHECK-LABEL: func.func @testLiftOverPositiveControl - func.func @testLiftOverPositiveControl() -> (i1, i1) { - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[ANY:.*]] ctrl %[[ANY:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0:.*]] = mqtopt.measure %[[Q0_1:.*]] - // CHECK: %[[Q1_2:.*]] = scf.if %[[C0]] -> (!mqtopt.Qubit) { - // CHECK-NEXT: %[[Q1_2IF:.*]] = mqtopt.h() %[[Q1_1]] : !mqtopt.Qubit - // CHECK-NEXT: scf.yield %[[Q1_2IF]] - // CHECK-NEXT: } else { - // CHECK-NEXT: scf.yield %[[Q1_1]] - // CHECK-NEXT: } - // CHECK: %[[Q1_3:.*]] = scf.if %[[C0]] -> (!mqtopt.Qubit) { - // CHECK-NEXT: %[[Q1_3IF:.*]] = mqtopt.x() %[[Q1_2]] : !mqtopt.Qubit - // CHECK-NEXT: scf.yield %[[Q1_3IF]] - // CHECK-NEXT: } else { - // CHECK-NEXT: scf.yield %[[Q1_2]] - // CHECK-NEXT: } - // CHECK: mqtopt.deallocQubit %[[Q0_2]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_2, %q0_2 = mqtopt.h() %q1_1 ctrl %q0_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_3, %q0_3 = mqtopt.x() %q1_2 ctrl %q0_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_4, %c0 = mqtopt.measure %q0_3 - %q1_4, %c1 = mqtopt.measure %q1_3 - - mqtopt.deallocQubit %q0_4 - mqtopt.deallocQubit %q1_4 - return %c0, %c1 : i1, i1 - } -} - -// ----- -// This test checks that measurements can be lifted over controlled gates if the measurement target is a negative control and the only control of the gate. -// In this case, the controlled gate is replaced by a conditional gate that uses the negated measurement outcome as a condition. - -module { - // CHECK-LABEL: func.func @testLiftOverNegativeControl - func.func @testLiftOverNegativeControl() -> (i1, i1) { - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[ANY:.*]] nctrl %[[ANY:.*]] : !mqtopt.Qubit nctrl !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: %[[Q1_2:.*]] = scf.if %[[C0]] -> (!mqtopt.Qubit) { - // CHECK-NEXT: scf.yield %[[Q1_1]] - // CHECK-NEXT: } else { - // CHECK-NEXT: %[[Q1_2IF:.*]] = mqtopt.h() %[[Q1_1]] : !mqtopt.Qubit - // CHECK-NEXT: scf.yield %[[Q1_2IF]] - // CHECK-NEXT: } - // CHECK: %[[Q1_3:.*]] = scf.if %[[C0]] -> (!mqtopt.Qubit) { - // CHECK-NEXT: scf.yield %[[Q1_2]] - // CHECK-NEXT: } else { - // CHECK-NEXT: %[[Q1_3IF:.*]] = mqtopt.x() %[[Q1_2]] : !mqtopt.Qubit - // CHECK-NEXT: scf.yield %[[Q1_3IF]] - // CHECK-NEXT: } - // CHECK: mqtopt.deallocQubit %[[Q0_2]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.x() %q0_0 nctrl %q1_0 : !mqtopt.Qubit nctrl !mqtopt.Qubit - %q1_2, %q0_2 = mqtopt.h() %q1_1 nctrl %q0_1 : !mqtopt.Qubit nctrl !mqtopt.Qubit - %q1_3, %q0_3 = mqtopt.x() %q1_2 nctrl %q0_2 : !mqtopt.Qubit nctrl !mqtopt.Qubit - - %q0_4, %c0 = mqtopt.measure %q0_3 - %q1_4, %c1 = mqtopt.measure %q1_3 - - mqtopt.deallocQubit %q0_4 - mqtopt.deallocQubit %q1_4 - return %c0, %c1 : i1, i1 - } -} - -// ----- -// This test checks that measurements can be lifted over controlled gates if the measurement target is one of multiple controls of the gate. -// In this case, the controlled gate is replaced by a conditional gate that uses the measurement outcome as a condition and still uses the remaining controls. - -module { - // CHECK-LABEL: func.func @testLiftOverOneOfMultipleControls - func.func @testLiftOverOneOfMultipleControls() -> (i1) { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_1:.*]], %[[C1:.*]] = mqtopt.measure %[[Q1_0]] - // CHECK: %[[Q02_1:.*]]:2 = scf.if %[[C1]] -> - // CHECK-NEXT: %[[Q0_1IF:.*]], %[[Q2_1IF:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q2_0]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK-NEXT: scf.yield %[[Q0_1IF:.*]], %[[Q2_1IF:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK-NEXT: } else { - // CHECK-NEXT: scf.yield %[[Q0_0]], %[[Q2_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK-NEXT: } - // CHECK: %[[Q02_2:.*]]:2 = scf.if %[[C1]] -> (!mqtopt.Qubit, !mqtopt.Qubit) { - // CHECK-NEXT: scf.yield %[[Q02_1]]#0, %[[Q02_1]]#1 - // CHECK-NEXT: } else { - // CHECK-NEXT: %[[Q0_2IF:.*]], %[[Q2_2IF:.*]] = mqtopt.x() %[[Q02_1]]#0 nctrl %[[Q02_1]]#1 : !mqtopt.Qubit nctrl !mqtopt.Qubit - // CHECK-NEXT: scf.yield %[[Q0_2IF]], %[[Q2_2IF]] - // CHECK-NEXT: } - // CHECK: %[[Q02_3:.*]]:2 = scf.if %[[C1]] -> (!mqtopt.Qubit, !mqtopt.Qubit) { - // CHECK-NEXT: %[[Q0_3IF:.*]], %[[Q2_3IF:.*]] = mqtopt.x() %[[Q02_2]]#0 nctrl %[[Q02_2]]#1 : !mqtopt.Qubit nctrl !mqtopt.Qubit - // CHECK-NEXT: scf.yield %[[Q0_3IF]], %[[Q2_3IF]] - // CHECK-NEXT: } else { - // CHECK-NEXT: scf.yield %[[Q02_2]]#0, %[[Q02_2]]#1 - // CHECK-NEXT: } - // CHECK: mqtopt.deallocQubit %[[Q1_1]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q0_1, %q12_0:2 = mqtopt.x() %q0_0 ctrl %q1_0, %q2_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - %q0_2, %q12_1:2 = mqtopt.x() %q0_1 nctrl %q12_0#1, %q12_0#0 : !mqtopt.Qubit nctrl !mqtopt.Qubit, !mqtopt.Qubit - %q0_3, %q1_3, %q2_3 = mqtopt.x() %q0_2 ctrl %q12_1#1 nctrl %q12_1#0 : !mqtopt.Qubit ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - - %q1_4, %c1 = mqtopt.measure %q1_3 - - %q0_4 = mqtopt.h() %q0_3 : !mqtopt.Qubit - %q2_4 = mqtopt.h() %q2_3 : !mqtopt.Qubit - %q0_5, %c0 = mqtopt.measure %q0_4 - %q2_5, %c2 = mqtopt.measure %q2_4 - - mqtopt.deallocQubit %q0_5 - mqtopt.deallocQubit %q1_4 - mqtopt.deallocQubit %q2_5 - return %c1 : i1 - } -} - -// ----- -// This test checks that several measurements can be lifted over the same controlled gate if the measurement targets are controls of the gate. -// In this case, the controlled gate is replaced by a conditional gate that uses the measurement outcomes as conditions. - -module { - // CHECK-LABEL: func.func @testLiftMultipleMeasuresOverControlledGate - func.func @testLiftMultipleMeasuresOverControlledGate() -> (i1, i1) { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q2_1:.*]], %[[C2:.*]] = mqtopt.measure %[[Q2_0]] - // CHECK: %[[Q1_1:.*]], %[[C1:.*]] = mqtopt.measure %[[Q1_0]] - // CHECK: %[[Q02_1:.*]]:2 = scf.if %[[C1]] -> (!mqtopt.Qubit, !mqtopt.Qubit) { - // CHECK-NEXT: %[[Q0_1IF:.*]] = scf.if %[[C2]] -> (!mqtopt.Qubit) { - // CHECK-NEXT: %[[Q0_1IFIF:.*]] = mqtopt.x() %[[ANY:.*]] : !mqtopt.Qubit - // CHECK-NEXT: scf.yield %[[Q0_1IFIF]] - // CHECK-NEXT: } else { - // CHECK-NEXT: scf.yield %[[ANY:.*]] - // CHECK-NEXT: } - // CHECK-NEXT: scf.yield %[[Q0_1IF]], %[[Q2_1]] - // CHECK-NEXT: } else { - // CHECK-NEXT: scf.yield %[[ANY:.*]], %[[Q2_1]] - // CHECK-NEXT: } - // CHECK: mqtopt.deallocQubit %[[Q02_1]]#0 - // CHECK: mqtopt.deallocQubit %[[Q1_1]] - // CHECK: mqtopt.deallocQubit %[[Q02_1]]#1 - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q0_1, %q12_0:2 = mqtopt.x() %q0_0 ctrl %q1_0, %q2_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - - %q1_1, %c1 = mqtopt.measure %q12_0#0 - %q2_1, %c2 = mqtopt.measure %q12_0#1 - - mqtopt.deallocQubit %q0_1 - mqtopt.deallocQubit %q1_1 - mqtopt.deallocQubit %q2_1 - return %c1, %c2 : i1, i1 - } -} - -// ----- -// This test checks that measurements can be lifted over controlled parametrized gates and the resulting classically controlled parametrized gates -// still have the correct parameters. - -module { - // CHECK-LABEL: func.func @testLiftControlledParametrizedGate - func.func @testLiftControlledParametrizedGate() -> (i1, i1) { - // CHECK: %[[Q1_1:.*]], %[[C1:.*]] = mqtopt.measure %[[Q1_0:.*]] - // CHECK: %[[Q0_1:.*]] = scf.if %[[C1]] -> (!mqtopt.Qubit) { - // CHECK-NEXT: %[[Q0_1IF:.*]] = mqtopt.rx(%[[ANGLE:.*]]) %[[Q0_0:.*]] : !mqtopt.Qubit - // CHECK-NEXT: scf.yield %[[Q0_1IF]] - // CHECK-NEXT: } else { - // CHECK-NEXT: scf.yield %[[Q0_0]] - // CHECK-NEXT: } - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %angle = arith.constant 3.000000e-01 : f64 - - %q0_1, %q1_1 = mqtopt.rx(%angle) %q0_0 ctrl %q1_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_2, %c0 = mqtopt.measure %q0_1 - %q1_2, %c1 = mqtopt.measure %q1_1 - - mqtopt.deallocQubit %q0_2 - mqtopt.deallocQubit %q1_2 - return %c0, %c1 : i1, i1 - } -} - -// =================================================== Lift over 1Q Gates ========================================================================= - -// ----- -// This test checks that measurements can be lifted over a single X Gate by adding a classical negation afterwards. - -module { - // CHECK-LABEL: func.func @testLiftOverSingleX - func.func @testLiftOverSingleX() -> i1 { - // CHECK: %[[Q0_1:.*]], %[[C0_0:.*]] = mqtopt.measure %[[ANY:.*]] - // CHECK-NOT: mqtopt.x - // CHECK: %[[C0_1:.*]] = arith.xori %[[C0_0]], %[[TRUE:.*]] : i1 - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: return %[[C0_1]] - - %q0_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.x() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0 = mqtopt.measure %q0_1 - - mqtopt.deallocQubit %q0_2 - return %c0 : i1 - } -} - -// ----- -// This test checks that measurements can be lifted over a single Y Gate by adding a classical negation afterwards. -// Essentially, a Y Gate before a measurement can be treated just like an X Gate - -module { - // CHECK-LABEL: func.func @testLiftOverSingleY - func.func @testLiftOverSingleY() -> i1 { - // CHECK: %[[Q0_1:.*]], %[[C0_0:.*]] = mqtopt.measure %[[ANY:.*]] - // CHECK-NOT: mqtopt.y - // CHECK: %[[C0_1:.*]] = arith.xori %[[C0_0]], %[[TRUE:.*]] : i1 - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: return %[[C0_1]] - - %q0_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.y() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0 = mqtopt.measure %q0_1 - - mqtopt.deallocQubit %q0_2 - return %c0 : i1 - } -} - -// ----- -// This test checks that measurements can be lifted over local phase gates (Z, etc.) without further classical operations. - -module { - // CHECK-LABEL: func.func @testLiftOverZ - func.func @testLiftOverZ() -> i1 { - // CHECK: %[[Q0_1:.*]], %[[C0:.*]] = mqtopt.measure %[[ANY:.*]] - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK-NOT: mqtopt.z - // CHECK: return %[[C0]] - - %q0_0 = mqtopt.allocQubit - %angle = arith.constant 3.000000e-01 : f64 - - %q0_1 = mqtopt.i() %q0_0 : !mqtopt.Qubit - %q0_2 = mqtopt.z() %q0_1 : !mqtopt.Qubit - %q0_3 = mqtopt.s() %q0_2 : !mqtopt.Qubit - %q0_4 = mqtopt.sdg() %q0_3 : !mqtopt.Qubit - %q0_5 = mqtopt.t() %q0_4 : !mqtopt.Qubit - %q0_6 = mqtopt.tdg() %q0_5 : !mqtopt.Qubit - %q0_7 = mqtopt.p(%angle) %q0_6 : !mqtopt.Qubit - %q0_8 = mqtopt.rz(%angle) %q0_7 : !mqtopt.Qubit - - %q0_9, %c0 = mqtopt.measure %q0_8 - - mqtopt.deallocQubit %q0_9 - return %c0 : i1 - } -} - -// ----- -// This test checks that measurements can be lifted over multiple X Gates by adding classical negations afterwards. - -module { - // CHECK-LABEL: func.func @testLiftOverMultipleX - func.func @testLiftOverMultipleX() -> i1 { - // CHECK: %[[Q0_1:.*]], %[[C0_0:.*]] = mqtopt.measure %[[ANY:.*]] - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK-NOT: mqtopt.x - // CHECK: return %[[C0_0]] - - %q0_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.x() %q0_0 : !mqtopt.Qubit - %q0_2 = mqtopt.x() %q0_1 : !mqtopt.Qubit - - %q0_3, %c0 = mqtopt.measure %q0_2 - - mqtopt.deallocQubit %q0_3 - return %c0 : i1 - } -} - -// ----- -// This test checks that measurements can be lifted over X Gates as well as controlled gates where the measurement target is a control at the same time. - -module { - // CHECK-LABEL: func.func @testLiftOverXAndControlledGates - func.func @testLiftOverXAndControlledGates() -> i1 { - // CHECK: %[[Q0_1:.*]], %[[C0_0:.*]] = mqtopt.measure %[[ANY:.*]] - // CHECK: %[[Q1_1:.*]] = scf.if %[[C0_0]] -> (!mqtopt.Qubit) { - // CHECK-NEXT: %[[Q1_1IF:.*]] = mqtopt.y() %[[ANY:.*]] : !mqtopt.Qubit - // CHECK-NEXT: scf.yield %[[Q1_1IF]] - // CHECK-NEXT: } else { - // CHECK-NEXT: scf.yield %[[ANY:.*]] - // CHECK-NEXT: } - // CHECK: %[[Q1_2:.*]] = scf.if %[[C0_0]] -> (!mqtopt.Qubit) { - // CHECK-NEXT: scf.yield %[[Q1_1]] - // CHECK-NEXT: } else { - // CHECK-NEXT: %[[Q1_2IF:.*]] = mqtopt.y() %[[Q1_1]] : !mqtopt.Qubit - // CHECK-NEXT: scf.yield %[[Q1_2IF]] - // CHECK-NEXT: } - // CHECK-NOT: mqtopt.x - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_2]] - // CHECK: return %[[C0_0]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - - %q1_1, %q0_1 = mqtopt.y() %q1_0 ctrl %q0_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_2 = mqtopt.x() %q0_1 : !mqtopt.Qubit - %q1_2, %q0_3 = mqtopt.y() %q1_1 ctrl %q0_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_4 = mqtopt.x() %q0_3 : !mqtopt.Qubit - - %q0_5, %c0 = mqtopt.measure %q0_4 - - mqtopt.deallocQubit %q0_5 - mqtopt.deallocQubit %q1_2 - return %c0 : i1 - } -} - -// ----- -// This test checks that measurements can be lifted over -// the targets of controlled gates if target and control are interchangeable. - -module { - // CHECK-LABEL: func.func @testLiftOverTargetAsControlInDiagonalGate - func.func @testLiftOverTargetAsControlInDiagonalGate() -> i1 { - // CHECK: %[[q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q0_1:.*]], %[[c0:.*]] = mqtopt.measure %[[q0_0]] - // CHECK: %[[q1_1:.*]] = scf.if %[[c0]] -> (!mqtopt.Qubit) { - // CHECK: %[[q1_1_then:.*]] = mqtopt.z() %[[q1_0]] : !mqtopt.Qubit - // CHECK: scf.yield %[[q1_1_then]] : !mqtopt.Qubit - // CHECK: } else { - // CHECK: scf.yield %[[q1_0]] : !mqtopt.Qubit - // CHECK: } - // CHECK: mqtopt.deallocQubit %[[q1_1]] - // CHECK: mqtopt.deallocQubit %[[q0_1]] - // CHECK: return %[[c0]] : i1 - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.z() %q0_0 ctrl %q1_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_2, %c0 = mqtopt.measure %q0_1 - - mqtopt.deallocQubit %q1_1 - mqtopt.deallocQubit %q0_2 - return %c0 : i1 - } -} - -// ----- -// This test checks that measurements can be lifted over -// the targets of controlled gates if target and negative control are interchangeable. - -module { - // CHECK-LABEL: func.func @testLiftOverTargetAsNegativeControlInDiagonalGate - func.func @testLiftOverTargetAsNegativeControlInDiagonalGate() -> i1 { - // CHECK: %[[q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q0_1:.*]], %[[c0:.*]] = mqtopt.measure %[[q0_0]] - // CHECK: %[[q1_1:.*]] = mqtopt.x() %[[q1_0]] : !mqtopt.Qubit - // CHECK: %[[q1_2:.*]] = scf.if %[[c0]] -> (!mqtopt.Qubit) { - // CHECK: %[[q1_2_then:.*]] = mqtopt.z() %[[q1_1]] : !mqtopt.Qubit - // CHECK: scf.yield %[[q1_2_then]] : !mqtopt.Qubit - // CHECK: } else { - // CHECK: scf.yield %[[q1_1]] : !mqtopt.Qubit - // CHECK: } - // CHECK: mqtopt.deallocQubit %[[q1_2]] - // CHECK: mqtopt.deallocQubit %[[q0_1]] - // CHECK: return %[[c0]] : i1 - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.z() %q0_0 nctrl %q1_0 : !mqtopt.Qubit nctrl !mqtopt.Qubit - %q0_2, %c0 = mqtopt.measure %q0_1 - - mqtopt.deallocQubit %q1_1 - mqtopt.deallocQubit %q0_2 - return %c0 : i1 - } -} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/merge-rotation-gates.mlir b/mlir/test/Dialect/MQTOpt/Transforms/merge-rotation-gates.mlir deleted file mode 100644 index 666eb890d9..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/merge-rotation-gates.mlir +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --merge-rotation-gates | FileCheck %s - -// ----- -// This test checks that consecutive p gates are merged correctly. - -module { - // CHECK-LABEL: func.func @testMergeRxGates - func.func @testMergeRxGates() { - // CHECK: %[[Res_2:.*]] = arith.constant 2.000000e+00 : f64 - // CHECK: %[[ANY:.*]] = mqtopt.p(%[[Res_2]]) %[[ANY:.*]] : !mqtopt.Qubit - // CHECK-NOT: %[[ANY:.*]] = mqtopt.p(%[[ANY:.*]]) %[[ANY:.*]] : !mqtopt.Qubit - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - %q0_1 = mqtopt.p(%c_0) %q0_0 : !mqtopt.Qubit - %q0_2 = mqtopt.p(%c_0) %q0_1 : !mqtopt.Qubit - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that consecutive rx gates are merged correctly. - -module { - // CHECK-LABEL: func.func @testMergeRxGates - func.func @testMergeRxGates() { - // CHECK: %[[Res_3:.*]] = arith.constant 3.000000e+00 : f64 - // CHECK: %[[ANY:.*]] = mqtopt.rx(%[[Res_3]]) %[[ANY:.*]] : !mqtopt.Qubit - // CHECK-NOT: %[[ANY:.*]] = mqtopt.rx(%[[ANY:.*]]) %[[ANY:.*]] : !mqtopt.Qubit - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - %q0_1 = mqtopt.rx(%c_0) %q0_0 : !mqtopt.Qubit - %q0_2 = mqtopt.rx(%c_0) %q0_1 : !mqtopt.Qubit - %q0_3 = mqtopt.rx(%c_0) %q0_2 : !mqtopt.Qubit - - memref.store %q0_3, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that consecutive ry gates are merged correctly. - -module { - // CHECK-LABEL: func.func @testMergeRyGates - func.func @testMergeRyGates(%c_0 : f64, %c_1 : f64) { - // CHECK: %[[Res:.*]] = arith.addf %arg0, %arg1 : f64 - // CHECK: %[[ANY:.*]] = mqtopt.ry(%[[Res]]) %[[ANY:.*]] : !mqtopt.Qubit - // CHECK-NOT: %[[ANY:.*]] = mqtopt.ry(%[[ANY:.*]]) %[[ANY:.*]] : !mqtopt.Qubit - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.ry(%c_0) %q0_0 : !mqtopt.Qubit - %q0_2 = mqtopt.ry(%c_1) %q0_1 : !mqtopt.Qubit - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that consecutive rz gates are merged correctly. - -module { - // CHECK-LABEL: func.func @testMergeRzGates - func.func @testMergeRzGates() { - // CHECK: %[[Res_3:.*]] = arith.constant 3.000000e+00 : f64 - // CHECK: %[[ANY:.*]] = mqtopt.rz(%[[Res_3]]) %[[ANY:.*]] : !mqtopt.Qubit - // CHECK-NOT: %[[ANY:.*]] = mqtopt.rz(%[[ANY:.*]]) %[[ANY:.*]] : !mqtopt.Qubit - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - %c_1 = arith.constant 2.000000e+00 : f64 - %q0_1 = mqtopt.rz(%c_0) %q0_0 : !mqtopt.Qubit - %q0_2 = mqtopt.rz(%c_1) %q0_1 : !mqtopt.Qubit - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that incompatible single-qubit gates are not merged. -// The gates cannot be merged because their types are different. - -module { - // CHECK-LABEL: func.func @testDoNotMergeSingleQubitGatesDifferentGates - func.func @testDoNotMergeSingleQubitGatesDifferentGates() { - // CHECK: %[[Res_2:.*]] = arith.constant 2.000000e+00 : f64 - // CHECK: %[[Res_1:.*]] = arith.constant 1.000000e+00 : f64 - // CHECK: %[[Q0_1:.*]] = mqtopt.rx(%[[Res_1]]) %[[ANY:.*]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]] = mqtopt.ry(%[[Res_1]]) %[[Q0_1]] : !mqtopt.Qubit - // CHECK: %[[ANY:.*]] = mqtopt.rz(%[[Res_2]]) %[[Q0_2]] : !mqtopt.Qubit - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - %c_1 = arith.constant 2.000000e+00 : f64 - %q0_1 = mqtopt.rx(%c_0) %q0_0 : !mqtopt.Qubit - %q0_2 = mqtopt.ry(%c_0) %q0_1 : !mqtopt.Qubit - %q0_3 = mqtopt.rz(%c_1) %q0_2 : !mqtopt.Qubit - - memref.store %q0_3, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that incompatible single-qubit gates are not merged. -// The gates cannot be merged because they act on different qubits. - -module { - // CHECK-LABEL: func.func @testDoNotMergeSingleQubitGatesIndependentGates - func.func @testDoNotMergeSingleQubitGatesIndependentGates() { - // CHECK: %[[Res_2:.*]] = arith.constant 2.000000e+00 : f64 - // CHECK: %[[Res_1:.*]] = arith.constant 1.000000e+00 : f64 - // CHECK: %[[ANY:.*]] = mqtopt.rx(%[[Res_1]]) %[[ANY:.*]] : !mqtopt.Qubit - // CHECK: %[[ANY:.*]] = mqtopt.rx(%[[Res_2]]) %[[ANY:.*]] : !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - %c_1 = arith.constant 2.000000e+00 : f64 - %q0_1 = mqtopt.rx(%c_0) %q0_0 : !mqtopt.Qubit - %q1_1 = mqtopt.rx(%c_1) %q1_0 : !mqtopt.Qubit - - memref.store %q0_1, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that consecutive rxx gates are merged correctly. - -module { - // CHECK-LABEL: func.func @testMergeRxxGates - func.func @testMergeRxxGates() { - // CHECK: %[[Res_3:.*]] = arith.constant 3.000000e+00 : f64 - // CHECK: %[[ANY:.*]]:2 = mqtopt.rxx(%[[Res_3]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK-NOT: %[[ANY:.*]]:2 = mqtopt.rxx(%[[ANY:.*]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - %q01_1:2 = mqtopt.rxx(%c_0) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_2:2 = mqtopt.rxx(%c_0) %q01_1#0, %q01_1#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_3:2 = mqtopt.rxx(%c_0) %q01_2#0, %q01_2#1 : !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q01_3#0, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q01_3#1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that consecutive ryy gates are merged correctly. - -module { - // CHECK-LABEL: func.func @testMergeRyyGates - func.func @testMergeRyyGates(%c_0 : f64, %c_1 : f64) { - // CHECK: %[[Res:.*]] = arith.addf %arg0, %arg1 : f64 - // CHECK: %[[ANY:.*]]:2 = mqtopt.ryy(%[[Res]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK-NOT: %[[ANY:.*]]:2 = mqtopt.ryy(%[[ANY:.*]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q01_1:2 = mqtopt.ryy(%c_0) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_2:2 = mqtopt.ryy(%c_1) %q01_1#0, %q01_1#1 : !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q01_2#0, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q01_2#1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that consecutive rzz gates are merged correctly. - -module { - // CHECK-LABEL: func.func @testMergeRzzGates - func.func @testMergeRzzGates() { - // CHECK: %[[Res_3:.*]] = arith.constant 3.000000e+00 : f64 - // CHECK: %[[ANY:.*]]:2 = mqtopt.rzz(%[[Res_3]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK-NOT: %[[ANY:.*]]:2 = mqtopt.rzz(%[[ANY:.*]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - %c_1 = arith.constant 2.000000e+00 : f64 - %q01_1:2 = mqtopt.rzz(%c_0) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_2:2 = mqtopt.rzz(%c_1) %q01_1#0, %q01_1#1 : !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q01_2#0, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q01_2#1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that consecutive rzx gates are merged correctly. - -module { - // CHECK-LABEL: func.func @testMergeRzxGates - func.func @testMergeRzxGates() { - // CHECK: %[[Res_3:.*]] = arith.constant 3.000000e+00 : f64 - // CHECK: %[[ANY:.*]]:2 = mqtopt.rzx(%[[Res_3]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK-NOT: %[[ANY:.*]]:2 = mqtopt.rzx(%[[ANY:.*]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - %c_1 = arith.constant 2.000000e+00 : f64 - %q01_1:2 = mqtopt.rzx(%c_0) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_2:2 = mqtopt.rzx(%c_1) %q01_1#0, %q01_1#1 : !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q01_2#0, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q01_2#1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that incompatible multi-qubit gates are not merged. -// The gates cannot be merged because their types are different. - -module { - // CHECK-LABEL: func.func @testDoNotMergeMultiQubitGatesDifferentGates - func.func @testDoNotMergeMultiQubitGatesDifferentGates() { - // CHECK: %[[Res_2:.*]] = arith.constant 2.000000e+00 : f64 - // CHECK: %[[Res_1:.*]] = arith.constant 1.000000e+00 : f64 - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.rxx(%[[Res_1]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q01_2:.*]]:2 = mqtopt.ryy(%[[Res_1]]) %[[Q01_1]]#0, %[[Q01_1]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[ANY:.*]]:2 = mqtopt.rzz(%[[Res_2]]) %[[Q01_2]]#0, %[[Q01_2]]#1 : !mqtopt.Qubit, !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - %c_1 = arith.constant 2.000000e+00 : f64 - %q01_1:2 = mqtopt.rxx(%c_0) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_2:2 = mqtopt.ryy(%c_0) %q01_1#0, %q01_1#1 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_3:2 = mqtopt.rzz(%c_1) %q01_2#0, %q01_2#1 : !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q01_3#0, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q01_3#1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that incompatible multi-qubit gates are not merged. -// The gates cannot be merged because their types are different. - -module { - // CHECK-LABEL: func.func @testDoNotMergeMultiQubitGatesIndependentGates - func.func @testDoNotMergeMultiQubitGatesIndependentGates() { - // CHECK: %[[Res_2:.*]] = arith.constant 2.000000e+00 : f64 - // CHECK: %[[Res_1:.*]] = arith.constant 1.000000e+00 : f64 - // CHECK: %[[Q0_1:.*]]:2 = mqtopt.rxx(%[[Res_1]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[ANY:.*]]:2 = mqtopt.rxx(%[[Res_2]]) %[[Q0_1:.*]]#1, %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<3x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<3x!mqtopt.Qubit> - %q2_0 = memref.load %qreg[%i2] : memref<3x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - %c_1 = arith.constant 2.000000e+00 : f64 - %q01_1:2 = mqtopt.rxx(%c_0) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q12_1:2 = mqtopt.rxx(%c_1) %q01_1#1, %q2_0 : !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q01_1#0, %qreg[%i0] : memref<3x!mqtopt.Qubit> - memref.store %q12_1#0, %qreg[%i1] : memref<3x!mqtopt.Qubit> - memref.store %q12_1#1, %qreg[%i2] : memref<3x!mqtopt.Qubit> - memref.dealloc %qreg : memref<3x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that incompatible multi-qubit gates are not merged. -// The gates cannot be merged because their input qubits do not have the same order. -// This test should fail when a canonicalization pass is implemented with #1031. - -module { - // CHECK-LABEL: func.func @testDoNotMergeMultiQubitGatesDifferentInputQubitOrder - func.func @testDoNotMergeMultiQubitGatesDifferentInputQubitOrder() { - // CHECK: %[[Res_2:.*]] = arith.constant 2.000000e+00 : f64 - // CHECK: %[[Res_1:.*]] = arith.constant 1.000000e+00 : f64 - // CHECK: %[[Q0_1:.*]]:2 = mqtopt.rxx(%[[Res_1]]) %[[ANY:.*]], %[[ANY:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[ANY:.*]]:2 = mqtopt.rxx(%[[Res_2]]) %[[Q0_1]]#1, %[[Q0_1]]#0 : !mqtopt.Qubit, !mqtopt.Qubit - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - %c_1 = arith.constant 2.000000e+00 : f64 - %q01_1:2 = mqtopt.rxx(%c_0) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q01_2:2 = mqtopt.rxx(%c_1) %q01_1#1, %q01_1#0 : !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q01_2#0, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q01_2#1, %qreg[%i1] : memref<2x!mqtopt.Qubit> - memref.dealloc %qreg : memref<2x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that consecutive gphase gates with controls are merged correctly. - -module { - // CHECK-LABEL: func.func @testMergeGphaseWithControls - func.func @testMergeGphaseWithControls() { - // CHECK: %[[Res_3:.*]] = arith.constant 3.000000e+00 : f64 - // CHECK: %[[ANY:.*]] = mqtopt.gphase(%[[Res_3]]) ctrl %[[ANY:.*]] : ctrl !mqtopt.Qubit - // CHECK-NOT: %[[ANY:.*]] = mqtopt.gphase(%[[ANY:.*]]) %[[ANY:.*]] : !mqtopt.Qubit - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - %q0_1 = mqtopt.gphase(%c_0) ctrl %q0_0 : ctrl !mqtopt.Qubit - %q0_2 = mqtopt.gphase(%c_0) ctrl %q0_1 : ctrl !mqtopt.Qubit - %q0_3 = mqtopt.gphase(%c_0) ctrl %q0_2 : ctrl !mqtopt.Qubit - - memref.store %q0_3, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that consecutive gphase gates without controls are not merged. -// The current implementation does not support merging gates without users. - -module { - // CHECK-LABEL: func.func @testDoNotMergeGphaseWithoutControls - func.func @testDoNotMergeGphaseWithoutControls() { - // CHECK: %[[Res_1:.*]] = arith.constant 1.000000e+00 : f64 - // CHECK: mqtopt.gphase(%[[Res_1]]) - // CHECK: mqtopt.gphase(%[[Res_1]]) - // CHECK: mqtopt.gphase(%[[Res_1]]) - // CHECK-NOT: mqtopt.gphase(%[[ANY:.*]]) - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - mqtopt.gphase(%c_0) : () - mqtopt.gphase(%c_0) : () - mqtopt.gphase(%c_0) : () - - memref.store %q0_0, %qreg[%i0] : memref<1x!mqtopt.Qubit> - memref.dealloc %qreg : memref<1x!mqtopt.Qubit> - - return - } -} - -// ----- -// This test checks that controlled rotation gates that have different pos/neg ctrl distributions are not merged. - -module { - // CHECK-LABEL: func.func @testDoNotMergeMultiQubitGatesDifferentControlSizes - func.func @testDoNotMergeMultiQubitGatesDifferentControlSizes() { - // CHECK: %[[Res_2:.*]] = arith.constant 2.000000e+00 : f64 - // CHECK: %[[Res_1:.*]] = arith.constant 1.000000e+00 : f64 - // CHECK: %[[Q0_1:.*]]:2, %[[Q2_1:.*]] = mqtopt.gphase(%[[Res_1]]) ctrl %[[ANY:.*]], %[[ANY:.*]] nctrl %[[ANY:.*]]: ctrl !mqtopt.Qubit, !mqtopt.Qubit nctrl !mqtopt.Qubit - // CHECK: %[[ANY:.*]], %[[ANY:.*]]:2 = mqtopt.gphase(%[[Res_2]]) ctrl %[[Q0_1]]#0 nctrl %[[Q0_1]]#1, %[[Q2_1]] : ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit, !mqtopt.Qubit - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<3x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<3x!mqtopt.Qubit> - %q2_0 = memref.load %qreg[%i2] : memref<3x!mqtopt.Qubit> - - %c_0 = arith.constant 1.000000e+00 : f64 - %c_1 = arith.constant 2.000000e+00 : f64 - %q012_1:3 = mqtopt.gphase(%c_0) ctrl %q0_0, %q1_0 nctrl %q2_0 : ctrl !mqtopt.Qubit, !mqtopt.Qubit nctrl !mqtopt.Qubit - %q012_2:3 = mqtopt.gphase(%c_1) ctrl %q012_1#0 nctrl %q012_1#1, %q012_1#2 : ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit, !mqtopt.Qubit - - memref.store %q012_2#0, %qreg[%i0] : memref<3x!mqtopt.Qubit> - memref.store %q012_2#1, %qreg[%i1] : memref<3x!mqtopt.Qubit> - memref.store %q012_2#2, %qreg[%i2] : memref<3x!mqtopt.Qubit> - memref.dealloc %qreg : memref<3x!mqtopt.Qubit> - - return - } -} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/reuse-qubits-with-measurement-lifting.mlir b/mlir/test/Dialect/MQTOpt/Transforms/reuse-qubits-with-measurement-lifting.mlir deleted file mode 100644 index 51f276e220..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/reuse-qubits-with-measurement-lifting.mlir +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --lift-measurements --reuse-qubits | FileCheck %s - -// ----- -// This test checks that qubit reuse and measurement lifting are compatible in a simple case, where measurements are -// lifted above a single-qubit gate. - -module { - // CHECK-LABEL: func.func @testSingleQubitLift - func.func @testSingleQubitLift() -> (i1, i1) { - // CHECK: %[[true:.*]] = arith.constant true - // CHECK: %[[q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q0_1:.*]], %[[c0_0:.*]] = mqtopt.measure %[[q0_0]] - // CHECK: %[[c0_1:.*]] = arith.xori %[[c0_0]], %[[true]] : i1 - // CHECK: %[[q1_0:.*]] = mqtopt.reset %[[q0_1]] - // CHECK: %[[q1_1:.*]] = mqtopt.h() %[[q1_0]] : !mqtopt.Qubit - // CHECK: %[[q1_2:.*]], %[[c1:.*]] = mqtopt.measure %[[q1_1]] - // CHECK: mqtopt.deallocQubit %[[q1_2]] - // CHECK: return %[[c0_1]], %[[c1]] : i1, i1 - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.x() %q0_0 : !mqtopt.Qubit - %q1_1 = mqtopt.h() %q1_0 : !mqtopt.Qubit - - %q0_2, %c0 = mqtopt.measure %q0_1 - %q1_2, %c1 = mqtopt.measure %q1_1 - - mqtopt.deallocQubit %q0_2 - mqtopt.deallocQubit %q1_2 - - return %c0, %c1 : i1, i1 - } -} - -// ----- -// This test checks that qubit reuse can be applied after lifting measurements over controlled gates -// in a case where the measurement lifting does not influence the applicability of qubit reuse. - -module { - // CHECK-LABEL: func.func @testReuseAfterUnneededControlLift - func.func @testReuseAfterUnneededControlLift() -> (i1, i1, i1) { - // CHECK: %[[q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q0_1:.*]] = mqtopt.h() %[[q0_0]] : !mqtopt.Qubit - // CHECK: %[[q1_1:.*]], %[[c1:.*]] = mqtopt.measure %[[q1_0]] - // CHECK: %[[q0_2:.*]] = scf.if %[[c1]] -> (!mqtopt.Qubit) { - // CHECK: %[[q0_2_if:.*]] = mqtopt.h() %[[q0_1]] : !mqtopt.Qubit - // CHECK: scf.yield %[[q0_2_if]] : !mqtopt.Qubit - // CHECK: } else { - // CHECK: scf.yield %[[q0_1]] : !mqtopt.Qubit - // CHECK: } - // CHECK: %[[q2_0:.*]] = mqtopt.reset %[[q1_1]] - // CHECK: %[[q2_1:.*]], %[[q0_3:.*]] = mqtopt.h() %[[q2_0]] ctrl %[[q0_2]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[q0_4:.*]] = mqtopt.h() %[[q0_3]] : !mqtopt.Qubit - // CHECK: %[[q0_5:.*]], %[[c0:.*]] = mqtopt.measure %[[q0_4]] - // CHECK: mqtopt.deallocQubit %[[q0_5]] - // CHECK: %[[q2_2:.*]], %[[c2:.*]] = mqtopt.measure %[[q2_1]] - // CHECK: mqtopt.deallocQubit %[[q2_2]] - // CHECK: return %[[c0]], %[[c1]], %[[c2]] : i1, i1, i1 - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q0_2, %q1_1 = mqtopt.h() %q0_1 ctrl %q1_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q2_1, %q0_3 = mqtopt.h() %q2_0 ctrl %q0_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_4 = mqtopt.h() %q0_3 : !mqtopt.Qubit - - %q0_5, %c0 = mqtopt.measure %q0_4 - %q1_2, %c1 = mqtopt.measure %q1_1 - %q2_2, %c2 = mqtopt.measure %q2_1 - - mqtopt.deallocQubit %q0_5 - mqtopt.deallocQubit %q1_2 - mqtopt.deallocQubit %q2_2 - return %c0, %c1, %c2 : i1, i1, i1 - } -} - -// ----- -// This test checks that qubit reuse can be applied with the help of measurement lifting -// over a control. - -module { - // CHECK-LABEL: func.func @testReuseThroughMeasurementLift - func.func @testReuseThroughMeasurementLift() -> (i1, i1) { - // CHECK: %[[q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q0_1:.*]] = mqtopt.h() %[[q0_0]] : !mqtopt.Qubit - // CHECK: %[[q0_2:.*]], %[[c0:.*]] = mqtopt.measure %[[q0_1]] - // CHECK: %[[q1_0:.*]] = mqtopt.reset %[[q0_2]] - // CHECK: %[[q1_1:.*]] = scf.if %[[c0]] -> (!mqtopt.Qubit) { - // CHECK: %[[q1_1_then:.*]] = mqtopt.x() %[[q1_0]] : !mqtopt.Qubit - // CHECK: scf.yield %[[q1_1_then]] : !mqtopt.Qubit - // CHECK: } else { - // CHECK: scf.yield %[[q1_0]] : !mqtopt.Qubit - // CHECK: } - // CHECK: %[[q1_2:.*]], %[[c1:.*]] = mqtopt.measure %[[q1_1]] - // CHECK: mqtopt.deallocQubit %[[q1_2]] - // CHECK: return %[[c0]], %[[c1]] : i1, i1 - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.x() %q1_0 ctrl %q0_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_3, %c0 = mqtopt.measure %q0_2 - %q1_2, %c1 = mqtopt.measure %q1_1 - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_2 - return %c0, %c1 : i1, i1 - } -} - -// ----- -// This test checks that qubit reuse can be applied with the help of measurement lifting -// over a control and over a single-qubit gate. - -module { - // CHECK-LABEL: func.func @testReuseThroughComplexMeasurementLift - func.func @testReuseThroughComplexMeasurementLift() -> (i1, i1) { - // CHECK: %[[q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q0_1:.*]] = mqtopt.h() %[[q0_0]] : !mqtopt.Qubit - // CHECK: %[[q0_2:.*]], %[[c0:.*]] = mqtopt.measure %[[q0_1]] - // CHECK: %[[c0_1:.*]] = arith.xori %[[c0]], %[[true:.*]] : i1 - // CHECK: %[[q1_0:.*]] = mqtopt.reset %[[q0_2]] - // CHECK: %[[q1_1:.*]] = scf.if %[[c0]] -> (!mqtopt.Qubit) { - // CHECK: %[[q1_1_then:.*]] = mqtopt.x() %[[q1_0]] : !mqtopt.Qubit - // CHECK: scf.yield %[[q1_1_then]] : !mqtopt.Qubit - // CHECK: } else { - // CHECK: scf.yield %[[q1_0]] : !mqtopt.Qubit - // CHECK: } - // CHECK: %[[q1_2:.*]], %[[c1:.*]] = mqtopt.measure %[[q1_1]] - // CHECK: mqtopt.deallocQubit %[[q1_2]] - // CHECK: return %[[c0_1]], %[[c1]] : i1, i1 - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.x() %q1_0 ctrl %q0_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3 = mqtopt.x() %q0_2 : !mqtopt.Qubit - - %q0_4, %c0 = mqtopt.measure %q0_3 - %q1_2, %c1 = mqtopt.measure %q1_1 - - mqtopt.deallocQubit %q0_4 - mqtopt.deallocQubit %q1_2 - return %c0, %c1 : i1, i1 - } -} - -// ----- -// This test checks that qubit reuse can be applied after lifting measurements over controlled gates if the -// qubit for which reuse should be applied was pulled into an if/else block. - -module { - // CHECK-LABEL: func.func @testReuseOutOfIf - func.func @testReuseOutOfIf() -> (i1, i1, i1) { - // CHECK: %[[q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q1_1:.*]], %[[c1:.*]] = mqtopt.measure %[[q1_0]] - // CHECK: %[[q0_0:.*]] = mqtopt.reset %[[q1_1]] - // CHECK: %[[q0_1:.*]] = mqtopt.h() %[[q0_0]] : !mqtopt.Qubit - // CHECK: %[[q0_2:.*]] = scf.if %[[c1]] -> (!mqtopt.Qubit) { - // CHECK: %[[q0_2_if:.*]] = mqtopt.h() %[[q0_1]] : !mqtopt.Qubit - // CHECK: scf.yield %[[q0_2_if]] : !mqtopt.Qubit - // CHECK: } else { - // CHECK: scf.yield %[[q0_1]] : !mqtopt.Qubit - // CHECK: } - // CHECK: %[[q0_3:.*]], %[[c0:.*]] = mqtopt.measure %[[q0_2]] - // CHECK: %[[q2_0:.*]] = mqtopt.reset %[[q0_3]] - // CHECK: %[[q2_1:.*]] = scf.if %[[c0]] -> (!mqtopt.Qubit) { - // CHECK: %[[q2_1_if:.*]] = mqtopt.h() %[[q2_0]] : !mqtopt.Qubit - // CHECK: scf.yield %[[q2_1_if]] : !mqtopt.Qubit - // CHECK: } else { - // CHECK: scf.yield %[[q2_0]] : !mqtopt.Qubit - // CHECK: } - // CHECK: %[[q2_2:.*]], %[[c2:.*]] = mqtopt.measure %[[q2_1]] - // CHECK: mqtopt.deallocQubit %[[q2_2]] - // CHECK: return %[[c0]], %[[c1]], %[[c2]] : i1, i1, i1 - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - %q0_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q0_2, %q1_1 = mqtopt.h() %q0_1 ctrl %q1_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q2_1, %q0_3 = mqtopt.h() %q2_0 ctrl %q0_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_4, %c0 = mqtopt.measure %q0_3 - %q1_2, %c1 = mqtopt.measure %q1_1 - %q2_2, %c2 = mqtopt.measure %q2_1 - - mqtopt.deallocQubit %q0_4 - mqtopt.deallocQubit %q1_2 - mqtopt.deallocQubit %q2_2 - return %c0, %c1, %c2 : i1, i1, i1 - } -} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/reuse-qubits.mlir b/mlir/test/Dialect/MQTOpt/Transforms/reuse-qubits.mlir deleted file mode 100644 index 15095c1c78..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/reuse-qubits.mlir +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --reuse-qubits | FileCheck %s - -// ----- -// This test checks that qubits are reused if they are disjointly used. - -module { - // CHECK-LABEL: func.func @testSimple - func.func @testSimple() -> (i1, i1) { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q0_1:.*]] = mqtopt.h() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: %[[Q0_3:.*]] = mqtopt.reset %[[Q0_2]] - // CHECK: %[[Q0_4:.*]] = mqtopt.h() %[[Q0_3]] : !mqtopt.Qubit - // CHECK: %[[Q0_5:.*]], %[[C1:.*]] = mqtopt.measure %[[Q0_4]] - // CHECK: mqtopt.deallocQubit %[[Q0_5]] - // CHECK: return %[[C0]], %[[C1]] : i1, i1 - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q1_1 = mqtopt.h() %q1_0 : !mqtopt.Qubit - - %q0_2, %c0 = mqtopt.measure %q0_1 - %q1_2, %c1 = mqtopt.measure %q1_1 - - mqtopt.deallocQubit %q0_2 - mqtopt.deallocQubit %q1_2 - return %c0, %c1 : i1, i1 - } -} - -// ----- -// This test checks that qubit reuse is not applied if the used qubits intersect. - -module { - // CHECK-LABEL: func.func @testNoReuse - func.func @testNoReuse() -> (i1, i1) { - // CHECK: %[[q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q0_1:.*]] = mqtopt.h() %[[q0_0]] : !mqtopt.Qubit - // CHECK: %[[q1_1:.*]], %[[q0_2:.*]] = mqtopt.h() %[[q1_0]] ctrl %[[q0_1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[q0_3:.*]], %[[c0:.*]] = mqtopt.measure %[[q0_2]] - // CHECK: %[[q1_2:.*]], %[[c1:.*]] = mqtopt.measure %[[q1_1]] - // CHECK: mqtopt.deallocQubit %[[q0_3]] - // CHECK: mqtopt.deallocQubit %[[q1_2]] - // CHECK: return %[[c0]], %[[c1]] : i1, i1 - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.h() %q1_0 ctrl %q0_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_3, %c0 = mqtopt.measure %q0_2 - %q1_2, %c1 = mqtopt.measure %q1_1 - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_2 - return %c0, %c1 : i1, i1 - } -} - -// ----- -// This test checks that qubit reuse is applied correctly in a context with several qubits. - -module { - // CHECK-LABEL: func.func @testReuseOne - func.func @testReuseOne() -> (i1, i1, i1) { - // CHECK: %[[q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q0_1:.*]] = mqtopt.h() %[[q0_0]] : !mqtopt.Qubit - // CHECK: %[[q1_1:.*]], %[[q0_2:.*]] = mqtopt.h() %[[q1_0]] ctrl %[[q0_1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[q0_3:.*]], %[[c0:.*]] = mqtopt.measure %[[q0_2]] - // CHECK: %[[q1_2:.*]], %[[c1:.*]] = mqtopt.measure %[[q1_1]] - // CHECK: mqtopt.deallocQubit %[[q0_3]] - // CHECK: %[[q2_0:.*]] = mqtopt.reset %[[q1_2]] - // CHECK: %[[q2_1:.*]] = mqtopt.h() %[[q2_0]] : !mqtopt.Qubit - // CHECK: %[[q2_2:.*]], %[[c2:.*]] = mqtopt.measure %[[q2_1]] - // CHECK: mqtopt.deallocQubit %[[q2_2]] - // CHECK: return %[[c0]], %[[c1]], %[[c2]] : i1, i1, i1 - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.h() %q1_0 ctrl %q0_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q2_1 = mqtopt.h() %q2_0 : !mqtopt.Qubit - - %q0_3, %c0 = mqtopt.measure %q0_2 - %q1_2, %c1 = mqtopt.measure %q1_1 - %q2_2, %c2 = mqtopt.measure %q2_1 - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_2 - mqtopt.deallocQubit %q2_2 - return %c0, %c1, %c2 : i1, i1, i1 - } -} - -// ----- -// This test checks that qubit reuse is even applied correctly if the reused qubit appears after the alloc but could be moved above it. - -module { - // CHECK-LABEL: func.func @testReuseOneWithBadOrdering - func.func @testReuseOneWithBadOrdering() -> (i1, i1, i1) { - // CHECK: %[[q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q0_1:.*]] = mqtopt.h() %[[q0_0]] : !mqtopt.Qubit - // CHECK: %[[q1_1:.*]], %[[q0_2:.*]] = mqtopt.h() %[[q1_0]] ctrl %[[q0_1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[q0_3:.*]], %[[c0:.*]] = mqtopt.measure %[[q0_2]] - // CHECK: %[[q1_2:.*]], %[[c1:.*]] = mqtopt.measure %[[q1_1]] - // CHECK: mqtopt.deallocQubit %[[q0_3]] - // CHECK: %[[q2_0:.*]] = mqtopt.reset %[[q1_2]] - // CHECK: %[[q2_1:.*]] = mqtopt.h() %[[q2_0]] : !mqtopt.Qubit - // CHECK: %[[q2_2:.*]], %[[c2:.*]] = mqtopt.measure %[[q2_1]] - // CHECK: mqtopt.deallocQubit %[[q2_2]] - // CHECK: return %[[c0]], %[[c1]], %[[c2]] : i1, i1, i1 - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q2_1 = mqtopt.h() %q2_0 : !mqtopt.Qubit - - %q2_2, %c2 = mqtopt.measure %q2_1 - - mqtopt.deallocQubit %q2_2 - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.h() %q1_0 ctrl %q0_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %c0 = mqtopt.measure %q0_2 - %q1_2, %c1 = mqtopt.measure %q1_1 - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_2 - return %c0, %c1, %c2 : i1, i1, i1 - } -} - -// ----- -// This test checks that qubit reuse can be applied multiple times. - -module { - // CHECK-LABEL: func.func @testReuseMultiple - func.func @testReuseMultiple() -> (i1, i1, i1) { - // CHECK: %[[q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q0_1:.*]] = mqtopt.h() %[[q0_0]] : !mqtopt.Qubit - // CHECK: %[[q0_2:.*]], %[[c0:.*]] = mqtopt.measure %[[q0_1]] - // q2 is reused first because we start from the last dealloc. - // CHECK: %[[q2_0:.*]] = mqtopt.reset %[[q0_2]] - // CHECK: %[[q2_1:.*]] = mqtopt.h() %[[q2_0]] : !mqtopt.Qubit - // CHECK: %[[q2_2:.*]], %[[c1:.*]] = mqtopt.measure %[[q2_1]] - // CHECK: %[[q1_0:.*]] = mqtopt.reset %[[q2_2]] - // CHECK: %[[q1_1:.*]] = mqtopt.h() %[[q1_0]] : !mqtopt.Qubit - // CHECK: %[[q1_2:.*]], %[[c2:.*]] = mqtopt.measure %[[q1_1]] - // CHECK: mqtopt.deallocQubit %[[q1_2]] - // CHECK: return %[[c0]], %[[c1]], %[[c2]] : i1, i1, i1 - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q1_1 = mqtopt.h() %q1_0 : !mqtopt.Qubit - %q2_1 = mqtopt.h() %q2_0 : !mqtopt.Qubit - - %q0_2, %c0 = mqtopt.measure %q0_1 - %q1_2, %c1 = mqtopt.measure %q1_1 - %q2_2, %c2 = mqtopt.measure %q2_1 - - mqtopt.deallocQubit %q0_2 - mqtopt.deallocQubit %q1_2 - mqtopt.deallocQubit %q2_2 - - return %c0, %c1, %c2 : i1, i1, i1 - } -} - -// ----- -// This test checks that qubit reuse can be applied even if a path exists between the qubits in the interaction graph - -module { - // CHECK-LABEL: func.func @testReuseWhenPathExists - func.func @testReuseWhenPathExists() -> (i1, i1, i1) { - // CHECK: %[[q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[q0_1:.*]] = mqtopt.h() %[[q0_0]] : !mqtopt.Qubit - // CHECK: %[[q1_1:.*]], %[[q0_2:.*]] = mqtopt.h() %[[q1_0]] ctrl %[[q0_1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[q1_2:.*]], %[[c1:.*]] = mqtopt.measure %[[q1_1]] - // CHECK: %[[q2_0:.*]] = mqtopt.reset %[[q1_2]] - // CHECK: %[[q2_1:.*]], %[[q0_3:.*]] = mqtopt.h() %[[q2_0]] ctrl %[[q0_2]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[q0_4:.*]], %[[c0:.*]] = mqtopt.measure %[[q0_3]] - // CHECK: mqtopt.deallocQubit %[[q0_4]] - // CHECK: %[[q2_2:.*]], %[[c2:.*]] = mqtopt.measure %[[q2_1]] - // CHECK: mqtopt.deallocQubit %[[q2_2]] - // CHECK: return %[[c0]], %[[c1]], %[[c2]] : i1, i1, i1 - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - %q1_1, %q0_2 = mqtopt.h() %q1_0 ctrl %q0_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %q2_1, %q0_3 = mqtopt.h() %q2_0 ctrl %q0_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q0_4, %c0 = mqtopt.measure %q0_3 - %q1_2, %c1 = mqtopt.measure %q1_1 - %q2_2, %c2 = mqtopt.measure %q2_1 - - mqtopt.deallocQubit %q0_4 - mqtopt.deallocQubit %q1_2 - mqtopt.deallocQubit %q2_2 - return %c0, %c1, %c2 : i1, i1, i1 - } -} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/swap-reconstruction-and-elision.mlir b/mlir/test/Dialect/MQTOpt/Transforms/swap-reconstruction-and-elision.mlir deleted file mode 100644 index 8b1d874ce8..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/swap-reconstruction-and-elision.mlir +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --swap-reconstruction-and-elision | FileCheck %s - -// ----- -// This test checks that a single SWAP gate is removed correctly. - -module { - // CHECK-LABEL: func.func @testSingleElidePermutation - func.func @testSingleElidePermutation() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // ========================== Check for operations that should be canceled ============================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.swap() - - // ========================== Check for operations that should not be canceled ============================== - // CHECK: %[[Q0_1:.*]] = mqtopt.x() %[[Q0_0]] - - // CHECK: mqtopt.deallocQubit %[[Q1_0]] - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.swap() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q1_2 = mqtopt.x() %q1_1 : !mqtopt.Qubit - - mqtopt.deallocQubit %q0_1 - mqtopt.deallocQubit %q1_2 - - return - } -} - - -// ----- -// This test checks that a controlled SWAP gate is not removed. - -module { - // CHECK-LABEL: func.func @testControlledSwapNoElidePermutation - func.func @testControlledSwapNoElidePermutation() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - - // ========================== Check for operations that should not be canceled ============================== - // CHECK: %[[Q01_1:.*]]:2, %[[Q2_1:.*]] = mqtopt.swap() %[[Q0_0]], %[[Q1_0]] ctrl %[[Q2_0]] - - // CHECK: mqtopt.deallocQubit %[[Q01_1]]#0 - // CHECK: mqtopt.deallocQubit %[[Q01_1]]#1 - // CHECK: mqtopt.deallocQubit %[[Q2_1]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q0_1, %q1_1, %q2_1 = mqtopt.swap() %q0_0, %q1_0 ctrl %q2_0 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_1 - mqtopt.deallocQubit %q1_1 - mqtopt.deallocQubit %q2_1 - - return - } -} - - -// ----- -// This test checks that all removable SWAP gates are removed. - -module { - // CHECK-LABEL: func.func @testMultiSwapElidePermutation - func.func @testMultiSwapElidePermutation() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - - // ========================== Check for operations that should not be canceled ============================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.swap() - - // ========================== Check for operations that should not be canceled ============================== - // CHECK: %[[Q12_1:.*]]:2, %[[Q0_1:.*]] = mqtopt.swap() %[[Q1_0]], %[[Q2_0]] ctrl %[[Q0_0]] - - // CHECK: mqtopt.deallocQubit %[[Q12_1]]#0 - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q12_1]]#1 - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.swap() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q1_2, %q2_1 = mqtopt.swap() %q1_1, %q2_0 : !mqtopt.Qubit, !mqtopt.Qubit - %q0_2, %q1_3, %q2_2 = mqtopt.swap() %q0_1, %q1_2 ctrl %q2_1 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_4, %q2_3 = mqtopt.swap() %q1_3, %q2_2 : !mqtopt.Qubit, !mqtopt.Qubit - - mqtopt.deallocQubit %q0_2 - mqtopt.deallocQubit %q1_4 - mqtopt.deallocQubit %q2_3 - - return - } -} - - -// ----- -// This test checks that two consecutive CNOT gates with swapped target and control are merged and elided correctly. - -module { - // CHECK-LABEL: func.func @testSingleTwoCNotSwapReconstructionAndElision - func.func @testSingleTwoCNotSwapReconstructionAndElision() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // ========================== Check for operations that should be inserted ============================== - // CHECK: %[[Q1_1:.*]], %[[Q0_1:.*]] = mqtopt.x() %[[Q1_0]] ctrl %[[Q0_0]] - - // ========================== Check for operations that should be canceled ============================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.x() - - // CHECK: mqtopt.deallocQubit %[[Q1_1]] - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_2, %q0_2 = mqtopt.x() %q1_1 ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_2 - mqtopt.deallocQubit %q1_2 - - return - } -} - - -// ----- -// This test checks that consecutive CNOT gates which match a SWAP gate are merged correctly. - -module { - // CHECK-LABEL: func.func @testSingleThreeCNotSwapReconstructionAndElision - func.func @testSingleThreeCNotSwapReconstructionAndElision() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // ========================== Check for operations that should be canceled ============================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.x() - - // CHECK: mqtopt.deallocQubit %[[Q1_0]] - // CHECK: mqtopt.deallocQubit %[[Q0_0]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_2, %q0_2 = mqtopt.x() %q1_1 ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %q1_3 = mqtopt.x() %q0_2 ctrl %q1_2: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_3 - - return - } -} - - -// ----- -// This test checks that recursive application is possible for performing a combination of two and three -// CNOT swap reconstructions and elisions. - -module { - // CHECK-LABEL: func.func @testFirstTwoThenThreeCNotSwapReconstructionAndElision - func.func @testFirstTwoThenThreeCNotSwapReconstructionAndElision() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // ========================== Check for operations that should be canceled ============================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.x() - - // CHECK: mqtopt.deallocQubit %[[Q0_0]] - // CHECK: mqtopt.deallocQubit %[[Q1_0]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_2, %q0_2 = mqtopt.x() %q1_1 ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q1_3, %q0_3 = mqtopt.x() %q1_2 ctrl %q0_2: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_4, %q1_4 = mqtopt.x() %q0_3 ctrl %q1_3: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_4 - mqtopt.deallocQubit %q1_4 - - return - } -} - - -// ----- -// This test checks that recursive application is possible for performing CNOT swap reconstruction and -// elision twice. - -module { - // CHECK-LABEL: func.func @testFirstTwoThenTwoCNotAgainSwapReconstructionAndElision - func.func @testFirstTwoThenTwoCNotAgainSwapReconstructionAndElision() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // ========================== Check for operations that should be inserted ============================== - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] - - // ========================== Check for operations that should be canceled ============================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.x() - - // CHECK: mqtopt.deallocQubit %[[Q0_1]] - // CHECK: mqtopt.deallocQubit %[[Q1_1]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_2, %q0_2 = mqtopt.x() %q1_1 ctrl %q0_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_3, %q0_3 = mqtopt.x() %q1_2 ctrl %q0_2: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_3 - - return - } -} - - -// ----- -// This test checks that consecutive CNOT gates with more than one control qubit are not merged. - -module { - // CHECK-LABEL: func.func @testTooManyControlsNoSwapReconstruction - func.func @testTooManyControlsNoSwapReconstruction() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - - // ========================== Check for operations that should not be canceled =========================== - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] - // CHECK: %[[Q1_2:.*]], %[[Q02_2:.*]]:2 = mqtopt.x() %[[Q1_1]] ctrl %[[Q0_1]], %[[Q2_0]] - - // ========================== Check for operations that should not be inserted =========================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.swap() - - // CHECK: mqtopt.deallocQubit %[[Q02_2]]#0 - // CHECK: mqtopt.deallocQubit %[[Q1_2]] - // CHECK: mqtopt.deallocQubit %[[Q02_2]]#1 - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q1_2, %q0_2, %q2_1 = mqtopt.x() %q1_1 ctrl %q0_1, %q2_0: !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - - mqtopt.deallocQubit %q0_2 - mqtopt.deallocQubit %q1_2 - mqtopt.deallocQubit %q2_1 - - return - } -} - - -// ----- -// This test checks that consecutive CNOT gates with same target and control are not merged. - -module { - // CHECK-LABEL: func.func @testWrongPatternNoSwapReconstruction - func.func @testWrongPatternNoSwapReconstruction() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - - // ========================== Check for operations that should be canceled ============================== - // CHECK: %[[Q0_1:.*]], %[[Q1_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]] - // CHECK: %[[Q0_2:.*]], %[[Q1_2:.*]] = mqtopt.x() %[[Q0_1]] ctrl %[[Q1_1]] - // CHECK: %[[Q0_3:.*]], %[[Q1_3:.*]] = mqtopt.x() %[[Q0_2]] ctrl %[[Q1_2]] - - // ========================== Check for operations that should not be inserted =========================== - // CHECK-NOT: %[[ANY:.*]] = mqtopt.swap() - - // CHECK: mqtopt.deallocQubit %[[Q0_3]] - // CHECK: mqtopt.deallocQubit %[[Q1_3]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - - %q0_1, %q1_1 = mqtopt.x() %q0_0 ctrl %q1_0: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_2, %q1_2 = mqtopt.x() %q0_1 ctrl %q1_1: !mqtopt.Qubit ctrl !mqtopt.Qubit - %q0_3, %q1_3 = mqtopt.x() %q0_2 ctrl %q1_2: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_3 - - return - } -} - - -// ----- -// This test checks that controlled CNOT gates with differing controls (positive and negative) are not merged. - -module { - // CHECK-LABEL: func.func @testNoControlledSwapReconstruction - func.func @testNoControlledSwapReconstruction() { - // CHECK: %[[Q0_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q1_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q2_0:.*]] = mqtopt.allocQubit - // CHECK: %[[Q3_0:.*]] = mqtopt.allocQubit - - // ========================= Check for operations that should be kept as-is ============================= - // CHECK: %[[Q0_1:.*]], %[[Q12_1:.*]]:2, %[[Q3_1:.*]] = mqtopt.x() %[[Q0_0]] ctrl %[[Q1_0]], %[[Q2_0]] nctrl %[[Q3_0]] - // CHECK: %[[Q1_2:.*]], %[[Q0_2:.*]], %[[Q3_2:.*]] = mqtopt.x() %[[Q12_1]]#0 ctrl %[[Q0_1]] nctrl %[[Q3_1]] - // CHECK: %[[Q0_3:.*]], %[[Q1_3:.*]] = mqtopt.x() %[[Q0_2]] ctrl %[[Q1_2]] - - // ======================== Check for operations that should not be inserted ============================ - // CHECK-NOT: %[[ANY:.*]] = mqtopt.swap() - - // CHECK: mqtopt.deallocQubit %[[Q0_3]] - // CHECK: mqtopt.deallocQubit %[[Q1_3]] - // CHECK: mqtopt.deallocQubit %[[Q12_1]]#1 - // CHECK: mqtopt.deallocQubit %[[Q3_2]] - - %q0_0 = mqtopt.allocQubit - %q1_0 = mqtopt.allocQubit - %q2_0 = mqtopt.allocQubit - %q3_0 = mqtopt.allocQubit - - %q0_1, %q1_1, %q2_1, %q3_1 = mqtopt.x() %q0_0 ctrl %q1_0, %q2_0 nctrl %q3_0: !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit nctrl !mqtopt.Qubit - %q1_2, %q0_2, %q3_2 = mqtopt.x() %q1_1 ctrl %q0_1 nctrl %q3_1: !mqtopt.Qubit ctrl !mqtopt.Qubit nctrl !mqtopt.Qubit - %q0_3, %q1_3 = mqtopt.x() %q0_2 ctrl %q1_2: !mqtopt.Qubit ctrl !mqtopt.Qubit - - mqtopt.deallocQubit %q0_3 - mqtopt.deallocQubit %q1_3 - mqtopt.deallocQubit %q2_1 - mqtopt.deallocQubit %q3_2 - - return - } -} diff --git a/mlir/test/Dialect/MQTOpt/Transforms/to-quantum-computation.mlir b/mlir/test/Dialect/MQTOpt/Transforms/to-quantum-computation.mlir deleted file mode 100644 index ce4f594901..0000000000 --- a/mlir/test/Dialect/MQTOpt/Transforms/to-quantum-computation.mlir +++ /dev/null @@ -1,1060 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file --mqt-core-round-trip | FileCheck %s - -// ----- -// This test checks that the QuantumComputation roundtrip works generally. - -module { - // CHECK-LABEL: func @circuit() - func.func @circuit() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.x() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q1_1:.*]] = mqtopt.x() %[[Q1_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[Q1_2:.*]] = mqtopt.x() %[[Q0_1]] ctrl %[[Q1_1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - // CHECK: %[[Q1_3:.*]] = mqtopt.x() %[[Q1_2]] : !mqtopt.Qubit - // CHECK: %[[Q0_3:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_2]] - // CHECK: %[[Q1_4:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q1_3]] - // CHECK: memref.store %[[Q0_3]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_4]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1 = mqtopt.x() %q0_0 : !mqtopt.Qubit - %q1_1 = mqtopt.x() %q1_0 : !mqtopt.Qubit - - %q0_2, %q1_2 = mqtopt.x() %q0_1 ctrl %q1_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - - %q1_3 = mqtopt.x() %q1_2 : !mqtopt.Qubit - - %q0_3, %c0_0 = mqtopt.measure %q0_2 - %q1_4, %c1_0 = mqtopt.measure %q1_3 - - memref.store %q0_3, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_4, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks that the QuantumComputation roundtrip works with negative controls. - -module { - // CHECK-LABEL: func @negativeControls() - func.func @negativeControls() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.x() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q1_1:.*]] = mqtopt.h() %[[Q1_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[Q1_2:.*]] = mqtopt.x() %[[Q0_1]] nctrl %[[Q1_1]] : !mqtopt.Qubit nctrl !mqtopt.Qubit - // CHECK: %[[Q0_3:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_2]] - // CHECK: %[[Q1_3:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q1_2]] - // CHECK: memref.store %[[Q0_3]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_3]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1 = mqtopt.x() %q0_0 : !mqtopt.Qubit - %q1_1 = mqtopt.h() %q1_0 : !mqtopt.Qubit - - %q0_2, %q1_2 = mqtopt.x() %q0_1 nctrl %q1_1 : !mqtopt.Qubit nctrl !mqtopt.Qubit - - %q0_3, %c0_0 = mqtopt.measure %q0_2 - %q1_3, %c1_0 = mqtopt.measure %q1_2 - - memref.store %q0_3, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_3, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks if the identity gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testIGate() - func.func @testIGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.i() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.i() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if the Hadamard gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testHGate() - func.func @testHGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.h() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.h() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if the x gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testXGate() - func.func @testXGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.x() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.x() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if the y gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testYGate() - func.func @testYGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.y() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.y() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if the z gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testZGate() - func.func @testZGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.z() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.z() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if the s gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testSGate() - func.func @testSGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.s() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.s() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if the sdg gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testSdgGate() - func.func @testSdgGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.sdg() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.sdg() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if the t gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testTGate() - func.func @testTGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.t() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.t() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if the tdg gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testTdgGate() - func.func @testTdgGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.tdg() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.tdg() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if the v gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testVGate() - func.func @testVGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.v() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.v() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if the vdg gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testVdgGate() - func.func @testVdgGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.vdg() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.vdg() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if the sx gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testSXGate() - func.func @testSXGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.sx() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.sx() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if the sxdg gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testSXdgGate() - func.func @testSXdgGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.sxdg() %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.sxdg() %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if the swap gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testSwapGate() - func.func @testSwapGate() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.swap() %[[Q0_0]], %[[Q1_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q01_1]]#0 - // CHECK: %[[Q1_2:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q01_1]]#1 - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.swap() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - %q1_2, %c1_0 = mqtopt.measure %q1_1 - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks if the iswap gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testISwapGate() - func.func @testISwapGate() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.iswap() %[[Q0_0]], %[[Q1_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q01_1]]#0 - // CHECK: %[[Q1_2:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q01_1]]#1 - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.iswap() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - %q1_2, %c1_0 = mqtopt.measure %q1_1 - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks if the iswapdg gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testISwapdgGate() - func.func @testISwapdgGate() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.iswapdg() %[[Q0_0]], %[[Q1_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q01_1]]#0 - // CHECK: %[[Q1_2:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q01_1]]#1 - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.iswapdg() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - %q1_2, %c1_0 = mqtopt.measure %q1_1 - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks if the peres gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testPeresGate() - func.func @testPeresGate() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.peres() %[[Q0_0]], %[[Q1_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q01_1]]#0 - // CHECK: %[[Q1_2:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q01_1]]#1 - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.peres() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - %q1_2, %c1_0 = mqtopt.measure %q1_1 - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks if the peresdg gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testPeresdgGate() - func.func @testPeresdgGate() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.peresdg() %[[Q0_0]], %[[Q1_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q01_1]]#0 - // CHECK: %[[Q1_2:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q01_1]]#1 - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.peresdg() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - %q1_2, %c1_0 = mqtopt.measure %q1_1 - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks if the dcx gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testDCXGate() - func.func @testDCXGate() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.dcx() %[[Q0_0]], %[[Q1_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q01_1]]#0 - // CHECK: %[[Q1_2:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q01_1]]#1 - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.dcx() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - %q1_2, %c1_0 = mqtopt.measure %q1_1 - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks if the ecr gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testECRGate() - func.func @testECRGate() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.ecr() %[[Q0_0]], %[[Q1_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q01_1]]#0 - // CHECK: %[[Q1_2:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q01_1]]#1 - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.ecr() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - %q1_2, %c1_0 = mqtopt.measure %q1_1 - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks if a u rotation gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testUGate() - func.func @testUGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.u(static [1.000000e-01, 2.000000e-01, 3.000000e-01]) %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.u(static [1.000000e-01, 2.000000e-01, 3.000000e-01]) %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if a u2 rotation gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testU2Gate() - func.func @testU2Gate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.u2(static [1.000000e-01, 2.000000e-01]) %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.u2(static [1.000000e-01, 2.000000e-01]) %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if a p rotation gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testPGate() - func.func @testPGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.p(static [1.000000e-01]) %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.p(static [1.000000e-01]) %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if a rx rotation gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testRXGate() - func.func @testRXGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.rx(static [1.000000e-01]) %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.rx(static [1.000000e-01]) %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if a ry rotation gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testRYGate() - func.func @testRYGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.ry(static [1.000000e-01]) %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.ry(static [1.000000e-01]) %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if a rz rotation gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testRZGate() - func.func @testRZGate() -> (memref<1x!mqtopt.Qubit>, i1) { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: %[[Q0_1:.*]] = mqtopt.rz(static [1.000000e-01]) %[[Q0_0]] : !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q0_1]] - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<1x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<1x!mqtopt.Qubit> - - %q0_1 = mqtopt.rz(static [1.000000e-01]) %q0_0 : !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - - memref.store %q0_2, %qreg[%i0] : memref<1x!mqtopt.Qubit> - return %qreg, %c0_0 : memref<1x!mqtopt.Qubit>, i1 - } -} - -// ----- -// This test checks if a rxx rotation gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testRXXGate() - func.func @testRXXGate() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.rxx(static [1.000000e-01]) %[[Q0_0]], %[[Q1_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q01_1]]#0 - // CHECK: %[[Q1_2:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q01_1]]#1 - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.rxx(static [1.000000e-01]) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - %q1_2, %c1_0 = mqtopt.measure %q1_1 - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks if a ryy rotation gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testRYYGate() - func.func @testRYYGate() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.ryy(static [1.000000e-01]) %[[Q0_0]], %[[Q1_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q01_1]]#0 - // CHECK: %[[Q1_2:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q01_1]]#1 - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.ryy(static [1.000000e-01]) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - %q1_2, %c1_0 = mqtopt.measure %q1_1 - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks if a rzz rotation gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testRZZGate() - func.func @testRZZGate() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.rzz(static [1.000000e-01]) %[[Q0_0]], %[[Q1_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q01_1]]#0 - // CHECK: %[[Q1_2:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q01_1]]#1 - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.rzz(static [1.000000e-01]) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - %q1_2, %c1_0 = mqtopt.measure %q1_1 - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks if a rzx rotation gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testRZXGate() - func.func @testRZXGate() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.rzx(static [1.000000e-01]) %[[Q0_0]], %[[Q1_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q01_1]]#0 - // CHECK: %[[Q1_2:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q01_1]]#1 - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.rzx(static [1.000000e-01]) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - %q1_2, %c1_0 = mqtopt.measure %q1_1 - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks if a xx_minus_yy rotation gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testXXminusYYGate() - func.func @testXXminusYYGate() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.xx_minus_yy(static [1.000000e-01, 2.000000e-01]) %[[Q0_0]], %[[Q1_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q01_1]]#0 - // CHECK: %[[Q1_2:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q01_1]]#1 - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.xx_minus_yy(static [1.000000e-01, 2.000000e-01]) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - %q1_2, %c1_0 = mqtopt.measure %q1_1 - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} - -// ----- -// This test checks if a xx_plus_yy rotation gate is parsed correctly by the QuantumComputation roundtrip pass. - -module { - // CHECK-LABEL: func.func @testXXplusYYGate() - func.func @testXXplusYYGate() -> (memref<2x!mqtopt.Qubit>, i1, i1) { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() {mqt_core} : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q0_0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q1_0:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: %[[Q01_1:.*]]:2 = mqtopt.xx_plus_yy(static [1.000000e-01, 2.000000e-01]) %[[Q0_0]], %[[Q1_0]] : !mqtopt.Qubit, !mqtopt.Qubit - // CHECK: %[[Q0_2:.*]], %[[C0_0:.*]] = mqtopt.measure %[[Q01_1]]#0 - // CHECK: %[[Q1_2:.*]], %[[C1_0:.*]] = mqtopt.measure %[[Q01_1]]#1 - // CHECK: memref.store %[[Q0_2]], %[[Qreg]][%[[I0]]] : memref<2x!mqtopt.Qubit> - // CHECK: memref.store %[[Q1_2]], %[[Qreg]][%[[I1]]] : memref<2x!mqtopt.Qubit> - // CHECK: return %[[Qreg]], %[[C0_0]], %[[C1_0]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtopt.Qubit> - %q0_0 = memref.load %qreg[%i0] : memref<2x!mqtopt.Qubit> - %q1_0 = memref.load %qreg[%i1] : memref<2x!mqtopt.Qubit> - - %q0_1, %q1_1 = mqtopt.xx_plus_yy(static [1.000000e-01, 2.000000e-01]) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit - - %q0_2, %c0_0 = mqtopt.measure %q0_1 - %q1_2, %c1_0 = mqtopt.measure %q1_1 - - memref.store %q0_2, %qreg[%i0] : memref<2x!mqtopt.Qubit> - memref.store %q1_2, %qreg[%i1] : memref<2x!mqtopt.Qubit> - return %qreg, %c0_0, %c1_0 : memref<2x!mqtopt.Qubit>, i1, i1 - } -} diff --git a/mlir/test/Dialect/MQTRef/IR/dialect_features.mlir b/mlir/test/Dialect/MQTRef/IR/dialect_features.mlir deleted file mode 100644 index 5e96be2151..0000000000 --- a/mlir/test/Dialect/MQTRef/IR/dialect_features.mlir +++ /dev/null @@ -1,1267 +0,0 @@ -// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -// All rights reserved. -// -// SPDX-License-Identifier: MIT -// -// Licensed under the MIT License - -// RUN: quantum-opt %s -split-input-file -verify-diagnostics | FileCheck %s - -// ----- -// This test checks if a qubit register can be allocated using a static value. -module { - // CHECK-LABEL: func.func @testAllocOpStatic - func.func @testAllocOpStatic() { - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtref.Qubit> - - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if a qubit register can be allocated using a dynamic value. -module { - // CHECK-LABEL: func.func @testAllocOpDynamic - func.func @testAllocOpDynamic() { - // CHECK: %[[I4:.*]] = arith.constant 4 : index - // CHECK: %[[Qreg:.*]] = memref.alloc(%[[I4]]) : memref - - %i4 = arith.constant 4 : index - %qreg = memref.alloc(%i4) : memref - return - } -} - -// ----- -// This test checks if the AllocQubitOp is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testAllocQubitOp - func.func @testAllocQubitOp() { - // CHECK: %[[Q0:.*]] = mqtref.allocQubit - // CHECK: %[[Q1:.*]] = mqtref.allocQubit - - %q0 = "mqtref.allocQubit"() : () -> !mqtref.Qubit - %q1 = mqtref.allocQubit - return - } -} - -// ----- -// This test checks if the DeallocQubitOp is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testDeallocQubitOp - func.func @testDeallocQubitOp() { - // CHECK: %[[Q0:.*]] = mqtref.allocQubit - // CHECK: %[[Q1:.*]] = mqtref.allocQubit - // CHECK: mqtref.deallocQubit %[[Q0]] - // CHECK: mqtref.deallocQubit %[[Q1]] - - %q0 = "mqtref.allocQubit"() : () -> !mqtref.Qubit - %q1 = mqtref.allocQubit - - "mqtref.deallocQubit"(%q0) : (!mqtref.Qubit) -> () - mqtref.deallocQubit %q1 - return - } -} - -// ----- -// This test checks if a qubit register can be deallocated. -module { - // CHECK-LABEL: func.func @testDeallocOp - func.func @testDeallocOp() { - // CHECK: memref.dealloc %[[ANY:.*]] : memref<2x!mqtref.Qubit> - - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - memref.dealloc %qreg : memref<2x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if a qubit can be loaded from the qubit register. -module { - // CHECK-LABEL: func.func @testLoadOp - func.func @testLoadOp() { - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Q0:.*]] = memref.load %[[ANY:.*]][%[[I0]]] : memref<2x!mqtref.Qubit> - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks that all resources defined in the MQTRef dialect are parsed and handled correctly using dynamic operands. -module { - // CHECK-LABEL: func.func @testAllResourcesUsingOperands - func.func @testAllResourcesUsingOperands() { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc(%[[I1]]) : memref - // CHECK: %[[Q0:.*]] = memref.load %[[Qreg:.*]][%[[I0]]] : memref - // CHECK: memref.dealloc %[[Qreg:.*]] : memref - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc(%i1) : memref - %q0 = memref.load %qreg[%i0] : memref - memref.dealloc %qreg : memref - return - } -} - -// ----- -// This test checks if the MeasureOp is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMeasureOp - func.func @testMeasureOp() { - // CHECK: [[M0:.*]] = mqtref.measure %[[ANY:.*]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - %m0 = mqtref.measure %q0 - - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if the MeasureOp on a static qubit is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMeasureOpStatic - func.func @testMeasureOpStatic() { - // CHECK: [[M0:.*]] = mqtref.measure %[[ANY:.*]] - - %q0 = mqtref.qubit 0 - %m0 = mqtref.measure %q0 - return - } -} - -// ----- -// This test checks if the MeasureOp applied to a static qubit is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMeasureOpOnStaticInput - func.func @testMeasureOpOnStaticInput() { - // CHECK: %[[M0:.*]] = mqtref.measure %[[ANY:.*]] - - %q0 = mqtref.qubit 0 - %m0 = mqtref.measure %q0 - return - } -} - -// ----- -// This test checks if the ResetOp on a dynamic qubit is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testResetOp - func.func @testResetOp() { - // CHECK: mqtref.reset %[[ANY:.*]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - mqtref.reset %q0 - - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if the ResetOp on a static qubit is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testResetOpOnStaticInput - func.func @testResetOpOnStaticInput() { - // CHECK: mqtref.reset %[[ANY:.*]] - - %q0 = mqtref.qubit 0 - mqtref.reset %q0 - return - } -} - -// ----- -// This test checks if no-target operations without controls are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNoTargetNoControls - func.func @testNoTargetNoControls() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: mqtref.gphase(%[[C0_F64]]) - - %c0_f64 = arith.constant 3.000000e-01 : f64 - mqtref.gphase(%c0_f64) - return - } -} - -// ----- -// This test checks if no-target operations with controls are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNoTargetWithControls - func.func @testNoTargetWithControls() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: mqtref.gphase(%[[C0_F64]]) ctrl %[[Q0:.*]] - // CHECK: mqtref.gphase(%[[C0_F64]]) ctrl %[[Q0]], %[[ANY:.*]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - mqtref.gphase(%c0_f64) ctrl %q0 - mqtref.gphase(%c0_f64) ctrl %q0, %q1 - - memref.dealloc %qreg : memref<2x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if no-target operations with positive and negative controls are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNoTargetPositiveNegativeControls - func.func @testNoTargetPositiveNegativeControls() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: mqtref.gphase(%[[C0_F64]]) ctrl %[[ANY:.*]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - mqtref.gphase(%c0_f64) ctrl %q0 nctrl %q1 - - memref.dealloc %qreg : memref<2x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if no-target operations with static controls are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNoTargetWithStaticControls - func.func @testNoTargetWithStaticControls() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: mqtref.gphase(%[[C0_F64]]) ctrl %[[Q0:.*]] - // CHECK: mqtref.gphase(%[[C0_F64]]) ctrl %[[Q0]], %[[ANY:.*]] - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - - %c0_f64 = arith.constant 3.000000e-01 : f64 - mqtref.gphase(%c0_f64) ctrl %q0 - mqtref.gphase(%c0_f64) ctrl %q0, %q1 - - return - } -} - -// ----- -// This test checks if no-target operations with positive and negative static controls are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNoTargetPositiveNegativeStaticControls - func.func @testNoTargetPositiveNegativeStaticControls() { - // CHECK: %[[C0_F64:.*]] = arith.constant 3.000000e-01 - // CHECK: mqtref.gphase(%[[C0_F64]]) ctrl %[[ANY:.*]] - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - - %c0_f64 = arith.constant 3.000000e-01 : f64 - mqtref.gphase(%c0_f64) ctrl %q0 nctrl %q1 - - return - } -} - -// ----- -// This test checks if single qubit gates on dynamic qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testSingleQubitOp - func.func @testSingleQubitOp() { - // CHECK: mqtref.i() %[[Q0:.*]] - // CHECK: mqtref.h() %[[Q0]] - // CHECK: mqtref.x() %[[Q0]] - // CHECK: mqtref.y() %[[Q0]] - // CHECK: mqtref.z() %[[Q0]] - // CHECK: mqtref.s() %[[Q0]] - // CHECK: mqtref.sdg() %[[Q0]] - // CHECK: mqtref.t() %[[Q0]] - // CHECK: mqtref.tdg() %[[Q0]] - // CHECK: mqtref.v() %[[Q0]] - // CHECK: mqtref.vdg() %[[Q0]] - // CHECK: mqtref.sx() %[[Q0]] - // CHECK: mqtref.sxdg() %[[Q0]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - mqtref.i() %q0 - mqtref.h() %q0 - mqtref.x() %q0 - mqtref.y() %q0 - mqtref.z() %q0 - mqtref.s() %q0 - mqtref.sdg() %q0 - mqtref.t() %q0 - mqtref.tdg() %q0 - mqtref.v() %q0 - mqtref.vdg() %q0 - mqtref.sx() %q0 - mqtref.sxdg() %q0 - - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if single qubit gates on static qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testSingleQubitOpStatic - func.func @testSingleQubitOpStatic() { - // CHECK: mqtref.i() %[[Q0:.*]] - // CHECK: mqtref.h() %[[Q0]] - // CHECK: mqtref.x() %[[Q0]] - // CHECK: mqtref.y() %[[Q0]] - // CHECK: mqtref.z() %[[Q0]] - // CHECK: mqtref.s() %[[Q0]] - // CHECK: mqtref.sdg() %[[Q0]] - // CHECK: mqtref.t() %[[Q0]] - // CHECK: mqtref.tdg() %[[Q0]] - // CHECK: mqtref.v() %[[Q0]] - // CHECK: mqtref.vdg() %[[Q0]] - // CHECK: mqtref.sx() %[[Q0]] - // CHECK: mqtref.sxdg() %[[Q0]] - - %q0 = mqtref.qubit 0 - - mqtref.i() %q0 - mqtref.h() %q0 - mqtref.x() %q0 - mqtref.y() %q0 - mqtref.z() %q0 - mqtref.s() %q0 - mqtref.sdg() %q0 - mqtref.t() %q0 - mqtref.tdg() %q0 - mqtref.v() %q0 - mqtref.vdg() %q0 - mqtref.sx() %q0 - mqtref.sxdg() %q0 - - return - } -} - -// ----- -// This test checks if parameterized single qubit gates on dynamic qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testSingleQubitRotationOp - func.func @testSingleQubitRotationOp() { - // CHECK: %[[P0:.*]] = arith.constant 3.000000e-01 - // CHECK: mqtref.u(%[[P0]], %[[P0]], %[[P0]]) %[[Q0:.*]] - // CHECK: mqtref.u2(%[[P0]], %[[P0]] static [] mask [false, false]) %[[Q0]] - // CHECK: mqtref.p(%[[P0]]) %[[Q0]] - // CHECK: mqtref.r(%[[P0]], %[[P0]] static [] mask [false, false]) %[[Q0]] - // CHECK: mqtref.rx(%[[P0]]) %[[Q0]] - // CHECK: mqtref.ry(%[[P0]]) %[[Q0]] - // CHECK: mqtref.rz(%[[P0]]) %[[Q0]] - - %p0 = arith.constant 3.000000e-01 : f64 - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - mqtref.u(%p0, %p0, %p0) %q0 - mqtref.u2(%p0, %p0 static [] mask [false, false]) %q0 - mqtref.p(%p0) %q0 - mqtref.r(%p0, %p0 static [] mask [false, false]) %q0 - mqtref.rx(%p0) %q0 - mqtref.ry(%p0) %q0 - mqtref.rz(%p0) %q0 - - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if parameterized single qubit gates on static qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testSingleQubitRotationOpStatic - func.func @testSingleQubitRotationOpStatic() { - // CHECK: %[[P0:.*]] = arith.constant 3.000000e-01 - // CHECK: mqtref.u(%[[P0]], %[[P0]], %[[P0]]) %[[Q0:.*]] - // CHECK: mqtref.u2(%[[P0]], %[[P0]] static [] mask [false, false]) %[[Q0]] - // CHECK: mqtref.p(%[[P0]]) %[[Q0]] - // CHECK: mqtref.r(%[[P0]], %[[P0]] static [] mask [false, false]) %[[Q0]] - // CHECK: mqtref.rx(%[[P0]]) %[[Q0]] - // CHECK: mqtref.ry(%[[P0]]) %[[Q0]] - // CHECK: mqtref.rz(%[[P0]]) %[[Q0]] - - %p0 = arith.constant 3.000000e-01 : f64 - %q0 = mqtref.qubit 0 - - mqtref.u(%p0, %p0, %p0) %q0 - mqtref.u2(%p0, %p0 static [] mask [false, false]) %q0 - mqtref.p(%p0) %q0 - mqtref.r(%p0, %p0 static [] mask [false, false]) %q0 - mqtref.rx(%p0) %q0 - mqtref.ry(%p0) %q0 - mqtref.rz(%p0) %q0 - - return - } -} - -// ----- -// This test checks if controlled parameterized single qubit gates on dynamic qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testControlledSingleQubitRotationOp - func.func @testControlledSingleQubitRotationOp() { - // CHECK: %[[P0:.*]] = arith.constant 3.000000e-01 - // CHECK: mqtref.u(%[[P0]], %[[P0]], %[[P0]]) %[[Q0:.*]] ctrl %[[Q1:.*]] - // CHECK: mqtref.u2(%[[P0]], %[[P0]]) %[[Q0]] ctrl %[[Q1]] - // CHECK: mqtref.p(%[[P0]]) %[[Q0]] ctrl %[[Q1]] - // CHECK: mqtref.r(%[[P0]], %[[P0]]) %[[Q0]] ctrl %[[Q1]] - // CHECK: mqtref.rx(%[[P0]]) %[[Q0]] ctrl %[[Q1]] - // CHECK: mqtref.ry(%[[P0]]) %[[Q0]] ctrl %[[Q1]] - // CHECK: mqtref.rz(%[[P0]]) %[[Q0]] ctrl %[[Q1]] - - %p0 = arith.constant 3.000000e-01 : f64 - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - mqtref.u(%p0, %p0, %p0) %q0 ctrl %q1 - mqtref.u2(%p0, %p0) %q0 ctrl %q1 - mqtref.p(%p0) %q0 ctrl %q1 - mqtref.r(%p0, %p0) %q0 ctrl %q1 - mqtref.rx(%p0) %q0 ctrl %q1 - mqtref.ry(%p0) %q0 ctrl %q1 - mqtref.rz(%p0) %q0 ctrl %q1 - - memref.dealloc %qreg : memref<2x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if controlled parameterized single qubit gates on static qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testControlledSingleQubitRotationOpStatic - func.func @testControlledSingleQubitRotationOpStatic() { - // CHECK: %[[P0:.*]] = arith.constant 3.000000e-01 - // CHECK: mqtref.u(%[[P0]], %[[P0]], %[[P0]]) %[[Q0:.*]] ctrl %[[Q1:.*]] - // CHECK: mqtref.u2(%[[P0]], %[[P0]]) %[[Q0]] ctrl %[[Q1]] - // CHECK: mqtref.p(%[[P0]]) %[[Q0]] ctrl %[[Q1]] - // CHECK: mqtref.r(%[[P0]], %[[P0]]) %[[Q0]] ctrl %[[Q1]] - // CHECK: mqtref.rx(%[[P0]]) %[[Q0]] ctrl %[[Q1]] - // CHECK: mqtref.ry(%[[P0]]) %[[Q0]] ctrl %[[Q1]] - // CHECK: mqtref.rz(%[[P0]]) %[[Q0]] ctrl %[[Q1]] - - %p0 = arith.constant 3.000000e-01 : f64 - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - - mqtref.u(%p0, %p0, %p0) %q0 ctrl %q1 - mqtref.u2(%p0, %p0) %q0 ctrl %q1 - mqtref.p(%p0) %q0 ctrl %q1 - mqtref.r(%p0, %p0) %q0 ctrl %q1 - mqtref.rx(%p0) %q0 ctrl %q1 - mqtref.ry(%p0) %q0 ctrl %q1 - mqtref.rz(%p0) %q0 ctrl %q1 - - return - } -} - -// ----- -// This test checks if an CX gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testCXOp - func.func @testCXOp() { - // CHECK: mqtref.x() %[[Q0:.*]] ctrl %[[Q1:.*]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - mqtref.x() %q0 ctrl %q1 - - memref.dealloc %qreg : memref<2x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if a negative CX gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNegativeCXOp - func.func @testNegativeCXOp() { - // CHECK: mqtref.x() %[[Q1:.*]] nctrl %[[Q0:.*]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - mqtref.x() %q1 nctrl %q0 - - memref.dealloc %qreg : memref<2x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if an CX gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testCXOpStatic - func.func @testCXOpStatic() { - // CHECK: mqtref.x() %[[Q0:.*]] ctrl %[[Q1:.*]] - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - - mqtref.x() %q0 ctrl %q1 - - return - } -} - -// ----- -// This test checks if a negative CX gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNegativeCXOpStatic - func.func @testNegativeCXOpStatic() { - // CHECK: mqtref.x() %[[Q1:.*]] nctrl %[[Q0:.*]] - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - - mqtref.x() %q1 nctrl %q0 - - return - } -} - -// ----- -// This test checks if an MCX gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMCXOp - func.func @testMCXOp() { - // CHECK: mqtref.x() %[[Q1:.*]] ctrl %[[Q0:.*]], %[[Q2:.*]] - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<3x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<3x!mqtref.Qubit> - %q2 = memref.load %qreg[%i2] : memref<3x!mqtref.Qubit> - - //===------------------------------------------------------------------===// - // q0: ──■── q0 - // ┌─┴─┐ - // q1: ┤ X ├ q1 - // └─┬─┘ - // q2: ──■── q2 - //===----------------------------------------------------------------===// - - mqtref.x() %q1 ctrl %q0, %q2 - - memref.dealloc %qreg : memref<3x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if a negative MCX gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNegativeMCXOp - func.func @testNegativeMCXOp() { - // CHECK: mqtref.x() %[[Q1:.*]] nctrl %[[Q0:.*]], %[[Q2:.*]] - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<3x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<3x!mqtref.Qubit> - %q2 = memref.load %qreg[%i2] : memref<3x!mqtref.Qubit> - - //===------------------------------------------------------------------===// - // q0: ──○── q0 - // ┌─┴─┐ - // q1: ┤ X ├ q1 - // └─┬─┘ - // q2: ──○── q2 - //===----------------------------------------------------------------===// - - mqtref.x() %q1 nctrl %q0, %q2 - - memref.dealloc %qreg : memref<3x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if an MCX gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMCXOpStatic - func.func @testMCXOpStatic() { - // CHECK: mqtref.x() %[[Q1:.*]] ctrl %[[Q0:.*]], %[[Q2:.*]] - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - %q2 = mqtref.qubit 2 - - //===------------------------------------------------------------------===// - // q0: ──■── q0 - // ┌─┴─┐ - // q1: ┤ X ├ q1 - // └─┬─┘ - // q2: ──■── q2 - //===----------------------------------------------------------------===// - - mqtref.x() %q1 ctrl %q0, %q2 - - return - } -} - -// ----- -// This test checks if a negative MCX gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNegativeMCXOpStatic - func.func @testNegativeMCXOpStatic() { - // CHECK: mqtref.x() %[[Q1:.*]] nctrl %[[Q0:.*]], %[[Q2:.*]] - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - %q2 = mqtref.qubit 2 - - //===------------------------------------------------------------------===// - // q0: ──○── q0 - // ┌─┴─┐ - // q1: ┤ X ├ q1 - // └─┬─┘ - // q2: ──○── q2 - //===----------------------------------------------------------------===// - - mqtref.x() %q1 nctrl %q0, %q2 - - return - } -} - -// ----- -// This test checks if an MCX gate on dynamic qubits is parsed and handled correctly using different types of controls. -module { - // CHECK-LABEL: func.func @testMixedMCXOp - func.func @testMixedMCXOp() { - // CHECK: mqtref.x() %[[Q1:.*]] ctrl %[[Q0:.*]] nctrl %[[Q2:.*]] - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<3x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<3x!mqtref.Qubit> - %q2 = memref.load %qreg[%i2] : memref<3x!mqtref.Qubit> - - //===------------------------------------------------------------------===// - // q0: ──■── q0 - // ┌─┴─┐ - // q1: ┤ X ├ q1 - // └─┬─┘ - // q2: ──○-─ q2 - //===----------------------------------------------------------------===// - - mqtref.x() %q1 ctrl %q0 nctrl %q2 - - memref.dealloc %qreg : memref<3x!mqtref.Qubit> - return - } -} - - -// ----- -// This test checks if an MCX gate on static qubits is parsed and handled correctly using different types of controls. -module { - // CHECK-LABEL: func.func @testMixedMCXOpStatic - func.func @testMixedMCXOpStatic() { - // CHECK: mqtref.x() %[[Q1:.*]] ctrl %[[Q0:.*]] nctrl %[[Q2:.*]] - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - %q2 = mqtref.qubit 2 - - //===------------------------------------------------------------------===// - // q0: ──■── q0 - // ┌─┴─┐ - // q1: ┤ X ├ q1 - // └─┬─┘ - // q2: ──○-─ q2 - //===----------------------------------------------------------------===// - - mqtref.x() %q1 ctrl %q0 nctrl %q2 - - return - } -} - -// ----- -// This test checks if two target gates on dynamic qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testTwoTargetOp - func.func @testTwoTargetOp() { - // CHECK: mqtref.swap() %[[Q0:.*]], %[[Q1:.*]] - // CHECK: mqtref.iswap() %[[Q0]], %[[Q1]] - // CHECK: mqtref.iswapdg() %[[Q0]], %[[Q1]] - // CHECK: mqtref.peres() %[[Q0]], %[[Q1]] - // CHECK: mqtref.peresdg() %[[Q0]], %[[Q1]] - // CHECK: mqtref.dcx() %[[Q0]], %[[Q1]] - // CHECK: mqtref.ecr() %[[Q0]], %[[Q1]] - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - mqtref.swap() %q0, %q1 - mqtref.iswap() %q0, %q1 - mqtref.iswapdg() %q0, %q1 - mqtref.peres() %q0, %q1 - mqtref.peresdg() %q0, %q1 - mqtref.dcx() %q0, %q1 - mqtref.ecr() %q0, %q1 - - memref.dealloc %qreg : memref<2x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if two target gates on static qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testTwoTargetOpStatic - func.func @testTwoTargetOpStatic() { - // CHECK: mqtref.swap() %[[Q0:.*]], %[[Q1:.*]] - // CHECK: mqtref.iswap() %[[Q0]], %[[Q1]] - // CHECK: mqtref.iswapdg() %[[Q0]], %[[Q1]] - // CHECK: mqtref.peres() %[[Q0]], %[[Q1]] - // CHECK: mqtref.peresdg() %[[Q0]], %[[Q1]] - // CHECK: mqtref.dcx() %[[Q0]], %[[Q1]] - // CHECK: mqtref.ecr() %[[Q0]], %[[Q1]] - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - - mqtref.swap() %q0, %q1 - mqtref.iswap() %q0, %q1 - mqtref.iswapdg() %q0, %q1 - mqtref.peres() %q0, %q1 - mqtref.peresdg() %q0, %q1 - mqtref.dcx() %q0, %q1 - mqtref.ecr() %q0, %q1 - - return - } -} - -// ----- -// This test checks if a controlled SWAP gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testControlledSWAPOp - func.func @testControlledSWAPOp() { - // CHECK: mqtref.swap() %[[Q0:.*]], %[[Q1:.*]] ctrl %[[Q2:.*]] - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<3x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<3x!mqtref.Qubit> - %q2 = memref.load %qreg[%i2] : memref<3x!mqtref.Qubit> - - mqtref.swap() %q0, %q1 ctrl %q2 - - memref.dealloc %qreg : memref<3x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if a negative controlled SWAP gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNegativeControlledSWAPOp - func.func @testNegativeControlledSWAPOp() { - // CHECK: mqtref.swap() %[[Q0:.*]], %[[Q1:.*]] nctrl %[[Q2:.*]] - - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<3x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<3x!mqtref.Qubit> - %q2 = memref.load %qreg[%i2] : memref<3x!mqtref.Qubit> - - mqtref.swap() %q0, %q1 nctrl %q2 - - memref.dealloc %qreg : memref<3x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if a controlled SWAP gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testControlledSWAPOpStatic - func.func @testControlledSWAPOpStatic() { - // CHECK: mqtref.swap() %[[Q0:.*]], %[[Q1:.*]] ctrl %[[Q2:.*]] - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - %q2 = mqtref.qubit 2 - - mqtref.swap() %q0, %q1 ctrl %q2 - - return - } -} - -// ----- -// This test checks if a negative controlled SWAP gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testNegativeControlledSWAPOpStatic - func.func @testNegativeControlledSWAPOpStatic() { - // CHECK: mqtref.swap() %[[Q0:.*]], %[[Q1:.*]] nctrl %[[Q2:.*]] - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - %q2 = mqtref.qubit 2 - - mqtref.swap() %q0, %q1 nctrl %q2 - - return - } -} - -// ----- -// This test checks if a mixed controlled SWAP gate on dynamic qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMixedControlledSWAPOp - func.func @testMixedControlledSWAPOp() { - // CHECK: mqtref.swap() %[[Q0:.*]], %[[Q1:.*]] ctrl %[[Q2:.*]] nctrl %[[Q3:.*]] - - %i3 = arith.constant 3 : index - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<4x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<4x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<4x!mqtref.Qubit> - %q2 = memref.load %qreg[%i2] : memref<4x!mqtref.Qubit> - %q3 = memref.load %qreg[%i3] : memref<4x!mqtref.Qubit> - - //===------------------------------------------------------------------===// - // ┌──────┐ - // q0: ┤ ├ q0 - // │ SWAP │ - // q1: ┤ ├ q1 - // └───┬──┘ - // q2: ────■─── q2 - // │ - // q3: ────○─── q3 - //===----------------------------------------------------------------===// - - mqtref.swap() %q0, %q1 ctrl %q2 nctrl %q3 - - memref.dealloc %qreg : memref<4x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if a mixed controlled SWAP gate on static qubits is parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMixedControlledSWAPOpStatic - func.func @testMixedControlledSWAPOpStatic() { - // CHECK: mqtref.swap() %[[Q0:.*]], %[[Q1:.*]] ctrl %[[Q2:.*]] nctrl %[[Q3:.*]] - - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - %q2 = mqtref.qubit 2 - %q3 = mqtref.qubit 3 - - //===------------------------------------------------------------------===// - // ┌──────┐ - // q0: ┤ ├ q0 - // │ SWAP │ - // q1: ┤ ├ q1 - // └───┬──┘ - // q2: ────■─── q2 - // │ - // q3: ────○─── q3 - //===----------------------------------------------------------------===// - - mqtref.swap() %q0, %q1 ctrl %q2 nctrl %q3 - - return - } -} - - -// ----- -// This test checks if parameterized multiple qubit gates on dynamic qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMultipleQubitRotationOp - func.func @testMultipleQubitRotationOp() { - // CHECK: %[[P0:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: mqtref.rxx(%[[P0]]) %[[Q0:.*]], %[[Q1:.*]] - // CHECK: mqtref.ryy(%[[P0]]) %[[Q0]], %[[Q1]] - // CHECK: mqtref.rzz(%[[P0]]) %[[Q0]], %[[Q1]] - // CHECK: mqtref.rzx(%[[P0]]) %[[Q0]], %[[Q1]] - // CHECK: mqtref.xx_minus_yy(%[[P0]], %[[P0]]) %[[Q0]], %[[Q1]] - // CHECK: mqtref.xx_plus_yy(%[[P0]], %[[P0]]) %[[Q0]], %[[Q1]] - - %p0 = arith.constant 3.000000e-01 : f64 - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - mqtref.rxx(%p0) %q0, %q1 - mqtref.ryy(%p0) %q0, %q1 - mqtref.rzz(%p0) %q0, %q1 - mqtref.rzx(%p0) %q0, %q1 - mqtref.xx_minus_yy(%p0, %p0) %q0, %q1 - mqtref.xx_plus_yy(%p0, %p0) %q0, %q1 - - memref.dealloc %qreg : memref<2x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if parameterized multiple qubit gates on static qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testMultipleQubitRotationOpStatic - func.func @testMultipleQubitRotationOpStatic() { - // CHECK: %[[P0:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: mqtref.rxx(%[[P0]]) %[[Q0:.*]], %[[Q1:.*]] - // CHECK: mqtref.ryy(%[[P0]]) %[[Q0]], %[[Q1]] - // CHECK: mqtref.rzz(%[[P0]]) %[[Q0]], %[[Q1]] - // CHECK: mqtref.rzx(%[[P0]]) %[[Q0]], %[[Q1]] - // CHECK: mqtref.xx_minus_yy(%[[P0]], %[[P0]]) %[[Q0]], %[[Q1]] - // CHECK: mqtref.xx_plus_yy(%[[P0]], %[[P0]]) %[[Q0]], %[[Q1]] - - %p0 = arith.constant 3.000000e-01 : f64 - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - - mqtref.rxx(%p0) %q0, %q1 - mqtref.ryy(%p0) %q0, %q1 - mqtref.rzz(%p0) %q0, %q1 - mqtref.rzx(%p0) %q0, %q1 - mqtref.xx_minus_yy(%p0, %p0) %q0, %q1 - mqtref.xx_plus_yy(%p0, %p0) %q0, %q1 - - return - } -} - -// ----- -// This test checks if parameterized multiple qubit gates on dynamic qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testControlledMultipleQubitRotationOp - func.func @testControlledMultipleQubitRotationOp() { - // CHECK: %[[P0:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: mqtref.rxx(%[[P0]]) %[[Q0:.*]], %[[Q1:.*]] ctrl %[[Q2:.*]] - // CHECK: mqtref.ryy(%[[P0]]) %[[Q0]], %[[Q1]] ctrl %[[Q2]] - // CHECK: mqtref.rzz(%[[P0]]) %[[Q0]], %[[Q1]] ctrl %[[Q2]] - // CHECK: mqtref.rzx(%[[P0]]) %[[Q0]], %[[Q1]] ctrl %[[Q2]] - // CHECK: mqtref.xx_minus_yy(%[[P0]], %[[P0]]) %[[Q0]], %[[Q1]] ctrl %[[Q2]] - // CHECK: mqtref.xx_plus_yy(%[[P0]], %[[P0]]) %[[Q0]], %[[Q1]] ctrl %[[Q2]] - - %p0 = arith.constant 3.000000e-01 : f64 - %i2 = arith.constant 2 : index - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<3x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<3x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<3x!mqtref.Qubit> - %q2 = memref.load %qreg[%i2] : memref<3x!mqtref.Qubit> - - mqtref.rxx(%p0) %q0, %q1 ctrl %q2 - mqtref.ryy(%p0) %q0, %q1 ctrl %q2 - mqtref.rzz(%p0) %q0, %q1 ctrl %q2 - mqtref.rzx(%p0) %q0, %q1 ctrl %q2 - mqtref.xx_minus_yy(%p0, %p0) %q0, %q1 ctrl %q2 - mqtref.xx_plus_yy(%p0, %p0) %q0, %q1 ctrl %q2 - - memref.dealloc %qreg : memref<3x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if parameterized multiple qubit gates on static qubits are parsed and handled correctly. -module { - // CHECK-LABEL: func.func @testControlledMultipleQubitRotationOpStatic - func.func @testControlledMultipleQubitRotationOpStatic() { - // CHECK: %[[P0:.*]] = arith.constant 3.000000e-01 : f64 - // CHECK: mqtref.rxx(%[[P0]]) %[[Q0:.*]], %[[Q1:.*]] ctrl %[[Q2:.*]] - // CHECK: mqtref.ryy(%[[P0]]) %[[Q0]], %[[Q1]] ctrl %[[Q2]] - // CHECK: mqtref.rzz(%[[P0]]) %[[Q0]], %[[Q1]] ctrl %[[Q2]] - // CHECK: mqtref.rzx(%[[P0]]) %[[Q0]], %[[Q1]] ctrl %[[Q2]] - // CHECK: mqtref.xx_minus_yy(%[[P0]], %[[P0]]) %[[Q0]], %[[Q1]] ctrl %[[Q2]] - // CHECK: mqtref.xx_plus_yy(%[[P0]], %[[P0]]) %[[Q0]], %[[Q1]] ctrl %[[Q2]] - - %p0 = arith.constant 3.000000e-01 : f64 - %q0 = mqtref.qubit 0 - %q1 = mqtref.qubit 1 - %q2 = mqtref.qubit 2 - - mqtref.rxx(%p0) %q0, %q1 ctrl %q2 - mqtref.ryy(%p0) %q0, %q1 ctrl %q2 - mqtref.rzz(%p0) %q0, %q1 ctrl %q2 - mqtref.rzx(%p0) %q0, %q1 ctrl %q2 - mqtref.xx_minus_yy(%p0, %p0) %q0, %q1 ctrl %q2 - mqtref.xx_plus_yy(%p0, %p0) %q0, %q1 ctrl %q2 - - return - } -} - -// ----- -// This test expects an error to be thrown when parsing a parameterised operation. -module { - func.func @testParamOpInvalidFormat() { - %p0 = arith.constant 3.000000e-01 : f64 - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - // expected-error@+1 {{operation expects exactly 3 parameters but got 2}} - mqtref.u(%p0, %p0) %q0 - - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if a no-target arity constraint operation detects correctly when a target is provided. -module { - func.func @testNoTargetContainsTarget() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+1 {{number of input qubits (1) must be 0}} - mqtref.gphase(%c0_f64) %q0 - - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if static parameters for rotation operations are parsed correctly. -module { - // CHECK-LABEL: func.func @testStaticParameters - func.func @testStaticParameters() { - // CHECK: mqtref.u(static [1.000000e-01, 2.000000e-01, 3.000000e-01]) %[[ANY:.*]] - // CHECK: mqtref.u(static [1.000000e-01, 2.000000e-01, 3.000000e-01] mask [true, true, true]) %[[ANY:.*]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - mqtref.u(static [1.00000e-01, 2.00000e-01, 3.00000e-01]) %q0 - mqtref.u(static [1.00000e-01, 2.00000e-01, 3.00000e-01] mask [true, true, true]) %q0 - - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if static parameters together with dynamic parameters for rotation operations are parsed correctly. -module { - // CHECK-LABEL: func.func @testStaticAndDynamicParameters - func.func @testStaticAndDynamicParameters() { - // CHECK: mqtref.u(%[[ANY:.*]] static [1.000000e-01, 2.000000e-01] mask [true, false, true]) %[[ANY:.*]] - - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - mqtref.u(%c0_f64 static [1.00000e-01, 2.00000e-01] mask [true, false, true]) %q0 - - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if static parameters and dynamic parameters surpassing the limit of parameters together is detected correctly. -module { - func.func @testTooManyStaticAndDynamicParameters() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+1 {{operation expects exactly 3 parameters but got 4}} - mqtref.u(%c0_f64, %c0_f64 static [1.00000e-01, 2.00000e-01] mask [true, false, true]) %q0 - - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if static parameters and dynamic parameters being passed without a mask is detected correctly. -module { - func.func @testStaticAndDynamicParametersNoMask() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+1 {{operation has mixed dynamic and static parameters but no parameter mask}} - mqtref.u(%c0_f64 static [1.00000e-01, 2.00000e-01]) %q0 - - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if a static parameter mask with incorrect size is detected correctly. -module { - func.func @testStaticAndDynamicParametersWrongSizeMask() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+1 {{operation expects exactly 3 parameters but has a parameter mask with 2 entries}} - mqtref.u(%c0_f64 static [1.00000e-01, 2.00000e-01] mask [true, true]) %q0 - - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if a static parameter mask with an incorrect number of true entries is detected correctly. -module { - func.func @testStaticAndDynamicParametersIncorrectTrueEntriesInMask() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+1 {{operation has 2 static parameter(s) but has a parameter mask with 3 true entries}} - mqtref.u(%c0_f64 static [1.00000e-01, 2.00000e-01] mask [true, true, true]) %q0 - - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if a static parameter mask with `true` parameters even though the operation has no static parameters is detected correctly. -module { - func.func @testParametersMaskWithTrueEntriesButNoStaticParameters() { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - - %c0_f64 = arith.constant 3.000000e-01 : f64 - // expected-error@+1 {{operation has no static parameter but has a parameter mask with 1 true entries}} - mqtref.u(%c0_f64, %c0_f64, %c0_f64 static [] mask [true, false, false]) %q0 - - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if a no-control gate being passed a control is detected correctly. -module { - func.func @testNoControlWithControl() { - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - // expected-error@+1 {{'mqtref.barrier' op Gate marked as NoControl should not have control qubits}} - mqtref.barrier() %q0 ctrl %q1 - - memref.dealloc %qreg : memref<2x!mqtref.Qubit> - return - } -} - -// ----- -// This test checks if a Bell state is parsed and handled correctly by using many instructions tested above. -module { - // CHECK-LABEL: func.func @bellState() - func.func @bellState() { - // CHECK: %[[I1:.*]] = arith.constant 1 : index - // CHECK: %[[I0:.*]] = arith.constant 0 : index - // CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtref.Qubit> - // CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtref.Qubit> - // CHECK: %[[Q1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtref.Qubit> - // CHECK: mqtref.h() %[[Q0]] - // CHECK: mqtref.x() %[[Q1]] ctrl %[[Q0]] - // CHECK: %[[M0:.*]] = mqtref.measure %[[Q0]] - // CHECK: %[[M1:.*]] = mqtref.measure %[[Q1]] - // CHECK: memref.dealloc %[[Qreg]] : memref<2x!mqtref.Qubit> - - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - - mqtref.h() %q0 - mqtref.x() %q1 ctrl %q0 - %m0 = mqtref.measure %q0 - %m1 = mqtref.measure %q1 - - memref.dealloc %qreg : memref<2x!mqtref.Qubit> - return - } -} diff --git a/mlir/test/lit.cfg.py b/mlir/test/lit.cfg.py deleted file mode 100644 index 5d877ad3a2..0000000000 --- a/mlir/test/lit.cfg.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -# ruff: noqa: INP001 - -"""LIT Configuration file for the MQT MLIR test suite. - -This file configures the LLVM LIT testing infrastructure for MLIR dialect tests. -""" - -from __future__ import annotations - -import sys -from pathlib import Path - -import lit.formats -from lit.llvm import llvm_config - -# Use `lit_config` to access `config` from lit.site.cfg.py -config = globals().get("config") -if config is None: - msg = "LIT config object is missing. Ensure lit.site.cfg.py is loaded first." - raise RuntimeError(msg) - -config.name = "MQT Core MLIR Lit Tests" -config.test_format = lit.formats.ShTest(execute_external=False) - -# Define the file extensions to treat as test files. -config.suffixes = [".mlir"] - -# Define the root path of where to look for tests. -config.test_source_root = Path(__file__).parent - -# Define where to execute tests (and produce the output). -config.test_exec_root = Path(config.mqt_core_mlir_test_dir) - -# Add substitutions for the required tools. -found = llvm_config.add_tool_substitutions(tools=["not", "FileCheck", "split-file"]) -if not found: - msg = "Could not find one or more required LLVM tools: 'not', 'FileCheck', 'split-file'." - raise RuntimeError(msg) - -# Successively check directories whether they contain the quantum-opt tool -tool_name = "quantum-opt" if sys.platform != "win32" else "quantum-opt.exe" -base_tool_dir = Path(config.mqt_core_mlir_tools_dir) - -candidate_dirs = [base_tool_dir] -if config.cmake_build_type: - candidate_dirs.append(base_tool_dir / config.cmake_build_type) -for cfg in ["Debug", "Release", "RelWithDebInfo", "MinSizeRel"]: - cfg_dir = base_tool_dir / cfg - if cfg_dir not in candidate_dirs: - candidate_dirs.append(cfg_dir) - -found = False -for candidate_dir in candidate_dirs: - if (candidate_dir / tool_name).exists(): - llvm_config.add_tool_substitutions(["quantum-opt"], [str(candidate_dir)]) - found = True - break - -if not found: - msg = f"Could not find {tool_name} anywhere under {base_tool_dir}." - raise RuntimeError(msg) diff --git a/mlir/test/lit.site.cfg.py.in b/mlir/test/lit.site.cfg.py.in deleted file mode 100644 index ef81a98de0..0000000000 --- a/mlir/test/lit.site.cfg.py.in +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -@LIT_SITE_CFG_IN_HEADER@ - -config.llvm_tools_dir = lit_config.substitute("@LLVM_TOOLS_DIR@") -config.cmake_build_type = "@CMAKE_BUILD_TYPE@" -config.mqt_core_mlir_tools_dir = "@PROJECT_BINARY_DIR@/mlir/tools/quantum-opt" -config.mqt_core_mlir_test_dir = "@PROJECT_BINARY_DIR@/mlir/test" - -import lit.llvm -lit.llvm.initialize(lit_config, config) - -# Let the main config do the real work. -lit_config.load_config(config, "@PROJECT_SOURCE_DIR@/mlir/test/lit.cfg.py") diff --git a/mlir/tools/CMakeLists.txt b/mlir/tools/CMakeLists.txt index 612bb92968..ea8dec04c0 100644 --- a/mlir/tools/CMakeLists.txt +++ b/mlir/tools/CMakeLists.txt @@ -6,5 +6,4 @@ # # Licensed under the MIT License -add_subdirectory(quantum-opt) add_subdirectory(mqt-cc) diff --git a/mlir/tools/quantum-opt/CMakeLists.txt b/mlir/tools/quantum-opt/CMakeLists.txt deleted file mode 100644 index 8b061da301..0000000000 --- a/mlir/tools/quantum-opt/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) -get_property(conversion_libs GLOBAL PROPERTY MLIR_CONVERSION_LIBS) -get_property(extension_libs GLOBAL PROPERTY MLIR_EXTENSION_LIBS) -set(LIBS - ${dialect_libs} - ${conversion_libs} - ${extension_libs} - ${transform_libs} - ${passes_libs} - MLIROptLib - MLIRMQTOptTransforms - MQTRefToMQTOpt - MQTOptToMQTRef - MQTRefToQIR - QIRToMQTRef) - -add_mlir_tool(quantum-opt quantum-opt.cpp DEPENDS ${LIBS} SUPPORT_PLUGINS) -target_link_libraries(quantum-opt PUBLIC ${LIBS}) -llvm_update_compile_flags(quantum-opt) -mlir_check_all_link_libraries(quantum-opt) -export_executable_symbols_for_plugins(quantum-opt) diff --git a/mlir/tools/quantum-opt/quantum-opt.cpp b/mlir/tools/quantum-opt/quantum-opt.cpp deleted file mode 100644 index 1bf5d1ffed..0000000000 --- a/mlir/tools/quantum-opt/quantum-opt.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Conversion/MQTOptToMQTRef/MQTOptToMQTRef.h" // IWYU pragma: keep -#include "mlir/Conversion/MQTRefToMQTOpt/MQTRefToMQTOpt.h" // IWYU pragma: keep -#include "mlir/Conversion/MQTRefToQIR/MQTRefToQIR.h" // IWYU pragma: keep -#include "mlir/Conversion/QIRToMQTRef/QIRToMQTRef.h" // IWYU pragma: keep -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" // IWYU pragma: keep -#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" // IWYU pragma: keep -#include "mlir/Dialect/MQTRef/IR/MQTRefDialect.h" // IWYU pragma: keep - -// Suppress warnings about implicit captures of `this` in lambdas -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-this-capture" -#endif - -#include -#include -#include -#include -#include - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -int main(const int argc, char** argv) { - mlir::registerAllPasses(); - mqt::ir::opt::registerMQTOptPasses(); - mqt::ir::registerMQTRefToMQTOptPasses(); - mqt::ir::registerMQTOptToMQTRefPasses(); - mqt::ir::registerQIRToMQTRefPasses(); - mqt::ir::registerMQTRefToQIRPass(); - - mlir::DialectRegistry registry; - mlir::registerAllDialects(registry); - mlir::func::registerAllExtensions(registry); - registry.insert(); - registry.insert(); - - return mlir::asMainReturnCode( - MlirOptMain(argc, argv, "Quantum optimizer driver\n", registry)); -} diff --git a/mlir/unittests/CMakeLists.txt b/mlir/unittests/CMakeLists.txt index 27a6fa2bd0..50f95e4cf0 100644 --- a/mlir/unittests/CMakeLists.txt +++ b/mlir/unittests/CMakeLists.txt @@ -6,11 +6,8 @@ # # Licensed under the MIT License -add_subdirectory(dialect) add_subdirectory(pipeline) -add_subdirectory(translation) add_custom_target(mqt-core-mlir-unittests) -add_dependencies(mqt-core-mlir-unittests mqt-core-mlir-compiler-pipeline-test - mqt-core-mlir-translation-test mqt-core-mlir-wireiterator-test) +add_dependencies(mqt-core-mlir-unittests mqt-core-mlir-compiler-pipeline-test) diff --git a/mlir/unittests/dialect/CMakeLists.txt b/mlir/unittests/dialect/CMakeLists.txt deleted file mode 100644 index 9b1611134c..0000000000 --- a/mlir/unittests/dialect/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -set(testname "mqt-core-mlir-wireiterator-test") -file(GLOB_RECURSE WIREITERATOR_TEST_SOURCES *.cpp) - -if(NOT TARGET ${testname}) - # create an executable in which the tests will be stored - add_executable(${testname} ${WIREITERATOR_TEST_SOURCES}) - # link the Google test infrastructure and a default main function to the test executable. - target_link_libraries(${testname} PRIVATE GTest::gtest_main MLIRParser MLIRMQTOpt MLIRSCFDialect - MLIRArithDialect MLIRIndexDialect) - # discover tests - gtest_discover_tests(${testname} DISCOVERY_TIMEOUT 60) - set_target_properties(${testname} PROPERTIES FOLDER unittests) -endif() diff --git a/mlir/unittests/dialect/test_wireiterator.cpp b/mlir/unittests/dialect/test_wireiterator.cpp deleted file mode 100644 index f88db94d55..0000000000 --- a/mlir/unittests/dialect/test_wireiterator.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" -#include "mlir/Dialect/MQTOpt/IR/WireIterator.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace mlir; -using namespace mqt::ir::opt; - -/** @returns a module containing the circuit from the "Tackling the Qubit - * Mapping Problem for NISQ-Era Quantum Devices" paper by Li et al. - */ -static OwningOpRef getModule(MLIRContext& ctx) { - const char* ir = R"mlir( -module { - %0 = mqtopt.allocQubit - %1 = mqtopt.allocQubit - %out_qubits = mqtopt.h() %0 : !mqtopt.Qubit - %out_qubits_0 = mqtopt.h() %1 : !mqtopt.Qubit - %out_qubits_1 = mqtopt.z() %out_qubits : !mqtopt.Qubit - %out_qubits_2, %pos_ctrl_out_qubits = mqtopt.x() %out_qubits_0 ctrl %out_qubits_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %out_qubits_3 = mqtopt.h() %out_qubits_2 : !mqtopt.Qubit - %out_qubits_4, %pos_ctrl_out_qubits_5 = mqtopt.x() %pos_ctrl_out_qubits ctrl %out_qubits_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit - %false = arith.constant false - %2:2 = scf.if %false -> (!mqtopt.Qubit, !mqtopt.Qubit) { - %out_qubits_7 = mqtopt.y() %out_qubits_4 : !mqtopt.Qubit - scf.yield %out_qubits_7, %pos_ctrl_out_qubits_5 : !mqtopt.Qubit, !mqtopt.Qubit - } else { - scf.yield %out_qubits_4, %pos_ctrl_out_qubits_5 : !mqtopt.Qubit, !mqtopt.Qubit - } - %idx0 = index.constant 0 - %idx8 = index.constant 8 - %idx1 = index.constant 1 - %3:2 = scf.for %arg0 = %idx0 to %idx8 step %idx1 iter_args(%arg1 = %2#0, %arg2 = %2#1) -> (!mqtopt.Qubit, !mqtopt.Qubit) { - %out_qubits_7 = mqtopt.h() %arg1 : !mqtopt.Qubit - %out_qubits_8 = mqtopt.h() %arg2 : !mqtopt.Qubit - scf.yield %out_qubits_7, %out_qubits_8 : !mqtopt.Qubit, !mqtopt.Qubit - } - mqtopt.deallocQubit %3#0 - mqtopt.deallocQubit %3#1 - - %4 = mqtopt.qubit 42 - %5 = mqtopt.reset %4 - %out_qubits_6 = mqtopt.h() %5 : !mqtopt.Qubit -} -)mlir"; - return parseSourceString(ir, &ctx); -} - -static std::string toString(Operation* op) { - std::string opStr; - llvm::raw_string_ostream os(opStr); - os << *op; - os.flush(); - return opStr; -} - -static void checkOperationEqual(Operation* op, const std::string& expected) { - ASSERT_EQ(expected, toString(op)); -} - -static void checkOperationStartsWith(Operation* op, const std::string& prefix) { - ASSERT_TRUE(toString(op).starts_with(prefix)); -} - -class WireIteratorTest : public ::testing::Test { -protected: - std::unique_ptr context; - - void SetUp() override { - DialectRegistry registry; - registry.insert(); - registry.insert(); - registry.insert(); - registry.insert(); - - context = std::make_unique(); - context->appendDialectRegistry(registry); - context->loadAllAvailableDialects(); - } -}; - -TEST_F(WireIteratorTest, TestForward) { - - /// - /// Test the forward iteration. - /// - - auto module = getModule(*context); - auto alloc = *(module->getOps().begin()); - auto q = alloc.getQubit(); - WireIterator it(q, q.getParentRegion()); - - checkOperationEqual(*it, "%0 = mqtopt.allocQubit"); - - ++it; - checkOperationEqual(*it, "%out_qubits = mqtopt.h() %0 : !mqtopt.Qubit"); - - ++it; - checkOperationEqual(*it, - "%out_qubits_1 = mqtopt.z() %out_qubits : !mqtopt.Qubit"); - - ++it; - checkOperationEqual( - *it, "%out_qubits_2, %pos_ctrl_out_qubits = mqtopt.x() %out_qubits_0 " - "ctrl %out_qubits_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit"); - - ++it; - checkOperationEqual( - *it, - "%out_qubits_4, %pos_ctrl_out_qubits_5 = mqtopt.x() %pos_ctrl_out_qubits " - "ctrl %out_qubits_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit"); - - ++it; - checkOperationStartsWith( - *it, "%2:2 = scf.if %false -> (!mqtopt.Qubit, !mqtopt.Qubit)"); - - ++it; - checkOperationStartsWith(*it, - "%3:2 = scf.for %arg0 = %idx0 to %idx8 step %idx1"); - - ++it; - checkOperationEqual(*it, "mqtopt.deallocQubit %3#0"); - - ++it; - ASSERT_EQ(it, std::default_sentinel); - - ++it; - ASSERT_EQ(it, std::default_sentinel); -} - -TEST_F(WireIteratorTest, TestBackward) { - - /// - /// Test the backward iteration. - /// - - auto module = getModule(*context); - auto allocs = module->getOps(); - const auto allocRng = llvm::make_range(allocs.begin(), allocs.end()); - const auto allocVec = llvm::to_vector(allocRng); - auto alloc = allocVec[1]; - auto q = alloc.getQubit(); - WireIterator it(q, q.getParentRegion()); - const WireIterator begin(it); - - ASSERT_EQ(it, begin); - - for (; it != std::default_sentinel; ++it) { - llvm::dbgs() << **it << '\n'; /// Keep for debugging purposes. - } - - ASSERT_EQ(it, std::default_sentinel); - - --it; - checkOperationEqual(*it, "mqtopt.deallocQubit %3#1"); - - --it; - checkOperationStartsWith(*it, - "%3:2 = scf.for %arg0 = %idx0 to %idx8 step %idx1"); - - --it; - checkOperationStartsWith( - *it, "%2:2 = scf.if %false -> (!mqtopt.Qubit, !mqtopt.Qubit)"); - - --it; - checkOperationEqual( - *it, - "%out_qubits_4, %pos_ctrl_out_qubits_5 = mqtopt.x() %pos_ctrl_out_qubits " - "ctrl %out_qubits_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit"); - - --it; - checkOperationEqual( - *it, "%out_qubits_3 = mqtopt.h() %out_qubits_2 : !mqtopt.Qubit"); - - --it; - checkOperationEqual( - *it, "%out_qubits_2, %pos_ctrl_out_qubits = mqtopt.x() %out_qubits_0 " - "ctrl %out_qubits_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit"); - - --it; - checkOperationEqual(*it, "%out_qubits_0 = mqtopt.h() %1 : !mqtopt.Qubit"); - - --it; - checkOperationEqual(*it, "%1 = mqtopt.allocQubit"); - - ASSERT_EQ(it, begin); - - --it; - checkOperationEqual(*it, "%1 = mqtopt.allocQubit"); - - ASSERT_EQ(it, begin); -} - -TEST_F(WireIteratorTest, TestForwardAndBackward) { - - /// - /// Test the forward as well as the backward iteration. - /// - - auto module = getModule(*context); - auto alloc = *(module->getOps().begin()); - auto q = alloc.getQubit(); - WireIterator it(q, q.getParentRegion()); - const WireIterator begin(it); - - checkOperationEqual(*it, "%0 = mqtopt.allocQubit"); - - ++it; - checkOperationEqual(*it, "%out_qubits = mqtopt.h() %0 : !mqtopt.Qubit"); - - ++it; - checkOperationEqual(*it, - "%out_qubits_1 = mqtopt.z() %out_qubits : !mqtopt.Qubit"); - - ++it; - checkOperationEqual( - *it, "%out_qubits_2, %pos_ctrl_out_qubits = mqtopt.x() %out_qubits_0 " - "ctrl %out_qubits_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit"); - - --it; - checkOperationEqual(*it, - "%out_qubits_1 = mqtopt.z() %out_qubits : !mqtopt.Qubit"); - - --it; - checkOperationEqual(*it, "%out_qubits = mqtopt.h() %0 : !mqtopt.Qubit"); - - --it; - checkOperationEqual(*it, "%0 = mqtopt.allocQubit"); - - ASSERT_EQ(it, begin); - - for (; it != std::default_sentinel; ++it) { - llvm::dbgs() << **it << '\n'; /// Keep for debugging purposes. - } - - ASSERT_EQ(it, std::default_sentinel); - - it = std::prev(it); // Back to last non-sentinel item. - - for (; it != begin; --it) { - llvm::dbgs() << **it << '\n'; /// Keep for debugging purposes. - } - - ASSERT_EQ(it, begin); -} - -TEST_F(WireIteratorTest, TestRecursiveUse) { - - /// - /// Test the recursive use of the iterator. - /// - - auto module = getModule(*context); - auto alloc = *(module->getOps().begin()); - auto q = alloc.getQubit(); - WireIterator it(q, q.getParentRegion()); - - /// Advance until 'scf.for'. - for (; it != std::default_sentinel; ++it) { - if (isa(*it)) { - break; - } - llvm::dbgs() << **it << '\n'; /// Keep for debugging purposes. - } - - auto loop = cast(*it); - for (auto [iter, init] : - llvm::zip(loop.getRegionIterArgs(), loop.getInitArgs())) { - if (init == it.qubit()) { - WireIterator rec(iter, &loop.getRegion()); - const WireIterator recBegin(rec); - rec--; - - ASSERT_EQ(rec, recBegin); // Test blockargument handling. - - rec++; - checkOperationEqual(*rec, - "%out_qubits_7 = mqtopt.h() %arg1 : !mqtopt.Qubit"); - - rec++; - checkOperationEqual(*rec, "scf.yield %out_qubits_7, %out_qubits_8 : " - "!mqtopt.Qubit, !mqtopt.Qubit"); - } - } -} - -TEST_F(WireIteratorTest, TestStaticQubit) { - - /// - /// Test the iteration with a static qubit. - /// - - auto module = getModule(*context); - auto qubit = *(module->getOps().begin()); - auto q = qubit.getQubit(); - WireIterator it(q, q.getParentRegion()); - const WireIterator begin(it); - - checkOperationEqual(*it, "%4 = mqtopt.qubit 42"); - - ++it; - checkOperationEqual(*it, "%5 = mqtopt.reset %4"); - - ++it; - checkOperationEqual(*it, "%out_qubits_6 = mqtopt.h() %5 : !mqtopt.Qubit"); - - ++it; - ASSERT_EQ(it, std::default_sentinel); - - --it; - checkOperationEqual(*it, "%out_qubits_6 = mqtopt.h() %5 : !mqtopt.Qubit"); - ASSERT_EQ(it.qubit(), (*it)->getResult(0)); // q = %out_qubits_6 - - --it; - checkOperationEqual(*it, "%out_qubits_6 = mqtopt.h() %5 : !mqtopt.Qubit"); - ASSERT_EQ(it.qubit(), (*it)->getOperand(0)); // q = %5 - - --it; - checkOperationEqual(*it, "%5 = mqtopt.reset %4"); - - --it; - checkOperationEqual(*it, "%4 = mqtopt.qubit 42"); - - ASSERT_EQ(it, begin); -} diff --git a/mlir/unittests/translation/CMakeLists.txt b/mlir/unittests/translation/CMakeLists.txt deleted file mode 100644 index 322de565ec..0000000000 --- a/mlir/unittests/translation/CMakeLists.txt +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM -# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH -# All rights reserved. -# -# SPDX-License-Identifier: MIT -# -# Licensed under the MIT License - -set(testname "mqt-core-mlir-translation-test") -file(GLOB_RECURSE TRANSLATION_TEST_SOURCES *.cpp) - -if(NOT TARGET ${testname}) - # create an executable in which the tests will be stored - add_executable(${testname} ${TRANSLATION_TEST_SOURCES}) - # link the Google test infrastructure and a default main function to the test executable. - target_link_libraries( - ${testname} - PRIVATE GTest::gmock - GTest::gtest_main - LLVMFileCheck - MLIRPass - MLIRTransforms - MLIRMQTRefTranslation - MQT::CoreQASM) - # discover tests - gtest_discover_tests(${testname} DISCOVERY_TIMEOUT 60) - set_target_properties(${testname} PROPERTIES FOLDER unittests) - - if(MSVC) - target_compile_options(${testname} PRIVATE /EHsc) - else() - target_compile_options(${testname} PRIVATE -fexceptions) - endif() -endif() diff --git a/mlir/unittests/translation/test_translation.cpp b/mlir/unittests/translation/test_translation.cpp deleted file mode 100644 index 305743831e..0000000000 --- a/mlir/unittests/translation/test_translation.cpp +++ /dev/null @@ -1,1149 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "ir/QuantumComputation.hpp" -#include "ir/operations/Control.hpp" -#include "ir/operations/IfElseOperation.hpp" -#include "ir/operations/OpType.hpp" -#include "ir/operations/StandardOperation.hpp" -#include "mlir/Dialect/MQTRef/IR/MQTRefDialect.h" -#include "mlir/Dialect/MQTRef/Translation/ImportQuantumComputation.h" -#include "qasm3/Importer.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace qc; - -namespace { - -class ImportTest : public ::testing::Test { -protected: - std::unique_ptr context; - - void SetUp() override { - mlir::DialectRegistry registry; - registry.insert(); - registry.insert(); - registry.insert(); - registry.insert(); - registry.insert(); - - context = std::make_unique(); - context->appendDialectRegistry(registry); - context->loadAllAvailableDialects(); - } - - void runPasses(const mlir::ModuleOp module) const { - mlir::PassManager passManager(context.get()); - passManager.addPass(mlir::createCanonicalizerPass()); - passManager.addPass(mlir::createMem2Reg()); - passManager.addPass(mlir::createRemoveDeadValuesPass()); - if (passManager.run(module).failed()) { - FAIL() << "Failed to run passes"; - } - } - - void TearDown() override {} -}; - -} // namespace - -// ################################################## -// # Helper functions -// ################################################## - -static std::string getOutputString(mlir::OwningOpRef* module) { - std::string outputString; - llvm::raw_string_ostream os(outputString); - (*module)->print(os); - os.flush(); - return outputString; -} - -static std::string formatTargets(std::initializer_list targets) { - std::string s; - bool first = true; - for (auto t : targets) { - if (!first) { - s += ", "; - } - first = false; - s += "%[[Q" + std::to_string(t) + "]]"; - } - return s; -} - -static std::string formatParams(std::initializer_list params) { - if (params.size() == 0) { - return ""; - } - std::ostringstream os; - os.setf(std::ios::scientific); - os << std::setprecision(6); - bool first = true; - os << "static ["; - for (const double p : params) { - if (!first) { - os << ", "; - } - first = false; - os << p; - } - os << "]"; - return os.str(); -} - -static std::string -getCheckStringOperation(const char* op, std::initializer_list targets) { - return std::string("CHECK: mqtref.") + op + "() " + formatTargets(targets); -} - -static std::string -getCheckStringOperationParams(const char* op, - std::initializer_list params, - std::initializer_list targets) { - return std::string("CHECK: mqtref.") + op + "(" + formatParams(params) + - ") " + formatTargets(targets); -} - -// Adapted from -// https://github.com/llvm/llvm-project/blob/d2b3e86321eaf954451e0a49534fa654dd67421e/llvm/unittests/MIR/MachineMetadata.cpp#L181 -static bool checkOutput(const std::string& checkString, - const std::string& outputString) { - auto checkBuffer = llvm::MemoryBuffer::getMemBuffer(checkString, ""); - auto outputBuffer = - llvm::MemoryBuffer::getMemBuffer(outputString, "Output", false); - - llvm::SmallString<4096> checkFileBuffer; - const llvm::FileCheckRequest request; - llvm::FileCheck fc(request); - const llvm::StringRef checkFileText = - fc.CanonicalizeFile(*checkBuffer, checkFileBuffer); - - llvm::SourceMgr sm; - sm.AddNewSourceBuffer( - llvm::MemoryBuffer::getMemBuffer(checkFileText, "CheckFile"), - llvm::SMLoc()); - if (fc.readCheckFile(sm, checkFileText)) { - return false; - } - - auto outputBufferBuffer = outputBuffer->getBuffer(); - sm.AddNewSourceBuffer(std::move(outputBuffer), llvm::SMLoc()); - return fc.checkInput(sm, outputBufferBuffer); -} - -// ################################################## -// # Basic tests -// ################################################## - -namespace { - -TEST_F(ImportTest, EntryPoint) { - const QuantumComputation qc{}; - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - - const auto outputString = getOutputString(&module); - const auto* checkString = R"( - CHECK: func.func @main() attributes {passthrough = ["entry_point"]} - CHECK: return - )"; - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -TEST_F(ImportTest, AllocationAndDeallocation) { - const QuantumComputation qc(3, 2); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - - const auto outputString = getOutputString(&module); - const auto* checkString = R"( - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<3x!mqtref.Qubit> - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<3x!mqtref.Qubit> - CHECK: %[[I1:.*]] = arith.constant 1 : index - CHECK: %[[Q1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<3x!mqtref.Qubit> - CHECK: %[[I2:.*]] = arith.constant 2 : index - CHECK: %[[Q2:.*]] = memref.load %[[Qreg]][%[[I2]]] : memref<3x!mqtref.Qubit> - CHECK: %[[Creg:.*]] = memref.alloca() : memref<2xi1> - CHECK: memref.dealloc %[[Qreg]] : memref<3x!mqtref.Qubit> - )"; - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -TEST_F(ImportTest, Measure01) { - QuantumComputation qc(2, 2); - qc.measure({0, 1}, {0, 1}); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - - const auto outputString = getOutputString(&module); - const auto* checkString = R"( - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtref.Qubit> - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtref.Qubit> - CHECK: %[[I1:.*]] = arith.constant 1 : index - CHECK: %[[Q1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtref.Qubit> - CHECK: %[[Creg:.*]] = memref.alloca() : memref<2xi1> - CHECK: %[[M0:.*]] = mqtref.measure %[[Q0]] - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: memref.store %[[M0]], %[[Creg]][%[[I0]]] : memref<2xi1> - CHECK: %[[M1:.*]] = mqtref.measure %[[Q1]] - CHECK: %[[I1:.*]] = arith.constant 1 : index - CHECK: memref.store %[[M1]], %[[Creg]][%[[I1]]] : memref<2xi1> - )"; - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -TEST_F(ImportTest, Measure0) { - QuantumComputation qc(2, 2); - qc.measure(0, 0); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - - const auto outputString = getOutputString(&module); - const auto* checkString = R"( - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtref.Qubit> - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtref.Qubit> - CHECK: %[[I1:.*]] = arith.constant 1 : index - CHECK: %[[Q1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtref.Qubit> - CHECK: %[[Creg:.*]] = memref.alloca() : memref<2xi1> - CHECK: %[[M0:.*]] = mqtref.measure %[[Q0]] - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: memref.store %[[M0]], %[[Creg]][%[[I0]]] : memref<2xi1> - CHECK-NOT: mqtref.measure %[[Q1]] - CHECK-NOT: arith.constant 1 : index - CHECK-NOT: memref.store %[[ANY:.*]], %[[Creg]][%[[ANY:.*]]] : memref<2xi1> - )"; - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -TEST_F(ImportTest, Reset01) { - QuantumComputation qc(2); - qc.reset({0, 1}); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - - const auto outputString = getOutputString(&module); - const auto* checkString = R"( - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtref.Qubit> - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtref.Qubit> - CHECK: %[[I1:.*]] = arith.constant 1 : index - CHECK: %[[Q1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtref.Qubit> - CHECK: mqtref.reset %[[Q0]] - CHECK: mqtref.reset %[[Q1]] - )"; - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -TEST_F(ImportTest, Reset0) { - QuantumComputation qc(2); - qc.reset(0); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - - const auto outputString = getOutputString(&module); - const auto* checkString = R"( - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtref.Qubit> - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtref.Qubit> - CHECK: %[[I1:.*]] = arith.constant 1 : index - CHECK: %[[Q1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtref.Qubit> - CHECK: mqtref.reset %[[Q0]] - CHECK-NOT: mqtref.reset %[[Q1]] - )"; - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -} // namespace - -// ################################################## -// # Test unitary operations -// ################################################## - -namespace { - -struct TestCaseUnitary { - std::string name; // Name of the test case - size_t numQubits; - std::function build; - std::string checkStringOperation; -}; - -} // namespace - -[[maybe_unused]] static std::ostream& -operator<<(std::ostream& os, const TestCaseUnitary& testCase) { - return os << testCase.name; -} - -static std::string -getCheckStringTestCaseUnitary(const TestCaseUnitary& testCase) { - std::string result; - - // Add entry point - result += - "CHECK: func.func @main() attributes {passthrough = [\"entry_point\"]}\n"; - - // Add register allocation - result += "CHECK: %[[Qreg:.*]] = memref.alloc() : memref<" + - std::to_string(testCase.numQubits) + "x!mqtref.Qubit>\n"; - - // Add qubit extraction - for (size_t i = 0; i < testCase.numQubits; ++i) { - result += "CHECK: %[[I" + std::to_string(i) + ":.*]] = arith.constant " + - std::to_string(i) + " : index\n"; - result += "CHECK: %[[Q" + std::to_string(i) + - ":.*]] = memref.load %[[Qreg]][%[[I" + std::to_string(i) + - ":.*]]] : memref<" + std::to_string(testCase.numQubits) + - "x!mqtref.Qubit>\n"; - } - - // Add operation-specific check - result += testCase.checkStringOperation; - result += "\n"; - - // Add dealocation - result += "CHECK: memref.dealloc %[[Qreg]] : memref<" + - std::to_string(testCase.numQubits) + "x!mqtref.Qubit>\n"; - - // Add return - result += "CHECK: return\n"; - - return result; -} - -namespace { - -class OperationTestUnitary - : public ImportTest, - public ::testing::WithParamInterface {}; - -TEST_P(OperationTestUnitary, EmitsExpectedOperation) { - const auto& param = GetParam(); - - QuantumComputation qc(param.numQubits); - param.build(qc); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - - const auto outputString = getOutputString(&module); - const auto checkString = getCheckStringTestCaseUnitary(param); - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -INSTANTIATE_TEST_SUITE_P( - Operations, OperationTestUnitary, - ::testing::Values( - // Barrier - TestCaseUnitary{ - .name = "Barrier_0", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.barrier(0); }, - .checkStringOperation = getCheckStringOperation("barrier", {0})}, - TestCaseUnitary{ - .name = "Barrier_01", - .numQubits = 2, - .build = [](QuantumComputation& qc) { qc.barrier({0, 1}); }, - .checkStringOperation = getCheckStringOperation("barrier", {0, 1})}, - // 1-qubit gates without parameters - TestCaseUnitary{ - .name = "I", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.i(0); }, - .checkStringOperation = getCheckStringOperation("i", {0})}, - TestCaseUnitary{ - .name = "H", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.h(0); }, - .checkStringOperation = getCheckStringOperation("h", {0})}, - TestCaseUnitary{ - .name = "X", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.x(0); }, - .checkStringOperation = getCheckStringOperation("x", {0})}, - TestCaseUnitary{ - .name = "Y", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.y(0); }, - .checkStringOperation = getCheckStringOperation("y", {0})}, - TestCaseUnitary{ - .name = "Z", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.z(0); }, - .checkStringOperation = getCheckStringOperation("z", {0})}, - TestCaseUnitary{ - .name = "S", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.s(0); }, - .checkStringOperation = getCheckStringOperation("s", {0})}, - TestCaseUnitary{ - .name = "Sdg", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.sdg(0); }, - .checkStringOperation = getCheckStringOperation("sdg", {0})}, - TestCaseUnitary{ - .name = "T", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.t(0); }, - .checkStringOperation = getCheckStringOperation("t", {0})}, - TestCaseUnitary{ - .name = "Tdg", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.tdg(0); }, - .checkStringOperation = getCheckStringOperation("tdg", {0})}, - TestCaseUnitary{ - .name = "V", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.v(0); }, - .checkStringOperation = getCheckStringOperation("v", {0})}, - TestCaseUnitary{ - .name = "Vdg", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.vdg(0); }, - .checkStringOperation = getCheckStringOperation("vdg", {0})}, - // 1-qubit gates with parameters - TestCaseUnitary{ - .name = "U", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.u(0.1, 0.2, 0.3, 0); }, - .checkStringOperation = - getCheckStringOperationParams("u", {0.1, 0.2, 0.3}, {0})}, - TestCaseUnitary{ - .name = "U2", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.u2(0.1, 0.2, 0); }, - .checkStringOperation = - getCheckStringOperationParams("u2", {0.1, 0.2}, {0})}, - TestCaseUnitary{.name = "Phase", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.p(0.1, 0); }, - .checkStringOperation = - getCheckStringOperationParams("p", {0.1}, {0})}, - TestCaseUnitary{ - .name = "R", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.r(0.1, 0.2, 0); }, - .checkStringOperation = - getCheckStringOperationParams("r", {0.1, 0.2}, {0})}, - TestCaseUnitary{.name = "RX", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.rx(0.1, 0); }, - .checkStringOperation = - getCheckStringOperationParams("rx", {0.1}, {0})}, - TestCaseUnitary{.name = "RY", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.ry(0.1, 0); }, - .checkStringOperation = - getCheckStringOperationParams("ry", {0.1}, {0})}, - TestCaseUnitary{.name = "RZ", - .numQubits = 1, - .build = [](QuantumComputation& qc) { qc.rz(0.1, 0); }, - .checkStringOperation = - getCheckStringOperationParams("rz", {0.1}, {0})}, - // 2-qubit gates without parameters - TestCaseUnitary{ - .name = "iSWAP", - .numQubits = 2, - .build = [](QuantumComputation& qc) { qc.iswap(0, 1); }, - .checkStringOperation = getCheckStringOperation("iswap", {0, 1})}, - TestCaseUnitary{ - .name = "iSWAPdg", - .numQubits = 2, - .build = [](QuantumComputation& qc) { qc.iswapdg(0, 1); }, - .checkStringOperation = getCheckStringOperation("iswapdg", {0, 1})}, - TestCaseUnitary{ - .name = "Peres", - .numQubits = 2, - .build = [](QuantumComputation& qc) { qc.peres(0, 1); }, - .checkStringOperation = getCheckStringOperation("peres", {0, 1})}, - TestCaseUnitary{ - .name = "Peresdg", - .numQubits = 2, - .build = [](QuantumComputation& qc) { qc.peresdg(0, 1); }, - .checkStringOperation = getCheckStringOperation("peresdg", {0, 1})}, - TestCaseUnitary{ - .name = "DCX", - .numQubits = 2, - .build = [](QuantumComputation& qc) { qc.dcx(0, 1); }, - .checkStringOperation = getCheckStringOperation("dcx", {0, 1})}, - TestCaseUnitary{ - .name = "ECR", - .numQubits = 2, - .build = [](QuantumComputation& qc) { qc.ecr(0, 1); }, - .checkStringOperation = getCheckStringOperation("ecr", {0, 1})}, - // 2-qubit gates with parameters - TestCaseUnitary{ - .name = "RXX", - .numQubits = 2, - .build = [](QuantumComputation& qc) { qc.rxx(0.1, 0, 1); }, - .checkStringOperation = getCheckStringOperationParams("rxx", {0.1}, - {0, 1})}, - TestCaseUnitary{ - .name = "RYY", - .numQubits = 2, - - .build = [](QuantumComputation& qc) { qc.ryy(0.1, 0, 1); }, - .checkStringOperation = getCheckStringOperationParams("ryy", {0.1}, - {0, 1})}, - TestCaseUnitary{ - .name = "RZZ", - .numQubits = 2, - - .build = [](QuantumComputation& qc) { qc.rzz(0.1, 0, 1); }, - .checkStringOperation = getCheckStringOperationParams("rzz", {0.1}, - {0, 1})}, - TestCaseUnitary{ - .name = "RZX", - .numQubits = 2, - .build = [](QuantumComputation& qc) { qc.rzx(0.1, 0, 1); }, - .checkStringOperation = getCheckStringOperationParams("rzx", {0.1}, - {0, 1})}, - TestCaseUnitary{ - .name = "XX_MINUS_YY", - .numQubits = 2, - .build = - [](QuantumComputation& qc) { qc.xx_minus_yy(0.1, 0.2, 0, 1); }, - .checkStringOperation = getCheckStringOperationParams( - "xx_minus_yy", {0.1, 0.2}, {0, 1})}, - TestCaseUnitary{ - .name = "XX_PLUS_YY", - .numQubits = 2, - .build = - [](QuantumComputation& qc) { qc.xx_plus_yy(0.1, 0.2, 0, 1); }, - .checkStringOperation = getCheckStringOperationParams( - "xx_plus_yy", {0.1, 0.2}, {0, 1})}, - // Controlled gates - TestCaseUnitary{ - .name = "CX_0P_1", - .numQubits = 2, - .build = [](QuantumComputation& qc) { qc.cx(0, 1); }, - .checkStringOperation = "CHECK: mqtref.x() %[[Q1]] ctrl %[[Q0]]"}, - TestCaseUnitary{ - .name = "CX_1P_0", - .numQubits = 2, - .build = [](QuantumComputation& qc) { qc.cx(1, 0); }, - .checkStringOperation = "CHECK: mqtref.x() %[[Q0]] ctrl %[[Q1]]"}, - TestCaseUnitary{ - .name = "CX_0N_1", - .numQubits = 2, - .build = [](QuantumComputation& qc) { qc.cx(0_nc, 1); }, - .checkStringOperation = "CHECK: mqtref.x() %[[Q1]] nctrl %[[Q0]]"}, - TestCaseUnitary{ - .name = "MCX_0P1P_2", - .numQubits = 3, - .build = [](QuantumComputation& qc) { qc.mcx({0, 1}, 2); }, - .checkStringOperation = - "CHECK: mqtref.x() %[[Q2]] ctrl %[[Q0]], %[[Q1]]"}, - TestCaseUnitary{ - .name = "MCX_0N2P_1", - .numQubits = 3, - .build = [](QuantumComputation& qc) { qc.mcx({0_nc, 2}, 1); }, - .checkStringOperation = - "CHECK: mqtref.x() %[[Q1]] ctrl %[[Q2]] nctrl %[[Q0]]"}, - TestCaseUnitary{ - .name = "MCX_2N1N_0", - .numQubits = 3, - .build = [](QuantumComputation& qc) { qc.mcx({2_nc, 1_nc}, 0); }, - .checkStringOperation = - "CHECK: mqtref.x() %[[Q0]] nctrl %[[Q1]], %[[Q2]]"})); - -} // namespace - -// ################################################## -// # Test register-controlled if-else operations -// ################################################## - -namespace { - -struct TestCaseIfRegister { - std::string name; // Name of the test case - ComparisonKind comparisonKind; - std::string predicate; -}; - -} // namespace - -[[maybe_unused]] static std::ostream& -operator<<(std::ostream& os, const TestCaseIfRegister& testCase) { - return os << testCase.name; -} - -static std::string -getCheckStringTestCaseIfRegister(const TestCaseIfRegister& testCase) { - std::string result; - - result += R"( - CHECK: func.func @main() attributes {passthrough = ["entry_point"]} - CHECK: %[[Exp:.*]] = arith.constant 1 : i64 - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<1x!mqtref.Qubit> - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtref.Qubit> - CHECK: %[[M0:.*]] = mqtref.measure %[[Q0]] - CHECK: %[[C0:.*]] = arith.extui %[[M0]] : i1 to i64 - )"; - - // Add comparison - result += "CHECK: %[[Cnd0:.*]] = arith.cmpi " + testCase.predicate + - ", %[[C0]], %[[Exp]] : i64\n"; - - result += R"( - CHECK: scf.if %[[Cnd0]] { - CHECK: mqtref.x() %[[Q0]] - CHECK: } - CHECK: memref.dealloc %[[Qreg]] : memref<1x!mqtref.Qubit> - CHECK: return - )"; - - return result; -} - -namespace { - -class OperationTestIfRegister - : public ImportTest, - public ::testing::WithParamInterface {}; - -TEST_P(OperationTestIfRegister, EmitsExpectedOperation) { - const auto& param = GetParam(); - - QuantumComputation qc(1); - const auto& creg = qc.addClassicalRegister(1); - qc.measure(0, 0); - qc.if_(X, 0, creg, 1U, param.comparisonKind); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - runPasses(module.get()); - - const auto outputString = getOutputString(&module); - const auto checkString = getCheckStringTestCaseIfRegister(param); - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -INSTANTIATE_TEST_SUITE_P( - Operations, OperationTestIfRegister, - ::testing::Values( - TestCaseIfRegister{ - .name = "Eq", .comparisonKind = Eq, .predicate = "eq"}, - TestCaseIfRegister{ - .name = "Neq", .comparisonKind = Neq, .predicate = "ne"}, - TestCaseIfRegister{ - .name = "Lt", .comparisonKind = Lt, .predicate = "ult"}, - TestCaseIfRegister{ - .name = "Leq", .comparisonKind = Leq, .predicate = "ule"}, - TestCaseIfRegister{ - .name = "Geq", .comparisonKind = Geq, .predicate = "uge"}, - TestCaseIfRegister{ - .name = "Gt", .comparisonKind = Gt, .predicate = "ugt"})); - -TEST_F(ImportTest, IfRegisterEq2) { - QuantumComputation qc(2); - const auto& creg = qc.addClassicalRegister(2); - qc.measure({0, 1}, {0, 1}); - qc.if_(X, 0, creg, 2U, Eq); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - runPasses(module.get()); - - const auto outputString = getOutputString(&module); - const auto* checkString = R"( - CHECK: %[[Exp:.*]] = arith.constant 2 : i64 - CHECK: %[[Sum0:.*]] = arith.constant 0 : i64 - CHECK: %[[I2:.*]] = arith.constant 2 : index - CHECK: %[[I1:.*]] = arith.constant 1 : index - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtref.Qubit> - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtref.Qubit> - CHECK: %[[Q1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtref.Qubit> - CHECK: %[[Creg:.*]] = memref.alloca() : memref<2xi1> - CHECK: %[[M0:.*]] = mqtref.measure %[[Q0]] - CHECK: memref.store %[[M0]], %[[Creg]][%[[I0]]] : memref<2xi1> - CHECK: %[[M1:.*]] = mqtref.measure %[[Q1]] - CHECK: memref.store %[[M1]], %[[Creg]][%[[I1]]] : memref<2xi1> - CHECK: %[[Sum1:.*]] = scf.for %[[Ii:.*]] = %[[I0]] to %[[I2]] step %[[I1]] iter_args(%[[Sumi:.*]] = %[[Sum0]]) -> (i64) { - CHECK: %[[Bi:.*]] = memref.load %[[Creg]][%[[Ii]]] : memref<2xi1> - CHECK: %[[Ci:.*]] = arith.extui %[[Bi:.*]] : i1 to i64 - CHECK: %[[Indi:.*]] = arith.index_cast %[[Ii]] : index to i64 - CHECK: %[[Shli:.*]] = arith.shli %[[Ci]], %[[Indi]] : i64 - CHECK: %[[Sumj:.*]] = arith.addi %[[Sumi]], %[[Shli]] : i64 - CHECK: scf.yield %[[Sumj]] : i64 - CHECK: } - CHECK: %[[Cnd0:.*]] = arith.cmpi eq, %[[Sum1]], %[[Exp]] : i64 - CHECK: scf.if %[[Cnd0]] { - CHECK: mqtref.x() %[[Q0]] - CHECK: } - )"; - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -TEST_F(ImportTest, IfElseRegister) { - QuantumComputation qc(2); - const auto& creg = qc.addClassicalRegister(2); - qc.measure({0, 1}, {0, 1}); - qc.ifElse(std::make_unique(0, X), - std::make_unique(0, Y), creg, 2U, Eq); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - runPasses(module.get()); - - const auto outputString = getOutputString(&module); - const auto* checkString = R"( - CHECK: %[[Exp:.*]] = arith.constant 2 : i64 - CHECK: %[[Sum0:.*]] = arith.constant 0 : i64 - CHECK: %[[I2:.*]] = arith.constant 2 : index - CHECK: %[[I1:.*]] = arith.constant 1 : index - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtref.Qubit> - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtref.Qubit> - CHECK: %[[Q1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtref.Qubit> - CHECK: %[[Creg:.*]] = memref.alloca() : memref<2xi1> - CHECK: %[[M0:.*]] = mqtref.measure %[[Q0]] - CHECK: memref.store %[[M0]], %[[Creg]][%[[I0]]] : memref<2xi1> - CHECK: %[[M1:.*]] = mqtref.measure %[[Q1]] - CHECK: memref.store %[[M1]], %[[Creg]][%[[I1]]] : memref<2xi1> - CHECK: %[[Sum1:.*]] = scf.for %[[Ii:.*]] = %[[I0]] to %[[I2]] step %[[I1]] iter_args(%[[Sumi:.*]] = %[[Sum0]]) -> (i64) { - CHECK: %[[Bi:.*]] = memref.load %[[Creg]][%[[Ii]]] : memref<2xi1> - CHECK: %[[Ci:.*]] = arith.extui %[[Bi:.*]] : i1 to i64 - CHECK: %[[Indi:.*]] = arith.index_cast %[[Ii]] : index to i64 - CHECK: %[[Shli:.*]] = arith.shli %[[Ci]], %[[Indi]] : i64 - CHECK: %[[Sumj:.*]] = arith.addi %[[Sumi]], %[[Shli]] : i64 - CHECK: scf.yield %[[Sumj]] : i64 - CHECK: } - CHECK: %[[Cnd0:.*]] = arith.cmpi eq, %[[Sum1]], %[[Exp]] : i64 - CHECK: scf.if %[[Cnd0:.*]] { - CHECK: mqtref.x() %[[Q0]] - CHECK: } else { - CHECK: mqtref.y() %[[Q0]] - CHECK: } - )"; - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -TEST_F(ImportTest, IfElseHandlingFromQasm) { - const qc::QuantumComputation qc = qasm3::Importer::imports("OPENQASM 3.0;" - "qubit q;" - "bit c;" - "h q;" - "c = measure q;" - "if(c) {" - " x q;" - "} else {" - " h q;" - "}" - "c = measure q;"); - auto module = translateQuantumComputationToMLIR(context.get(), qc); - - const auto outputString = getOutputString(&module); - const auto* checkString = R"( - CHECK: func.func @main() attributes {passthrough = ["entry_point"]} - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<1x!mqtref.Qubit> - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtref.Qubit> - CHECK: %[[Creg:.*]] = memref.alloca() : memref<1xi1> - CHECK: mqtref.h() %[[Q0]] - CHECK: %[[M0:.*]] = mqtref.measure %[[Q0]] - CHECK: %[[I1:.*]] = arith.constant 0 : index - CHECK: memref.store %[[M0]], %[[Creg]][%[[I1]]] : memref<1xi1> - CHECK: %[[I2:.*]] = arith.constant 0 : index - CHECK: %[[M2:.*]] = memref.load %[[Creg]][%[[I2]]] : memref<1xi1> - CHECK: %[[true:.*]] = arith.constant true - CHECK: %[[M3:.*]] = arith.cmpi eq, %[[M2]], %[[true]] : i1 - CHECK: scf.if %[[M3]] { - CHECK: mqtref.x() %[[Q0]] - CHECK: } else { - CHECK: mqtref.h() %[[Q0]] - CHECK: } - CHECK: %[[M4:.*]] = mqtref.measure %[[Q0]] - CHECK: %[[I3:.*]] = arith.constant 0 : index - CHECK: memref.store %[[M4]], %[[Creg]][%[[I3]]] : memref<1xi1> - CHECK: memref.dealloc %[[Qreg]] : memref<1x!mqtref.Qubit> - )"; - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -TEST_F(ImportTest, IfElseHandlingFromQasmMultipleStatements) { - const qc::QuantumComputation qc = qasm3::Importer::imports("OPENQASM 3.0;" - "qubit q;" - "bit c;" - "h q;" - "c = measure q;" - "if(c) {" - " x q;" - " s q;" - "} else {" - " h q;" - " t q;" - "}" - "c = measure q;"); - auto module = translateQuantumComputationToMLIR(context.get(), qc); - - const auto outputString = getOutputString(&module); - const auto* checkString = R"( - CHECK: func.func @main() attributes {passthrough = ["entry_point"]} - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<1x!mqtref.Qubit> - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtref.Qubit> - CHECK: %[[Creg:.*]] = memref.alloca() : memref<1xi1> - CHECK: mqtref.h() %[[Q0]] - CHECK: %[[M0:.*]] = mqtref.measure %[[Q0]] - CHECK: %[[I1:.*]] = arith.constant 0 : index - CHECK: memref.store %[[M0]], %[[Creg]][%[[I1]]] : memref<1xi1> - CHECK: %[[I2:.*]] = arith.constant 0 : index - CHECK: %[[M2:.*]] = memref.load %[[Creg]][%[[I2]]] : memref<1xi1> - CHECK: %[[true:.*]] = arith.constant true - CHECK: %[[M3:.*]] = arith.cmpi eq, %[[M2]], %[[true]] : i1 - CHECK: scf.if %[[M3]] { - CHECK: mqtref.x() %[[Q0]] - CHECK: mqtref.s() %[[Q0]] - CHECK: } else { - CHECK: mqtref.h() %[[Q0]] - CHECK: mqtref.t() %[[Q0]] - CHECK: } - CHECK: %[[M4:.*]] = mqtref.measure %[[Q0]] - CHECK: %[[I3:.*]] = arith.constant 0 : index - CHECK: memref.store %[[M4]], %[[Creg]][%[[I3]]] : memref<1xi1> - CHECK: memref.dealloc %[[Qreg]] : memref<1x!mqtref.Qubit> - )"; - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -} // namespace - -// ################################################## -// # Test bit-controlled if-else operations -// ################################################## - -namespace { - -struct TestCaseIfBit { - std::string name; // Name of the test case - ComparisonKind comparisonKind; - bool expectedValueInput; - bool expectedValueOutput; -}; - -} // namespace - -[[maybe_unused]] static std::ostream& -operator<<(std::ostream& os, const TestCaseIfBit& testCase) { - return os << testCase.name; -} - -static std::string getCheckStringTestCaseIfBit(const TestCaseIfBit& testCase) { - std::string result; - - result += R"( - CHECK: func.func @main() attributes {passthrough = ["entry_point"]} - )"; - - if (!testCase.expectedValueOutput) { - result += "CHECK: %[[Exp0:.*]] = arith.constant false\n"; - } - - result += R"( - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<1x!mqtref.Qubit> - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtref.Qubit> - CHECK: %[[M0:.*]] = mqtref.measure %[[Q0]] - )"; - - if (!testCase.expectedValueOutput) { - result += R"( - CHECK: %[[Cnd0:.*]] = arith.cmpi eq, %[[M0]], %[[Exp0]] : i1 - CHECK: scf.if %[[Cnd0]] { - )"; - } else { - result += "CHECK: scf.if %[[M0]] {\n"; - } - - result += R"( - CHECK: mqtref.x() %[[Q0]] - CHECK: } - CHECK: memref.dealloc %[[Qreg]] : memref<1x!mqtref.Qubit> - CHECK: return - )"; - - return result; -} - -namespace { - -class OperationTestIfBit : public ImportTest, - public ::testing::WithParamInterface { -}; - -TEST_P(OperationTestIfBit, EmitsExpectedOperation) { - const auto& param = GetParam(); - - QuantumComputation qc(1, 1); - qc.measure(0, 0); - qc.if_(X, 0, 0, param.expectedValueInput, param.comparisonKind); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - runPasses(module.get()); - - const auto outputString = getOutputString(&module); - const auto checkString = getCheckStringTestCaseIfBit(param); - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -INSTANTIATE_TEST_SUITE_P( - Operations, OperationTestIfBit, - ::testing::Values(TestCaseIfBit{.name = "EqTrue", - .comparisonKind = Eq, - .expectedValueInput = true, - .expectedValueOutput = true}, - TestCaseIfBit{.name = "EqFalse", - .comparisonKind = Eq, - .expectedValueInput = false, - .expectedValueOutput = false}, - TestCaseIfBit{.name = "NeqTrue", - .comparisonKind = Neq, - .expectedValueInput = true, - .expectedValueOutput = false}, - TestCaseIfBit{.name = "NeqFalse", - .comparisonKind = Neq, - .expectedValueInput = false, - .expectedValueOutput = true})); - -struct TestCaseIfElseBit { - std::string name; // Name of the test case - ComparisonKind comparisonKind; - bool expectedValueInput; - std::string thenOperation; - std::string elseOperation; -}; - -} // namespace - -[[maybe_unused]] static std::ostream& -operator<<(std::ostream& os, const TestCaseIfElseBit& testCase) { - return os << testCase.name; -} - -static std::string -getCheckStringTestCaseIfElseBit(const TestCaseIfElseBit& testCase) { - std::string result; - - result += R"( - CHECK: func.func @main() attributes {passthrough = ["entry_point"]} - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<1x!mqtref.Qubit> - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<1x!mqtref.Qubit> - CHECK: %[[M0:.*]] = mqtref.measure %[[Q0]] - CHECK: scf.if %[[M0]] { - )"; - - result += "CHECK: mqtref." + testCase.thenOperation + "() %[[Q0]]\n"; - result += "} else {\n"; - result += "CHECK: mqtref." + testCase.elseOperation + "() %[[Q0]]\n"; - - result += R"( - CHECK: } - CHECK: memref.dealloc %[[Qreg]] : memref<1x!mqtref.Qubit> - CHECK: return - )"; - - return result; -} - -namespace { - -class OperationTestIfElseBit - : public ImportTest, - public ::testing::WithParamInterface {}; - -TEST_P(OperationTestIfElseBit, EmitsExpectedOperation) { - const auto& param = GetParam(); - - QuantumComputation qc(1, 1); - qc.measure(0, 0); - qc.ifElse(std::make_unique(0, X), - std::make_unique(0, Y), 0, - param.expectedValueInput, param.comparisonKind); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - runPasses(module.get()); - - const auto outputString = getOutputString(&module); - const auto checkString = getCheckStringTestCaseIfElseBit(param); - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -INSTANTIATE_TEST_SUITE_P( - Operations, OperationTestIfElseBit, - ::testing::Values(TestCaseIfElseBit{.name = "EqTrue", - .comparisonKind = Eq, - .expectedValueInput = true, - .thenOperation = "x", - .elseOperation = "y"}, - TestCaseIfElseBit{.name = "EqFalse", - .comparisonKind = Eq, - .expectedValueInput = false, - .thenOperation = "y", - .elseOperation = "x"}, - TestCaseIfElseBit{.name = "NeqTrue", - .comparisonKind = Neq, - .expectedValueInput = true, - .thenOperation = "y", - .elseOperation = "x"}, - TestCaseIfElseBit{.name = "NeqFalse", - .comparisonKind = Neq, - .expectedValueInput = false, - .thenOperation = "x", - .elseOperation = "y"})); - -} // namespace - -// ################################################## -// # Test full programs -// ################################################## - -namespace { - -TEST_F(ImportTest, GHZ) { - QuantumComputation qc(3, 3); - qc.h(0); - qc.cx(0, 1); - qc.cx(0, 2); - qc.measure({0, 1, 2}, {0, 1, 2}); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - - const auto outputString = getOutputString(&module); - const auto* checkString = R"( - CHECK: func.func @main() attributes {passthrough = ["entry_point"]} - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<3x!mqtref.Qubit> - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<3x!mqtref.Qubit> - CHECK: %[[I1:.*]] = arith.constant 1 : index - CHECK: %[[Q1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<3x!mqtref.Qubit> - CHECK: %[[I2:.*]] = arith.constant 2 : index - CHECK: %[[Q2:.*]] = memref.load %[[Qreg]][%[[I2]]] : memref<3x!mqtref.Qubit> - CHECK: %[[Creg:.*]] = memref.alloca() : memref<3xi1> - CHECK: mqtref.h() %[[Q0]] - CHECK: mqtref.x() %[[Q1]] ctrl %[[Q0]] - CHECK: mqtref.x() %[[Q2]] ctrl %[[Q0]] - CHECK: %[[M0:.*]] = mqtref.measure %[[Q0]] - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: memref.store %[[M0]], %[[Creg]][%[[I0]]] : memref<3xi1> - CHECK: %[[M1:.*]] = mqtref.measure %[[Q1]] - CHECK: %[[I1:.*]] = arith.constant 1 : index - CHECK: memref.store %[[M1]], %[[Creg]][%[[I1]]] : memref<3xi1> - CHECK: %[[M2:.*]] = mqtref.measure %[[Q2]] - CHECK: %[[I2:.*]] = arith.constant 2 : index - CHECK: memref.store %[[M2]], %[[Creg]][%[[I2]]] : memref<3xi1> - CHECK: memref.dealloc %[[Qreg]] : memref<3x!mqtref.Qubit> - CHECK: return - )"; - - ASSERT_TRUE(checkOutput(checkString, outputString)); -} - -TEST_F(ImportTest, MultipleClassicalRegistersMeasureStores) { - QuantumComputation qc(2, 0); - qc.addClassicalRegister(1, "c0"); - qc.addClassicalRegister(1, "c1"); - qc.measure({0, 1}, {0, 1}); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - // We do not run passes here; pattern should match raw allocation and stores - - const auto output = getOutputString(&module); - const std::string check = R"( - CHECK: func.func @main() attributes {passthrough = ["entry_point"]} - CHECK: %[[Qreg:.*]] = memref.alloc() : memref<2x!mqtref.Qubit> - CHECK: %[[I0:.*]] = arith.constant 0 : index - CHECK: %[[Q0:.*]] = memref.load %[[Qreg]][%[[I0]]] : memref<2x!mqtref.Qubit> - CHECK: %[[I1:.*]] = arith.constant 1 : index - CHECK: %[[Q1:.*]] = memref.load %[[Qreg]][%[[I1]]] : memref<2x!mqtref.Qubit> - CHECK: %[[CregA:.*]] = memref.alloca() : memref<1xi1> - CHECK: %[[CregB:.*]] = memref.alloca() : memref<1xi1> - CHECK: %[[M0:.*]] = mqtref.measure %[[Q0]] - CHECK: %[[I0A:.*]] = arith.constant 0 : index - CHECK: memref.store %[[M0]], %[[CregA]][%[[I0A]]] : memref<1xi1> - CHECK: %[[M1:.*]] = mqtref.measure %[[Q1]] - CHECK: %[[I0B:.*]] = arith.constant 0 : index - CHECK: memref.store %[[M1]], %[[CregB]][%[[I0B]]] : memref<1xi1> - CHECK: memref.dealloc %[[Qreg]] : memref<2x!mqtref.Qubit> - CHECK: return - )"; - - ASSERT_TRUE(checkOutput(check, output)); -} - -TEST_F(ImportTest, MultipleQuantumRegistersCX) { - QuantumComputation qc(0, 0); - qc.addQubitRegister(1, "q0"); - qc.addQubitRegister(1, "q1"); - qc.cx(0, 1); - - auto module = translateQuantumComputationToMLIR(context.get(), qc); - - const auto output = getOutputString(&module); - const std::string check = R"( - CHECK: func.func @main() attributes {passthrough = ["entry_point"]} - CHECK: %[[QregA:.*]] = memref.alloc() : memref<1x!mqtref.Qubit> - CHECK: %[[I0A:.*]] = arith.constant 0 : index - CHECK: %[[Q0A:.*]] = memref.load %[[QregA]][%[[I0A]]] : memref<1x!mqtref.Qubit> - CHECK: %[[QregB:.*]] = memref.alloc() : memref<1x!mqtref.Qubit> - CHECK: %[[I0B:.*]] = arith.constant 0 : index - CHECK: %[[Q0B:.*]] = memref.load %[[QregB]][%[[I0B]]] : memref<1x!mqtref.Qubit> - CHECK: mqtref.x() %[[Q0B]] ctrl %[[Q0A]] - CHECK: memref.dealloc %[[QregA]] : memref<1x!mqtref.Qubit> - CHECK: memref.dealloc %[[QregB]] : memref<1x!mqtref.Qubit> - CHECK: return - )"; - - ASSERT_TRUE(checkOutput(check, output)); -} - -} // namespace From 7b9657fb8ac41aba44bc8e882b15d3ef06782cd7 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 8 Jan 2026 19:13:02 +0100 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=94=A5=20Remove=20leftover=20statemen?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- cmake/SetupMLIR.cmake | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cmake/SetupMLIR.cmake b/cmake/SetupMLIR.cmake index 644b13fd5b..6e7c829228 100644 --- a/cmake/SetupMLIR.cmake +++ b/cmake/SetupMLIR.cmake @@ -40,10 +40,6 @@ include_directories(${MQT_MLIR_BUILD_INCLUDE_DIR}) link_directories(${LLVM_BUILD_LIBRARY_DIR}) add_definitions(${LLVM_DEFINITIONS}) -string(REPLACE "." ";" MLIR_VERSION_COMPONENTS ${MLIR_VERSION}) -list(GET MLIR_VERSION_COMPONENTS 0 MLIR_VERSION_MAJOR) -add_compile_definitions(MLIR_VERSION_MAJOR=${MLIR_VERSION_MAJOR}) - # set the binary directory for the build tree such that, e.g., docs can be generated in the build # tree set(MLIR_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) From 75f3b6d01ec89cc6ab58abf441d018ffccac195b Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 8 Jan 2026 21:14:03 +0100 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=93=9D=20update=20MLIR=20documentatio?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- docs/mlir/Conversions.md | 10 ++-- docs/mlir/MQTOpt.md | 29 ----------- docs/mlir/MQTRef.md | 23 --------- docs/mlir/QC.md | 11 ++++ docs/mlir/QCO.md | 11 ++++ docs/mlir/index.md | 109 +++------------------------------------ 6 files changed, 31 insertions(+), 162 deletions(-) delete mode 100644 docs/mlir/MQTOpt.md delete mode 100644 docs/mlir/MQTRef.md create mode 100644 docs/mlir/QC.md create mode 100644 docs/mlir/QCO.md diff --git a/docs/mlir/Conversions.md b/docs/mlir/Conversions.md index 18d81e44ba..9da1119faf 100644 --- a/docs/mlir/Conversions.md +++ b/docs/mlir/Conversions.md @@ -2,20 +2,16 @@ ## Internal Conversions -```{include} Conversions/MLIRMQTOptToMQTRef.md +```{include} Conversions/MLIRQCToQCO.md ``` -```{include} Conversions/MLIRMQTRefToMQTOpt.md +```{include} Conversions/MLIRQCOToQC.md ``` ## QIR Conversions -```{include} Conversions/MLIRMQTRefToQIR.md - -``` - -```{include} Conversions/MLIRQIRToMQTRef.md +```{include} Conversions/MLIRQCToQIR.md ``` diff --git a/docs/mlir/MQTOpt.md b/docs/mlir/MQTOpt.md deleted file mode 100644 index 67508788eb..0000000000 --- a/docs/mlir/MQTOpt.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -tocdepth: 3 ---- - -# MQTOpt Dialect - -```{include} Dialects/MLIRMQTOptDialect.md -:start-line: 2 -:end-before: "## Operations" -``` - -## Operations - -```{include} Dialects/MLIRMQTOptDialect.md -:start-after: "## Operations" -``` - -## Passes - -```{include} Passes/MLIRMQTOptPasses.md - -``` - -## Interfaces - -```{include} Dialects/MLIRMQTOptInterfaces.md -:start-line: 2 -:heading-offset: 1 -``` diff --git a/docs/mlir/MQTRef.md b/docs/mlir/MQTRef.md deleted file mode 100644 index 12005a6496..0000000000 --- a/docs/mlir/MQTRef.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -tocdepth: 3 ---- - -# MQTRef Dialect - -```{include} Dialects/MLIRMQTRefDialect.md -:start-line: 2 -:end-before: "## Operations" -``` - -## Operations - -```{include} Dialects/MLIRMQTRefDialect.md -:start-after: "## Operations" -``` - -## Interfaces - -```{include} Dialects/MLIRMQTRefInterfaces.md -:start-line: 2 -:heading-offset: 1 -``` diff --git a/docs/mlir/QC.md b/docs/mlir/QC.md new file mode 100644 index 0000000000..4bc93432fa --- /dev/null +++ b/docs/mlir/QC.md @@ -0,0 +1,11 @@ +--- +tocdepth: 3 +--- + +```{include} Dialects/MLIRQCDialect.md + +``` + +```{include} Dialects/MLIRQCInterfaces.md +:heading-offset: 1 +``` diff --git a/docs/mlir/QCO.md b/docs/mlir/QCO.md new file mode 100644 index 0000000000..5411108bd6 --- /dev/null +++ b/docs/mlir/QCO.md @@ -0,0 +1,11 @@ +--- +tocdepth: 3 +--- + +```{include} Dialects/MLIRQCODialect.md + +``` + +```{include} Dialects/MLIRQCOInterfaces.md +:heading-offset: 1 +``` diff --git a/docs/mlir/index.md b/docs/mlir/index.md index 8184041d5f..3b5e91b20c 100644 --- a/docs/mlir/index.md +++ b/docs/mlir/index.md @@ -4,20 +4,20 @@ This part of the MQT explores the capabilities of the Multi-Level Intermediate R We define multiple dialects, each with its dedicated purpose: -- The {doc}`MQTRef dialect ` uses reference semantics and is designed as a compatibility dialect that simplifies translations from and to existing languages such as Qiskit, OpenQASM, or QIR. +- The {doc}`QC dialect ` uses reference semantics and is designed as a compatibility dialect that simplifies translations from and to existing languages such as Qiskit, OpenQASM, or QIR. -- The {doc}`MQTOpt dialect ` uses value semantics and is mainly designed for running optimizations. +- The {doc}`QCO dialect ` uses value semantics and is mainly designed for running optimizations. -Both dialects define various transformation passes. +Both dialects define various canonicalization and transformations that enable the compilation of quantum programs to native quantum hardware. For intercompatibility, we provide {doc}`conversions ` between dialects. -So far, this comprises a conversion from MQTOpt to MQTRef and one from MQTRef to MQTOpt. +So far, this comprises conversions between QC and QCO as well as from QC to QIR. ```{toctree} :maxdepth: 2 -MQTRef -MQTOpt +QC +QCO Conversions ``` @@ -27,100 +27,3 @@ The content is not yet complete and subject to change. Contributions are welcome. See the {doc}`contribution guide <../contributing>` for more information. ::: - -## Register Handling - -In MQT's MLIR dialects, quantum and classical registers are represented by MLIR-native `memref` operations rather than custom types. -This design choice offers several advantages: - -- **MLIR Integration**: Seamless compatibility with existing MLIR infrastructure, enabling reuse of memory handling patterns and optimization passes -- **Implementation Efficiency**: No need to define and maintain custom register operations, significantly reducing implementation complexity -- **Enhanced Interoperability**: Easier integration with other MLIR dialects and passes, allowing for more flexible compilation pipelines -- **Sustainable Evolution**: Standard memory operations can handle transformations while allowing new features without changing the fundamental register model - -### Quantum Register Representation - -A quantum register is represented by a `memref` of type `!mqtref.Qubit` or `!mqtopt.Qubit`, depending on the dialect: - -```mlir -// A quantum register with 2 qubits -%qreg = memref.alloc() : memref<2x!mqtref.Qubit> -``` - -Individual qubits are accessed through standard memory operations: - -```mlir -// Load qubits from the register -%q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> -%q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> -``` - -Here's a complete example of quantum register allocation, qubit access, and deallocation: - -```mlir -module { - func.func @main() attributes {passthrough = ["entry_point"]} { - %i1 = arith.constant 1 : index - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<2x!mqtref.Qubit> - %q0 = memref.load %qreg[%i0] : memref<2x!mqtref.Qubit> - %q1 = memref.load %qreg[%i1] : memref<2x!mqtref.Qubit> - memref.dealloc %qreg : memref<2x!mqtref.Qubit> - return - } -} -``` - -### Classical Register Representation - -Classical registers follow the same pattern as quantum registers but use the `i1` type for boolean measurement results: - -```mlir -// A classical register with 1 bit -%creg = memref.alloc() : memref<1xi1> -``` - -Measurement operations produce `i1` values that can be stored in classical registers: - -```mlir -// Measure a qubit and store the result -%c = mqtref.measure %q -memref.store %c, %creg[%i0] : memref<1xi1> -``` - -### Example: Full Quantum Program with Measurement - -Consider the following quantum computation represented in OpenQASM 3.0 code: - -```qasm3 -OPENQASM 3.0; -include "stdgates.inc"; -qubit[1] q; -bit[1] c; -x q[0]; -c[0] = measure q[0]; -``` - -In the MQTRef dialect, this corresponds to: - -```mlir -module { - func.func @main() attributes {passthrough = ["entry_point"]} { - %i0 = arith.constant 0 : index - %qreg = memref.alloc() : memref<1x!mqtref.Qubit> - %q = memref.load %qreg[%i0] : memref<1x!mqtref.Qubit> - %creg = memref.alloca() : memref<1xi1> - mqtref.x() %q - %c = mqtref.measure %q - memref.store %c, %creg[%i0] : memref<1xi1> - memref.dealloc %qreg : memref<1x!mqtref.Qubit> - return - } -} -``` - -## Development - -Building the MLIR library requires LLVM version 21.1 or later. -Our CI pipeline on GitHub continuously builds and tests the MLIR library on Linux, macOS, and Windows. -To access the latest build logs, visit the [GitHub Actions page](https://github.com/munich-quantum-toolkit/core/actions/workflows/ci.yml). From b322bf639ac0b47d38a33d665bd7a60a52265023 Mon Sep 17 00:00:00 2001 From: burgholzer Date: Thu, 8 Jan 2026 23:54:05 +0100 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=9A=B8=20add=20a=20dedicated=20step?= =?UTF-8?q?=20for=20building=20MLIR=20docs=20to=20nox=20docs=20job?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: burgholzer --- noxfile.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/noxfile.py b/noxfile.py index 6a5a0743b9..725697f4d3 100755 --- a/noxfile.py +++ b/noxfile.py @@ -186,6 +186,10 @@ def docs(session: nox.Session) -> None: external=True, ) + # build the MLIR API docs via building mlir-doc + session.run("uvx", "cmake", "-S", ".", "-B", "build", "-DBUILD_MQT_CORE_MLIR=ON") + session.run("uvx", "cmake", "--build", "build", "--target", "mlir-doc") + shared_args = [ "-n", # nitpicky mode "-T", # full tracebacks