From d0c370affbde8624f9cf2c5ae9a9073d5ae6dc81 Mon Sep 17 00:00:00 2001 From: "Kostrzewa, Niklas" Date: Mon, 9 Feb 2026 11:26:31 +0100 Subject: [PATCH 1/7] Missing file changes --- cmake/ExternalDependencies.cmake | 2 +- include/mlir/Conversion/CMakeLists.txt | 4 ++-- .../CatalystQuantumToMQTOpt/CMakeLists.txt | 13 ---------- .../CatalystQuantumToQC/CMakeLists.txt | 13 ++++++++++ .../CatalystQuantumToQC.h} | 8 +++---- .../CatalystQuantumToQC.td} | 8 +++---- lib/CMakeLists.txt | 5 ++-- lib/Conversion/CMakeLists.txt | 4 ++-- .../CMakeLists.txt | 24 +++++++++---------- .../CatalystQuantumToQC.cpp} | 12 +++++----- lib/mqt-plugin.cpp | 16 ++++++------- 11 files changed, 55 insertions(+), 54 deletions(-) delete mode 100644 include/mlir/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt create mode 100644 include/mlir/Conversion/CatalystQuantumToQC/CMakeLists.txt rename include/mlir/Conversion/{CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h => CatalystQuantumToQC/CatalystQuantumToQC.h} (61%) rename include/mlir/Conversion/{CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.td => CatalystQuantumToQC/CatalystQuantumToQC.td} (71%) rename lib/Conversion/{CatalystQuantumToMQTOpt => CatalystQuantumToQC}/CMakeLists.txt (62%) rename lib/Conversion/{CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.cpp => CatalystQuantumToQC/CatalystQuantumToQC.cpp} (98%) diff --git a/cmake/ExternalDependencies.cmake b/cmake/ExternalDependencies.cmake index f79dee4..4467352 100644 --- a/cmake/ExternalDependencies.cmake +++ b/cmake/ExternalDependencies.cmake @@ -15,7 +15,7 @@ set(MQT_CORE_MINIMUM_VERSION 3.4.0 CACHE STRING "MQT Core minimum version") set(MQT_CORE_VERSION 3.4.0 CACHE STRING "MQT Core version") -set(MQT_CORE_REV "6bcc01e7d135058c6439c64fdd5f14b65ab88816" +set(MQT_CORE_REV "main" CACHE STRING "MQT Core identifier (tag, branch or commit hash)") set(MQT_CORE_REPO_OWNER "munich-quantum-toolkit" CACHE STRING "MQT Core repository owner (change when using a fork)") diff --git a/include/mlir/Conversion/CMakeLists.txt b/include/mlir/Conversion/CMakeLists.txt index 20799e3..4634ee7 100644 --- a/include/mlir/Conversion/CMakeLists.txt +++ b/include/mlir/Conversion/CMakeLists.txt @@ -6,5 +6,5 @@ # # Licensed under the MIT License -add_subdirectory(MQTOptToCatalystQuantum) -add_subdirectory(CatalystQuantumToMQTOpt) +# add_subdirectory(MQTOptToCatalystQuantum) +add_subdirectory(CatalystQuantumToQC) diff --git a/include/mlir/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt b/include/mlir/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt deleted file mode 100644 index 0c2377b..0000000 --- a/include/mlir/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2025 - 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 CatalystQuantumToMQTOpt.td) -mlir_tablegen(CatalystQuantumToMQTOpt.h.inc -gen-pass-decls -name CatalystQuantumToMQTOpt) -add_public_tablegen_target(CatalystQuantumToMQTOptIncGen) - -add_mlir_doc(CatalystQuantumToMQTOpt CatalystQuantumToMQTOpt ./ -gen-pass-doc) diff --git a/include/mlir/Conversion/CatalystQuantumToQC/CMakeLists.txt b/include/mlir/Conversion/CatalystQuantumToQC/CMakeLists.txt new file mode 100644 index 0000000..e382460 --- /dev/null +++ b/include/mlir/Conversion/CatalystQuantumToQC/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2025 - 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 CatalystQuantumToQC.td) +mlir_tablegen(CatalystQuantumToQC.h.inc -gen-pass-decls -name CatalystQuantumToQC) +add_public_tablegen_target(CatalystQuantumToQCIncGen) + +add_mlir_doc(CatalystQuantumToQC CatalystQuantumToQC ./ -gen-pass-doc) diff --git a/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h similarity index 61% rename from include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h rename to include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h index 74b0c5e..35a305f 100644 --- a/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h +++ b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h @@ -12,12 +12,12 @@ #include // NOLINT(misc-include-cleaner) -namespace mqt::ir::conversions { +namespace mlir { #define GEN_PASS_DECL -#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h.inc" +#include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h.inc" #define GEN_PASS_REGISTRATION -#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h.inc" +#include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h.inc" -} // namespace mqt::ir::conversions +} // namespace mlir diff --git a/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.td b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td similarity index 71% rename from include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.td rename to include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td index 4f148c0..31cf320 100644 --- a/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.td +++ b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td @@ -8,11 +8,11 @@ include "mlir/Pass/PassBase.td" -def CatalystQuantumToMQTOpt : Pass<"catalystquantum-to-mqtopt"> { - let summary = "Convert Catalyst's `Quantum` to MQT's `MQTOpt` dialect."; +def CatalystQuantumToQC : Pass<"catalystquantum-to-qc"> { + let summary = "Convert Catalyst's `Quantum` to MQT's `QC` dialect."; let description = [{ - This pass converts Catalyst's `Quantum` to MQT's `MQTOpt` dialect. + This pass converts Catalyst's `Quantum` to MQT's `QC` dialect. The following operations are currently NOT converted (and instead marked legal): - DeviceInitOp - DeviceReleaseOp @@ -27,6 +27,6 @@ def CatalystQuantumToMQTOpt : Pass<"catalystquantum-to-mqtopt"> { // Define dependent dialects let dependentDialects = [ "catalyst::quantum::QuantumDialect", - "::mqt::ir::opt::MQTOptDialect" + "::mlir::qc" ]; } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 98443bc..96cbcb8 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -22,8 +22,9 @@ add_subdirectory(Conversion) set(TARGET_NAME mqt-core-plugins-catalyst) -add_llvm_library(${TARGET_NAME} MODULE mqt-plugin.cpp LINK_LIBS CatalystQuantumToMQTOpt - MQTOptToCatalystQuantum) +add_llvm_library( + ${TARGET_NAME} MODULE mqt-plugin.cpp LINK_LIBS CatalystQuantumToQC # MQTOptToCatalystQuantum +) # set required C++ standard target_compile_features(${TARGET_NAME} PUBLIC cxx_std_20) diff --git a/lib/Conversion/CMakeLists.txt b/lib/Conversion/CMakeLists.txt index 20799e3..4634ee7 100644 --- a/lib/Conversion/CMakeLists.txt +++ b/lib/Conversion/CMakeLists.txt @@ -6,5 +6,5 @@ # # Licensed under the MIT License -add_subdirectory(MQTOptToCatalystQuantum) -add_subdirectory(CatalystQuantumToMQTOpt) +# add_subdirectory(MQTOptToCatalystQuantum) +add_subdirectory(CatalystQuantumToQC) diff --git a/lib/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt b/lib/Conversion/CatalystQuantumToQC/CMakeLists.txt similarity index 62% rename from lib/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt rename to lib/Conversion/CatalystQuantumToQC/CMakeLists.txt index f293f8c..99e94f3 100644 --- a/lib/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt +++ b/lib/Conversion/CatalystQuantumToQC/CMakeLists.txt @@ -7,27 +7,27 @@ # Licensed under the MIT License add_mlir_library( - CatalystQuantumToMQTOpt - CatalystQuantumToMQTOpt.cpp + CatalystQuantumToQC + CatalystQuantumToQC.cpp LINK_LIBS - MLIRMQTOpt + MLIRQC MLIRTransforms MLIRFuncDialect DEPENDS - CatalystQuantumToMQTOptIncGen) + CatalystQuantumToQCIncGen) -target_compile_features(CatalystQuantumToMQTOpt PUBLIC cxx_std_20) -target_compile_options(CatalystQuantumToMQTOpt PRIVATE -fexceptions) -target_include_directories(CatalystQuantumToMQTOpt PRIVATE ${CATALYST_INCLUDE_DIRS}) +target_compile_features(CatalystQuantumToQC PUBLIC cxx_std_20) +target_compile_options(CatalystQuantumToQC PRIVATE -fexceptions) +target_include_directories(CatalystQuantumToQC PRIVATE ${CATALYST_INCLUDE_DIRS}) file(GLOB_RECURSE CONVERSION_HEADERS_SOURCE - "${MQT_MLIR_PLUGIN_SOURCE_INCLUDE_DIR}/mlir/Conversion/CatalystQuantumToMQTOpt/*.h") + "${MQT_MLIR_PLUGIN_SOURCE_INCLUDE_DIR}/mlir/Conversion/CatalystQuantumToQC/*.h") file(GLOB_RECURSE CONVERSION_HEADERS_BUILD - "${MQT_MLIR_PLUGIN_BUILD_INCLUDE_DIR}/mlir/Conversion/CatalystQuantumToMQTOpt/*.inc") + "${MQT_MLIR_PLUGIN_BUILD_INCLUDE_DIR}/mlir/Conversion/CatalystQuantumToQC/*.inc") # add public headers using file sets target_sources( - CatalystQuantumToMQTOpt + CatalystQuantumToQC PUBLIC FILE_SET HEADERS BASE_DIRS @@ -42,6 +42,6 @@ target_sources( ${CONVERSION_HEADERS_BUILD}) if(ENABLE_COVERAGE) - target_compile_options(CatalystQuantumToMQTOpt PRIVATE --coverage -O0) - target_link_options(CatalystQuantumToMQTOpt PRIVATE --coverage) + target_compile_options(CatalystQuantumToQC PRIVATE --coverage -O0) + target_link_options(CatalystQuantumToQC PRIVATE --coverage) endif() diff --git a/lib/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.cpp b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp similarity index 98% rename from lib/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.cpp rename to lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp index 93d4b12..1b7f4fe 100644 --- a/lib/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.cpp +++ b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp @@ -8,9 +8,9 @@ * Licensed under the MIT License */ -#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h" // NOLINT(misc-include-cleaner) +#include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h" // NOLINT(misc-include-cleaner) -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" +#include "mlir/Dialect/QC/IR/QCDialect.h" #include #include @@ -38,10 +38,10 @@ #include #include -namespace mqt::ir::conversions { +namespace mlir { -#define GEN_PASS_DEF_CATALYSTQUANTUMTOMQTOPT -#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h.inc" +#define GEN_PASS_DEF_CATALYSTQUANTUMTOQC +#include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h.inc" using namespace mlir; using namespace mlir::arith; @@ -835,4 +835,4 @@ struct CatalystQuantumToMQTOpt final } }; -} // namespace mqt::ir::conversions +} // namespace mlir diff --git a/lib/mqt-plugin.cpp b/lib/mqt-plugin.cpp index de553d9..3cab6a3 100644 --- a/lib/mqt-plugin.cpp +++ b/lib/mqt-plugin.cpp @@ -8,9 +8,9 @@ * Licensed under the MIT License */ -#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h" -#include "mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h" -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" +#include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h" +// #include "mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h" +#include "mlir/Dialect/QC/IR/QCDialect.h" #include #include @@ -26,10 +26,10 @@ using namespace mlir; extern "C" LLVM_ATTRIBUTE_WEAK DialectPluginLibraryInfo mlirGetDialectPluginInfo() { return {.apiVersion = MLIR_PLUGIN_API_VERSION, - .pluginName = "MQTOpt", + .pluginName = "QC", .pluginVersion = LLVM_VERSION_STRING, .registerDialectRegistryCallbacks = [](DialectRegistry* registry) { - registry->insert<::mqt::ir::opt::MQTOptDialect>(); + registry->insert<::mlir::qc>(); }}; } @@ -37,13 +37,13 @@ mlirGetDialectPluginInfo() { /// Necessary symbol to register the pass plugin. extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo mlirGetPassPluginInfo() { return {.apiVersion = MLIR_PLUGIN_API_VERSION, - .pluginName = "MQTOptPasses", + .pluginName = "QCPasses", .pluginVersion = LLVM_VERSION_STRING, .registerPassRegistryCallbacks = []() { // Only register the conversion passes we implement // Note: mqt::ir::opt::registerMQTOptPasses() is not called to avoid // pulling in transpilation transforms that require LLVM 21 - mqt::ir::conversions::registerCatalystQuantumToMQTOptPasses(); - mqt::ir::conversions::registerMQTOptToCatalystQuantumPasses(); + mlir::registerCatalystQuantumToQCPasses(); + // mqt::ir::conversions::registerMQTOptToCatalystQuantumPasses(); }}; } From c997a9fb6eb971b847effa59b46310e7a00a88ad Mon Sep 17 00:00:00 2001 From: "Kostrzewa, Niklas" Date: Mon, 9 Feb 2026 11:26:31 +0100 Subject: [PATCH 2/7] Change names in and of files for quantum->qc Conversion --- cmake/ExternalDependencies.cmake | 2 +- include/mlir/Conversion/CMakeLists.txt | 4 ++-- .../CatalystQuantumToMQTOpt/CMakeLists.txt | 13 ---------- .../CatalystQuantumToQC/CMakeLists.txt | 13 ++++++++++ .../CatalystQuantumToQC.h} | 8 +++---- .../CatalystQuantumToQC.td} | 8 +++---- lib/CMakeLists.txt | 5 ++-- lib/Conversion/CMakeLists.txt | 4 ++-- .../CMakeLists.txt | 24 +++++++++---------- .../CatalystQuantumToQC.cpp} | 12 +++++----- lib/mqt-plugin.cpp | 16 ++++++------- 11 files changed, 55 insertions(+), 54 deletions(-) delete mode 100644 include/mlir/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt create mode 100644 include/mlir/Conversion/CatalystQuantumToQC/CMakeLists.txt rename include/mlir/Conversion/{CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h => CatalystQuantumToQC/CatalystQuantumToQC.h} (61%) rename include/mlir/Conversion/{CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.td => CatalystQuantumToQC/CatalystQuantumToQC.td} (71%) rename lib/Conversion/{CatalystQuantumToMQTOpt => CatalystQuantumToQC}/CMakeLists.txt (62%) rename lib/Conversion/{CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.cpp => CatalystQuantumToQC/CatalystQuantumToQC.cpp} (98%) diff --git a/cmake/ExternalDependencies.cmake b/cmake/ExternalDependencies.cmake index f79dee4..4467352 100644 --- a/cmake/ExternalDependencies.cmake +++ b/cmake/ExternalDependencies.cmake @@ -15,7 +15,7 @@ set(MQT_CORE_MINIMUM_VERSION 3.4.0 CACHE STRING "MQT Core minimum version") set(MQT_CORE_VERSION 3.4.0 CACHE STRING "MQT Core version") -set(MQT_CORE_REV "6bcc01e7d135058c6439c64fdd5f14b65ab88816" +set(MQT_CORE_REV "main" CACHE STRING "MQT Core identifier (tag, branch or commit hash)") set(MQT_CORE_REPO_OWNER "munich-quantum-toolkit" CACHE STRING "MQT Core repository owner (change when using a fork)") diff --git a/include/mlir/Conversion/CMakeLists.txt b/include/mlir/Conversion/CMakeLists.txt index 20799e3..4634ee7 100644 --- a/include/mlir/Conversion/CMakeLists.txt +++ b/include/mlir/Conversion/CMakeLists.txt @@ -6,5 +6,5 @@ # # Licensed under the MIT License -add_subdirectory(MQTOptToCatalystQuantum) -add_subdirectory(CatalystQuantumToMQTOpt) +# add_subdirectory(MQTOptToCatalystQuantum) +add_subdirectory(CatalystQuantumToQC) diff --git a/include/mlir/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt b/include/mlir/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt deleted file mode 100644 index 0c2377b..0000000 --- a/include/mlir/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2025 - 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 CatalystQuantumToMQTOpt.td) -mlir_tablegen(CatalystQuantumToMQTOpt.h.inc -gen-pass-decls -name CatalystQuantumToMQTOpt) -add_public_tablegen_target(CatalystQuantumToMQTOptIncGen) - -add_mlir_doc(CatalystQuantumToMQTOpt CatalystQuantumToMQTOpt ./ -gen-pass-doc) diff --git a/include/mlir/Conversion/CatalystQuantumToQC/CMakeLists.txt b/include/mlir/Conversion/CatalystQuantumToQC/CMakeLists.txt new file mode 100644 index 0000000..e382460 --- /dev/null +++ b/include/mlir/Conversion/CatalystQuantumToQC/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2025 - 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 CatalystQuantumToQC.td) +mlir_tablegen(CatalystQuantumToQC.h.inc -gen-pass-decls -name CatalystQuantumToQC) +add_public_tablegen_target(CatalystQuantumToQCIncGen) + +add_mlir_doc(CatalystQuantumToQC CatalystQuantumToQC ./ -gen-pass-doc) diff --git a/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h similarity index 61% rename from include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h rename to include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h index 74b0c5e..35a305f 100644 --- a/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h +++ b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h @@ -12,12 +12,12 @@ #include // NOLINT(misc-include-cleaner) -namespace mqt::ir::conversions { +namespace mlir { #define GEN_PASS_DECL -#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h.inc" +#include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h.inc" #define GEN_PASS_REGISTRATION -#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h.inc" +#include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h.inc" -} // namespace mqt::ir::conversions +} // namespace mlir diff --git a/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.td b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td similarity index 71% rename from include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.td rename to include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td index 4f148c0..31cf320 100644 --- a/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.td +++ b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td @@ -8,11 +8,11 @@ include "mlir/Pass/PassBase.td" -def CatalystQuantumToMQTOpt : Pass<"catalystquantum-to-mqtopt"> { - let summary = "Convert Catalyst's `Quantum` to MQT's `MQTOpt` dialect."; +def CatalystQuantumToQC : Pass<"catalystquantum-to-qc"> { + let summary = "Convert Catalyst's `Quantum` to MQT's `QC` dialect."; let description = [{ - This pass converts Catalyst's `Quantum` to MQT's `MQTOpt` dialect. + This pass converts Catalyst's `Quantum` to MQT's `QC` dialect. The following operations are currently NOT converted (and instead marked legal): - DeviceInitOp - DeviceReleaseOp @@ -27,6 +27,6 @@ def CatalystQuantumToMQTOpt : Pass<"catalystquantum-to-mqtopt"> { // Define dependent dialects let dependentDialects = [ "catalyst::quantum::QuantumDialect", - "::mqt::ir::opt::MQTOptDialect" + "::mlir::qc" ]; } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 98443bc..96cbcb8 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -22,8 +22,9 @@ add_subdirectory(Conversion) set(TARGET_NAME mqt-core-plugins-catalyst) -add_llvm_library(${TARGET_NAME} MODULE mqt-plugin.cpp LINK_LIBS CatalystQuantumToMQTOpt - MQTOptToCatalystQuantum) +add_llvm_library( + ${TARGET_NAME} MODULE mqt-plugin.cpp LINK_LIBS CatalystQuantumToQC # MQTOptToCatalystQuantum +) # set required C++ standard target_compile_features(${TARGET_NAME} PUBLIC cxx_std_20) diff --git a/lib/Conversion/CMakeLists.txt b/lib/Conversion/CMakeLists.txt index 20799e3..4634ee7 100644 --- a/lib/Conversion/CMakeLists.txt +++ b/lib/Conversion/CMakeLists.txt @@ -6,5 +6,5 @@ # # Licensed under the MIT License -add_subdirectory(MQTOptToCatalystQuantum) -add_subdirectory(CatalystQuantumToMQTOpt) +# add_subdirectory(MQTOptToCatalystQuantum) +add_subdirectory(CatalystQuantumToQC) diff --git a/lib/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt b/lib/Conversion/CatalystQuantumToQC/CMakeLists.txt similarity index 62% rename from lib/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt rename to lib/Conversion/CatalystQuantumToQC/CMakeLists.txt index f293f8c..99e94f3 100644 --- a/lib/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt +++ b/lib/Conversion/CatalystQuantumToQC/CMakeLists.txt @@ -7,27 +7,27 @@ # Licensed under the MIT License add_mlir_library( - CatalystQuantumToMQTOpt - CatalystQuantumToMQTOpt.cpp + CatalystQuantumToQC + CatalystQuantumToQC.cpp LINK_LIBS - MLIRMQTOpt + MLIRQC MLIRTransforms MLIRFuncDialect DEPENDS - CatalystQuantumToMQTOptIncGen) + CatalystQuantumToQCIncGen) -target_compile_features(CatalystQuantumToMQTOpt PUBLIC cxx_std_20) -target_compile_options(CatalystQuantumToMQTOpt PRIVATE -fexceptions) -target_include_directories(CatalystQuantumToMQTOpt PRIVATE ${CATALYST_INCLUDE_DIRS}) +target_compile_features(CatalystQuantumToQC PUBLIC cxx_std_20) +target_compile_options(CatalystQuantumToQC PRIVATE -fexceptions) +target_include_directories(CatalystQuantumToQC PRIVATE ${CATALYST_INCLUDE_DIRS}) file(GLOB_RECURSE CONVERSION_HEADERS_SOURCE - "${MQT_MLIR_PLUGIN_SOURCE_INCLUDE_DIR}/mlir/Conversion/CatalystQuantumToMQTOpt/*.h") + "${MQT_MLIR_PLUGIN_SOURCE_INCLUDE_DIR}/mlir/Conversion/CatalystQuantumToQC/*.h") file(GLOB_RECURSE CONVERSION_HEADERS_BUILD - "${MQT_MLIR_PLUGIN_BUILD_INCLUDE_DIR}/mlir/Conversion/CatalystQuantumToMQTOpt/*.inc") + "${MQT_MLIR_PLUGIN_BUILD_INCLUDE_DIR}/mlir/Conversion/CatalystQuantumToQC/*.inc") # add public headers using file sets target_sources( - CatalystQuantumToMQTOpt + CatalystQuantumToQC PUBLIC FILE_SET HEADERS BASE_DIRS @@ -42,6 +42,6 @@ target_sources( ${CONVERSION_HEADERS_BUILD}) if(ENABLE_COVERAGE) - target_compile_options(CatalystQuantumToMQTOpt PRIVATE --coverage -O0) - target_link_options(CatalystQuantumToMQTOpt PRIVATE --coverage) + target_compile_options(CatalystQuantumToQC PRIVATE --coverage -O0) + target_link_options(CatalystQuantumToQC PRIVATE --coverage) endif() diff --git a/lib/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.cpp b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp similarity index 98% rename from lib/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.cpp rename to lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp index 93d4b12..1b7f4fe 100644 --- a/lib/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.cpp +++ b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp @@ -8,9 +8,9 @@ * Licensed under the MIT License */ -#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h" // NOLINT(misc-include-cleaner) +#include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h" // NOLINT(misc-include-cleaner) -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" +#include "mlir/Dialect/QC/IR/QCDialect.h" #include #include @@ -38,10 +38,10 @@ #include #include -namespace mqt::ir::conversions { +namespace mlir { -#define GEN_PASS_DEF_CATALYSTQUANTUMTOMQTOPT -#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h.inc" +#define GEN_PASS_DEF_CATALYSTQUANTUMTOQC +#include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h.inc" using namespace mlir; using namespace mlir::arith; @@ -835,4 +835,4 @@ struct CatalystQuantumToMQTOpt final } }; -} // namespace mqt::ir::conversions +} // namespace mlir diff --git a/lib/mqt-plugin.cpp b/lib/mqt-plugin.cpp index de553d9..3cab6a3 100644 --- a/lib/mqt-plugin.cpp +++ b/lib/mqt-plugin.cpp @@ -8,9 +8,9 @@ * Licensed under the MIT License */ -#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h" -#include "mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h" -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" +#include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h" +// #include "mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h" +#include "mlir/Dialect/QC/IR/QCDialect.h" #include #include @@ -26,10 +26,10 @@ using namespace mlir; extern "C" LLVM_ATTRIBUTE_WEAK DialectPluginLibraryInfo mlirGetDialectPluginInfo() { return {.apiVersion = MLIR_PLUGIN_API_VERSION, - .pluginName = "MQTOpt", + .pluginName = "QC", .pluginVersion = LLVM_VERSION_STRING, .registerDialectRegistryCallbacks = [](DialectRegistry* registry) { - registry->insert<::mqt::ir::opt::MQTOptDialect>(); + registry->insert<::mlir::qc>(); }}; } @@ -37,13 +37,13 @@ mlirGetDialectPluginInfo() { /// Necessary symbol to register the pass plugin. extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo mlirGetPassPluginInfo() { return {.apiVersion = MLIR_PLUGIN_API_VERSION, - .pluginName = "MQTOptPasses", + .pluginName = "QCPasses", .pluginVersion = LLVM_VERSION_STRING, .registerPassRegistryCallbacks = []() { // Only register the conversion passes we implement // Note: mqt::ir::opt::registerMQTOptPasses() is not called to avoid // pulling in transpilation transforms that require LLVM 21 - mqt::ir::conversions::registerCatalystQuantumToMQTOptPasses(); - mqt::ir::conversions::registerMQTOptToCatalystQuantumPasses(); + mlir::registerCatalystQuantumToQCPasses(); + // mqt::ir::conversions::registerMQTOptToCatalystQuantumPasses(); }}; } From 64364f699cd407ff6c62cb729a1cc18dd3ce3a18 Mon Sep 17 00:00:00 2001 From: "Kostrzewa, Niklas" Date: Wed, 11 Feb 2026 13:55:51 +0100 Subject: [PATCH 3/7] Update files, see changes below cmake/ExternalDependencies.cmake MQT_CORE_REV -> "main" include/mlir/Conversion Filename CatalystQuantumToQC -> Changed in CMakeLists.txt include/mlir/Conversion/CatalystQuantumToQC Filenames to QC include/mlir/Conversion/CatalystQuantumToQC/CMakeLists.txt MQTOpt replaced by QC include/mlir/Conversion/CatalystQuantumToQC/.h MQTOpt replaced by QC include/mlir/Conversion/CatalystQuantumToQC/.td MQTOpt replaced by QC namespace dependent dialects: ::mlir::qc lib/CMakelists.txt MQTOpt replaced by QC lib/mqt-plugin.cpp #includes exclude QCToCatalystQuantum, MQTOpt replaced by QC change register namespaces to ::mlir:: lib/Conversion Filename CatalystQuantumToQC -> changed also in CMakeLists.txt lib/Conversion/CatalystQuantumToQC/CMakeLists.txt MLIRQCDialect in add_mlir_library MQTOpt replaced by QC lib/Conversion/CatalystQuantumToQC/.cpp MQTOpt replaced by QC --- include/mlir/Conversion/CMakeLists.txt | 2 +- .../CatalystQuantumToQC/CatalystQuantumToQC.h | 4 +- .../CatalystQuantumToQC.td | 2 +- lib/CMakeLists.txt | 5 +- lib/Conversion/CMakeLists.txt | 2 +- .../CatalystQuantumToQC/CMakeLists.txt | 2 +- .../CatalystQuantumToQC.cpp | 84 +++++++++---------- lib/mqt-plugin.cpp | 10 +-- 8 files changed, 55 insertions(+), 56 deletions(-) diff --git a/include/mlir/Conversion/CMakeLists.txt b/include/mlir/Conversion/CMakeLists.txt index 4634ee7..062917c 100644 --- a/include/mlir/Conversion/CMakeLists.txt +++ b/include/mlir/Conversion/CMakeLists.txt @@ -6,5 +6,5 @@ # # Licensed under the MIT License -# add_subdirectory(MQTOptToCatalystQuantum) +add_subdirectory(MQTOptToCatalystQuantum) add_subdirectory(CatalystQuantumToQC) diff --git a/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h index 35a305f..698e3fd 100644 --- a/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h +++ b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h @@ -12,7 +12,7 @@ #include // NOLINT(misc-include-cleaner) -namespace mlir { +namespace mqt::ir::conversions { #define GEN_PASS_DECL #include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h.inc" @@ -20,4 +20,4 @@ namespace mlir { #define GEN_PASS_REGISTRATION #include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h.inc" -} // namespace mlir +} // namespace mqt::ir::conversions diff --git a/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td index 31cf320..aed4f0d 100644 --- a/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td +++ b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td @@ -8,7 +8,7 @@ include "mlir/Pass/PassBase.td" -def CatalystQuantumToQC : Pass<"catalystquantum-to-qc"> { +def CatalystQuantumToMQTOpt : Pass<"catalystquantum-to-qc"> { let summary = "Convert Catalyst's `Quantum` to MQT's `QC` dialect."; let description = [{ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 96cbcb8..37f4394 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -22,9 +22,8 @@ add_subdirectory(Conversion) set(TARGET_NAME mqt-core-plugins-catalyst) -add_llvm_library( - ${TARGET_NAME} MODULE mqt-plugin.cpp LINK_LIBS CatalystQuantumToQC # MQTOptToCatalystQuantum -) +add_llvm_library(${TARGET_NAME} MODULE mqt-plugin.cpp LINK_LIBS CatalystQuantumToQC + QCToCatalystQuantum) # set required C++ standard target_compile_features(${TARGET_NAME} PUBLIC cxx_std_20) diff --git a/lib/Conversion/CMakeLists.txt b/lib/Conversion/CMakeLists.txt index 4634ee7..062917c 100644 --- a/lib/Conversion/CMakeLists.txt +++ b/lib/Conversion/CMakeLists.txt @@ -6,5 +6,5 @@ # # Licensed under the MIT License -# add_subdirectory(MQTOptToCatalystQuantum) +add_subdirectory(MQTOptToCatalystQuantum) add_subdirectory(CatalystQuantumToQC) diff --git a/lib/Conversion/CatalystQuantumToQC/CMakeLists.txt b/lib/Conversion/CatalystQuantumToQC/CMakeLists.txt index 99e94f3..e422729 100644 --- a/lib/Conversion/CatalystQuantumToQC/CMakeLists.txt +++ b/lib/Conversion/CatalystQuantumToQC/CMakeLists.txt @@ -10,7 +10,7 @@ add_mlir_library( CatalystQuantumToQC CatalystQuantumToQC.cpp LINK_LIBS - MLIRQC + MLIRQCDialect MLIRTransforms MLIRFuncDialect DEPENDS diff --git a/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp index 1b7f4fe..63cc9b9 100644 --- a/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp +++ b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp @@ -38,7 +38,7 @@ #include #include -namespace mlir { +namespace mqt::ir::conversions { #define GEN_PASS_DEF_CATALYSTQUANTUMTOQC #include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h.inc" @@ -178,13 +178,13 @@ FailureOr processParameters(catalyst::quantum::CustomOp op, } // namespace -class CatalystQuantumToMQTOptTypeConverter final : public TypeConverter { +class CatalystQuantumToQCTypeConverter final : public TypeConverter { public: - explicit CatalystQuantumToMQTOptTypeConverter(MLIRContext* ctx) { + explicit CatalystQuantumToQCTypeConverter(MLIRContext* ctx) { // Identity conversion: Allow all types to pass through unmodified if needed addConversion([](const Type type) { return type; }); - // Convert Catalyst QubitType to MQTOpt QubitType + // Convert Catalyst QubitType to QC QubitType addConversion([ctx](catalyst::quantum::QubitType /*type*/) -> Type { return opt::QubitType::get(ctx); }); @@ -296,12 +296,12 @@ struct ConvertQuantumMeasure final // Create the new operation // Note: quantum.measure returns (i1, !quantum.bit) - // mqtopt.measure returns (!mqtopt.Qubit, i1) - auto mqtoptOp = rewriter.create(op.getLoc(), qubitType, - bitType, inQubit); + // QC.measure returns (!QC.Qubit, i1) + auto QCOp = rewriter.create(op.getLoc(), qubitType, bitType, + inQubit); // Replace with results in the correct order - rewriter.replaceOp(op, {mqtoptOp.getResult(1), mqtoptOp.getResult(0)}); + rewriter.replaceOp(op, {QCOp.getResult(1), QCOp.getResult(0)}); return success(); } }; @@ -456,7 +456,7 @@ struct ConvertQuantumGlobalPhase final DenseBoolArrayAttr::get(rewriter.getContext(), paramsMaskVec); // Create the new operation - auto mqtoptOp = rewriter.create( + auto QCOp = rewriter.create( op.getLoc(), TypeRange{}, // out_qubits ValueRange(inPosCtrlQubitsVec).getTypes(), // pos_ctrl_out_qubits ValueRange(inNegCtrlQubitsVec).getTypes(), // neg_ctrl_out_qubits @@ -466,7 +466,7 @@ struct ConvertQuantumGlobalPhase final ValueRange(inNegCtrlQubitsVec)); // neg_ctrl_in_qubits // Replace the original with the new operation - rewriter.replaceOp(op, mqtoptOp); + rewriter.replaceOp(op, QCOp); return success(); } }; @@ -510,7 +510,7 @@ struct ConvertQuantumCustomOp final DenseBoolArrayAttr::get(rewriter.getContext(), paramInfo.paramsMask); // Create the new operation - Operation* mqtoptOp = nullptr; + Operation* QCOp = nullptr; #define CREATE_GATE_OP(GATE_TYPE) \ rewriter.create( \ @@ -521,51 +521,51 @@ struct ConvertQuantumCustomOp final additionalNegCtrlQubits) if (gateName == "Hadamard") { - mqtoptOp = CREATE_GATE_OP(H); + QCOp = CREATE_GATE_OP(H); } else if (gateName == "Identity") { - mqtoptOp = CREATE_GATE_OP(I); + QCOp = CREATE_GATE_OP(I); } else if (gateName == "PauliX") { - mqtoptOp = CREATE_GATE_OP(X); + QCOp = CREATE_GATE_OP(X); } else if (gateName == "PauliY") { - mqtoptOp = CREATE_GATE_OP(Y); + QCOp = CREATE_GATE_OP(Y); } else if (gateName == "PauliZ") { - mqtoptOp = CREATE_GATE_OP(Z); + QCOp = CREATE_GATE_OP(Z); } else if (gateName == "S") { if (op.getAdjoint()) { - mqtoptOp = CREATE_GATE_OP(Sdg); + QCOp = CREATE_GATE_OP(Sdg); } else { - mqtoptOp = CREATE_GATE_OP(S); + QCOp = CREATE_GATE_OP(S); } } else if (gateName == "T") { if (op.getAdjoint()) { - mqtoptOp = CREATE_GATE_OP(Tdg); + QCOp = CREATE_GATE_OP(Tdg); } else { - mqtoptOp = CREATE_GATE_OP(T); + QCOp = CREATE_GATE_OP(T); } } else if (gateName == "SX") { if (op.getAdjoint()) { - mqtoptOp = CREATE_GATE_OP(SXdg); + QCOp = CREATE_GATE_OP(SXdg); } else { - mqtoptOp = CREATE_GATE_OP(SX); + QCOp = CREATE_GATE_OP(SX); } } else if (gateName == "ECR") { - mqtoptOp = CREATE_GATE_OP(ECR); + QCOp = CREATE_GATE_OP(ECR); } else if (gateName == "SWAP") { - mqtoptOp = CREATE_GATE_OP(SWAP); + QCOp = CREATE_GATE_OP(SWAP); } else if (gateName == "ISWAP") { if (op.getAdjoint()) { - mqtoptOp = CREATE_GATE_OP(iSWAPdg); + QCOp = CREATE_GATE_OP(iSWAPdg); } else { - mqtoptOp = CREATE_GATE_OP(iSWAP); + QCOp = CREATE_GATE_OP(iSWAP); } } else if (gateName == "RX") { - mqtoptOp = CREATE_GATE_OP(RX); + QCOp = CREATE_GATE_OP(RX); } else if (gateName == "RY") { - mqtoptOp = CREATE_GATE_OP(RY); + QCOp = CREATE_GATE_OP(RY); } else if (gateName == "RZ") { - mqtoptOp = CREATE_GATE_OP(RZ); + QCOp = CREATE_GATE_OP(RZ); } else if (gateName == "PhaseShift") { - mqtoptOp = CREATE_GATE_OP(P); + QCOp = CREATE_GATE_OP(P); } else if (gateName == "CRX") { auto controlLists = buildControlLists( inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), @@ -639,18 +639,18 @@ struct ConvertQuantumCustomOp final auto isingxyParamsMaskAttr = DenseBoolArrayAttr::get(rewriter.getContext(), isingxyParamsMask); - mqtoptOp = rewriter.create( + QCOp = rewriter.create( op.getLoc(), inQubits.getTypes(), ValueRange(additionalPosCtrlQubits).getTypes(), ValueRange(additionalNegCtrlQubits).getTypes(), isingxyStaticParamsAttr, isingxyParamsMaskAttr, finalParamValues, inQubits, additionalPosCtrlQubits, additionalNegCtrlQubits); } else if (gateName == "IsingXX") { - mqtoptOp = CREATE_GATE_OP(RXX); + QCOp = CREATE_GATE_OP(RXX); } else if (gateName == "IsingYY") { - mqtoptOp = CREATE_GATE_OP(RYY); + QCOp = CREATE_GATE_OP(RYY); } else if (gateName == "IsingZZ") { - mqtoptOp = CREATE_GATE_OP(RZZ); + QCOp = CREATE_GATE_OP(RZZ); } else if (gateName == "CNOT") { auto controlLists = buildControlLists( inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), @@ -722,7 +722,7 @@ struct ConvertQuantumCustomOp final staticParams, paramsMask, finalParamValues, ValueRange{inQubits[1], inQubits[2]}, ctrls, negCtrls); - // MQTOpt SWAP returns (target0, target1, ctrl0, ctrl1, ...) + // QC SWAP returns (target0, target1, ctrl0, ctrl1, ...) // Catalyst CSWAP expects (ctrl0, ctrl1, ..., target0, target1) SmallVector results; const size_t numTargets = 2; @@ -745,21 +745,21 @@ struct ConvertQuantumCustomOp final #undef CREATE_GATE_OP // Replace the original with the new operation - rewriter.replaceOp(op, mqtoptOp); + rewriter.replaceOp(op, QCOp); return success(); } }; -struct CatalystQuantumToMQTOpt final - : impl::CatalystQuantumToMQTOptBase { - using CatalystQuantumToMQTOptBase::CatalystQuantumToMQTOptBase; +struct CatalystQuantumToQC final + : impl::CatalystQuantumToQCBase { + using CatalystQuantumToQCBase::CatalystQuantumToQCBase; void runOnOperation() override { MLIRContext* context = &getContext(); auto* module = getOperation(); ConversionTarget target(*context); - target.addLegalDialect(); + target.addLegalDialect(); target.addLegalDialect(); target.addLegalDialect(); target.addIllegalDialect(); @@ -771,7 +771,7 @@ struct CatalystQuantumToMQTOpt final catalyst::quantum::FinalizeOp, catalyst::quantum::ComputationalBasisOp, catalyst::quantum::StateOp, catalyst::quantum::InitializeOp>(); - const CatalystQuantumToMQTOptTypeConverter typeConverter(context); + const CatalystQuantumToQCTypeConverter typeConverter(context); RewritePatternSet patterns(context); patterns @@ -835,4 +835,4 @@ struct CatalystQuantumToMQTOpt final } }; -} // namespace mlir +} // namespace mqt::ir::conversions diff --git a/lib/mqt-plugin.cpp b/lib/mqt-plugin.cpp index 3cab6a3..9bba78d 100644 --- a/lib/mqt-plugin.cpp +++ b/lib/mqt-plugin.cpp @@ -9,7 +9,7 @@ */ #include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h" -// #include "mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h" +// #include "mlir/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include @@ -29,7 +29,7 @@ mlirGetDialectPluginInfo() { .pluginName = "QC", .pluginVersion = LLVM_VERSION_STRING, .registerDialectRegistryCallbacks = [](DialectRegistry* registry) { - registry->insert<::mlir::qc>(); + registry->insert<::mlir::QCDialect>(); }}; } @@ -41,9 +41,9 @@ extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo mlirGetPassPluginInfo() { .pluginVersion = LLVM_VERSION_STRING, .registerPassRegistryCallbacks = []() { // Only register the conversion passes we implement - // Note: mqt::ir::opt::registerMQTOptPasses() is not called to avoid + // Note: mqt::ir::opt::registerQCPasses() is not called to avoid // pulling in transpilation transforms that require LLVM 21 - mlir::registerCatalystQuantumToQCPasses(); - // mqt::ir::conversions::registerMQTOptToCatalystQuantumPasses(); + // mqt::ir::conversions::registerCatalystQuantumToQCPasses(); + mqt::ir::conversions::registerQCToCatalystQuantumPasses(); }}; } From 82bbf3400ebbb8d37cd85ca3ab9be204cd714aa3 Mon Sep 17 00:00:00 2001 From: "Kostrzewa, Niklas" Date: Mon, 16 Feb 2026 08:39:22 +0100 Subject: [PATCH 4/7] Added Type, Alloc, DeAlloc, Measure, Extract, Insert, CustomGates (based on quantum -> MQTOpt conversion) --- lib/CMakeLists.txt | 6 +- .../CatalystQuantumToQC.cpp | 193 +++--------------- lib/mqt-plugin.cpp | 6 +- 3 files changed, 30 insertions(+), 175 deletions(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 37f4394..750b176 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -22,8 +22,10 @@ add_subdirectory(Conversion) set(TARGET_NAME mqt-core-plugins-catalyst) -add_llvm_library(${TARGET_NAME} MODULE mqt-plugin.cpp LINK_LIBS CatalystQuantumToQC - QCToCatalystQuantum) +add_llvm_library( + ${TARGET_NAME} MODULE mqt-plugin.cpp LINK_LIBS CatalystQuantumToQC + # QCToCatalystQuantum +) # set required C++ standard target_compile_features(${TARGET_NAME} PUBLIC cxx_std_20) diff --git a/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp index 63cc9b9..335f0b1 100644 --- a/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp +++ b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp @@ -186,13 +186,13 @@ class CatalystQuantumToQCTypeConverter final : public TypeConverter { // Convert Catalyst QubitType to QC QubitType addConversion([ctx](catalyst::quantum::QubitType /*type*/) -> Type { - return opt::QubitType::get(ctx); + return qc::QubitType::get(ctx); }); // Convert Catalyst QuregType to dynamic memref as placeholder // The actual static memref types will flow through from alloc operations addConversion([ctx](catalyst::quantum::QuregType /*type*/) -> Type { - auto qubitType = opt::QubitType::get(ctx); + auto qubitType = qc::QubitType::get(ctx); return MemRefType::get( {ShapedType::kDynamic}, // NOLINT(misc-include-cleaner) qubitType); @@ -227,7 +227,7 @@ struct ConvertQuantumAlloc final auto nqubitsAttr = op.getNqubitsAttrAttr(); // Prepare the result type(s) - const auto qubitType = opt::QubitType::get(rewriter.getContext()); + const auto qubitType = qc::QubitType::get(rewriter.getContext()); if (nqubitsAttr) { // Static allocation @@ -290,18 +290,13 @@ struct ConvertQuantumMeasure final // Extract operand(s) const auto inQubit = adaptor.getInQubit(); - // Prepare the result type(s) - const auto qubitType = opt::QubitType::get(rewriter.getContext()); - const auto bitType = rewriter.getI1Type(); - // Create the new operation // Note: quantum.measure returns (i1, !quantum.bit) - // QC.measure returns (!QC.Qubit, i1) - auto QCOp = rewriter.create(op.getLoc(), qubitType, bitType, - inQubit); + // qc.measure returns i1 + auto QCOp = rewriter.create(op.getLoc(), inQubit); // Replace with results in the correct order - rewriter.replaceOp(op, {QCOp.getResult(1), QCOp.getResult(0)}); + rewriter.replaceOp(op, {inQubit, QCOp.getResult()}); return success(); } }; @@ -314,7 +309,7 @@ struct ConvertQuantumExtract final matchAndRewrite(catalyst::quantum::ExtractOp op, OpAdaptor adaptor, ConversionPatternRewriter& rewriter) const override { // Prepare the result type(s) - const auto qubitType = opt::QubitType::get(rewriter.getContext()); + const auto qubitType = qc::QubitType::get(rewriter.getContext()); // Get index (either from attribute or operand) Value indexValue; @@ -418,59 +413,6 @@ struct ConvertQuantumInsert final } }; -struct ConvertQuantumGlobalPhase final - : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(catalyst::quantum::GlobalPhaseOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Extract operand(s) and attribute(s) - const auto param = adaptor.getParams(); - const auto inCtrlQubits = adaptor.getInCtrlQubits(); - const auto inCtrlValues = adaptor.getInCtrlValues(); - - // Separate positive and negative control qubits - ControlPartitionResult ctrlResult; - if (failed(partitionControlQubits(inCtrlQubits, inCtrlValues, rewriter, - op.getLoc(), ctrlResult))) { - return failure(); - } - - const auto& inPosCtrlQubitsVec = ctrlResult.posCtrlQubits; - const auto& inNegCtrlQubitsVec = ctrlResult.negCtrlQubits; - - // Create the parameter attributes - const SmallVector staticParamsVec; - SmallVector paramsMaskVec; - SmallVector finalParamValues; - - // All parameters are treated as dynamic during conversion - // Constant folding should be done by canonicalization passes - finalParamValues.push_back(param); - paramsMaskVec.push_back(false); - - const auto staticParams = - DenseF64ArrayAttr::get(rewriter.getContext(), staticParamsVec); - const auto paramsMask = - DenseBoolArrayAttr::get(rewriter.getContext(), paramsMaskVec); - - // Create the new operation - auto QCOp = rewriter.create( - op.getLoc(), TypeRange{}, // out_qubits - ValueRange(inPosCtrlQubitsVec).getTypes(), // pos_ctrl_out_qubits - ValueRange(inNegCtrlQubitsVec).getTypes(), // neg_ctrl_out_qubits - staticParams, paramsMask, finalParamValues, // params - ValueRange{}, // in_qubits - ValueRange(inPosCtrlQubitsVec), // pos_ctrl_in_qubits - ValueRange(inNegCtrlQubitsVec)); // neg_ctrl_in_qubits - - // Replace the original with the new operation - rewriter.replaceOp(op, QCOp); - return success(); - } -}; - struct ConvertQuantumCustomOp final : OpConversionPattern { using OpConversionPattern::OpConversionPattern; @@ -513,7 +455,7 @@ struct ConvertQuantumCustomOp final Operation* QCOp = nullptr; #define CREATE_GATE_OP(GATE_TYPE) \ - rewriter.create( \ + rewriter.create( \ op.getLoc(), inQubits.getTypes(), \ ValueRange(additionalPosCtrlQubits).getTypes(), \ ValueRange(additionalNegCtrlQubits).getTypes(), staticParams, \ @@ -523,7 +465,7 @@ struct ConvertQuantumCustomOp final if (gateName == "Hadamard") { QCOp = CREATE_GATE_OP(H); } else if (gateName == "Identity") { - QCOp = CREATE_GATE_OP(I); + QCOp = CREATE_GATE_OP(Id); } else if (gateName == "PauliX") { QCOp = CREATE_GATE_OP(X); } else if (gateName == "PauliY") { @@ -553,11 +495,7 @@ struct ConvertQuantumCustomOp final } else if (gateName == "SWAP") { QCOp = CREATE_GATE_OP(SWAP); } else if (gateName == "ISWAP") { - if (op.getAdjoint()) { - QCOp = CREATE_GATE_OP(iSWAPdg); - } else { - QCOp = CREATE_GATE_OP(iSWAP); - } + QCOp = CREATE_GATE_OP(iSWAP); } else if (gateName == "RX") { QCOp = CREATE_GATE_OP(RX); } else if (gateName == "RY") { @@ -570,7 +508,7 @@ struct ConvertQuantumCustomOp final auto controlLists = buildControlLists( inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), ValueRange(additionalNegCtrlQubits)); - auto crxOp = rewriter.create( + auto crxOp = rewriter.create( op.getLoc(), inQubits[1].getType(), ValueRange(controlLists.posCtrlQubits).getTypes(), ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, @@ -584,7 +522,7 @@ struct ConvertQuantumCustomOp final auto controlLists = buildControlLists( inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), ValueRange(additionalNegCtrlQubits)); - auto cryOp = rewriter.create( + auto cryOp = rewriter.create( op.getLoc(), inQubits[1].getType(), ValueRange(controlLists.posCtrlQubits).getTypes(), ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, @@ -598,7 +536,7 @@ struct ConvertQuantumCustomOp final auto controlLists = buildControlLists( inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), ValueRange(additionalNegCtrlQubits)); - auto crzOp = rewriter.create( + auto crzOp = rewriter.create( op.getLoc(), inQubits[1].getType(), ValueRange(controlLists.posCtrlQubits).getTypes(), ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, @@ -612,7 +550,7 @@ struct ConvertQuantumCustomOp final auto controlLists = buildControlLists( inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), ValueRange(additionalNegCtrlQubits)); - auto cpOp = rewriter.create( + auto cpOp = rewriter.create( op.getLoc(), inQubits[1].getType(), ValueRange(controlLists.posCtrlQubits).getTypes(), ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, @@ -639,7 +577,7 @@ struct ConvertQuantumCustomOp final auto isingxyParamsMaskAttr = DenseBoolArrayAttr::get(rewriter.getContext(), isingxyParamsMask); - QCOp = rewriter.create( + QCOp = rewriter.create( op.getLoc(), inQubits.getTypes(), ValueRange(additionalPosCtrlQubits).getTypes(), ValueRange(additionalNegCtrlQubits).getTypes(), @@ -655,7 +593,7 @@ struct ConvertQuantumCustomOp final auto controlLists = buildControlLists( inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), ValueRange(additionalNegCtrlQubits)); - auto cnotOp = rewriter.create( + auto cnotOp = rewriter.create( op.getLoc(), inQubits[1].getType(), ValueRange(controlLists.posCtrlQubits).getTypes(), ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, @@ -669,7 +607,7 @@ struct ConvertQuantumCustomOp final auto controlLists = buildControlLists( inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), ValueRange(additionalNegCtrlQubits)); - auto cyOp = rewriter.create( + auto cyOp = rewriter.create( op.getLoc(), inQubits[1].getType(), ValueRange(controlLists.posCtrlQubits).getTypes(), ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, @@ -683,7 +621,7 @@ struct ConvertQuantumCustomOp final auto controlLists = buildControlLists( inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), ValueRange(additionalNegCtrlQubits)); - auto czOp = rewriter.create( + auto czOp = rewriter.create( op.getLoc(), inQubits[1].getType(), ValueRange(controlLists.posCtrlQubits).getTypes(), ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, @@ -697,7 +635,7 @@ struct ConvertQuantumCustomOp final auto controlLists = buildControlLists( inQubits.take_front(2), ValueRange(additionalPosCtrlQubits), ValueRange(additionalNegCtrlQubits)); - auto toffoliOp = rewriter.create( + auto toffoliOp = rewriter.create( op.getLoc(), inQubits[2].getType(), ValueRange(controlLists.posCtrlQubits).getTypes(), ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, @@ -716,7 +654,7 @@ struct ConvertQuantumCustomOp final additionalPosCtrlQubits.end()); const SmallVector negCtrls(additionalNegCtrlQubits.begin(), additionalNegCtrlQubits.end()); - auto cswapOp = rewriter.create( + auto cswapOp = rewriter.create( op.getLoc(), ValueRange{inQubits[1], inQubits[2]}, ValueRange(ctrls).getTypes(), ValueRange(negCtrls).getTypes(), staticParams, paramsMask, finalParamValues, @@ -730,11 +668,11 @@ struct ConvertQuantumCustomOp final // Collect all control results first for (size_t i = 0; i < totalCtrls; ++i) { - results.push_back(cswapOp.getResult(numTargets + i)); + results.push_back(cswapOp.getODSResults(numTargets + i)); } // Then append target results - results.push_back(cswapOp.getResult(0)); - results.push_back(cswapOp.getResult(1)); + results.push_back(cswapOp.getODSResults(0)); + results.push_back(cswapOp.getODSResults(1)); rewriter.replaceOp(op, results); return success(); @@ -750,89 +688,4 @@ struct ConvertQuantumCustomOp final } }; -struct CatalystQuantumToQC final - : impl::CatalystQuantumToQCBase { - using CatalystQuantumToQCBase::CatalystQuantumToQCBase; - - void runOnOperation() override { - MLIRContext* context = &getContext(); - auto* module = getOperation(); - - ConversionTarget target(*context); - target.addLegalDialect(); - target.addLegalDialect(); - target.addLegalDialect(); - target.addIllegalDialect(); - - // Mark operations legal that have no equivalent in the target dialect - target.addLegalOp< - catalyst::quantum::DeviceInitOp, catalyst::quantum::DeviceReleaseOp, - catalyst::quantum::NamedObsOp, catalyst::quantum::ExpvalOp, - catalyst::quantum::FinalizeOp, catalyst::quantum::ComputationalBasisOp, - catalyst::quantum::StateOp, catalyst::quantum::InitializeOp>(); - - const CatalystQuantumToQCTypeConverter typeConverter(context); - RewritePatternSet patterns(context); - - patterns - .add(typeConverter, - context); - - // Type conversion boilerplate to handle function signatures and control - // flow See: https://www.jeremykun.com/2023/10/23/mlir-dialect-conversion - - // Convert func.func signatures to use the converted types - populateFunctionOpInterfaceTypeConversionPattern( - patterns, typeConverter); - - // Mark func.func as legal only if signature and body types are converted - target.addDynamicallyLegalOp([&](Operation* op) { - if (auto funcOp = dyn_cast(op)) { - return typeConverter.isSignatureLegal(funcOp.getFunctionType()) && - typeConverter.isLegal(&funcOp.getBody()); - } - return true; // Not a FuncOp, treat as legal (not our concern) - }); - - // Convert return ops to match the new function result types - populateReturnOpTypeConversionPattern(patterns, typeConverter); - - // Mark func.return as legal only if operand types match converted types - target.addDynamicallyLegalOp([&](Operation* op) { - if (isa(op)) { - return typeConverter.isLegal(op); - } - return true; - }); - - // Convert call sites to use the converted argument and result types - populateCallOpTypeConversionPattern(patterns, typeConverter); - - // Mark func.call as legal only if operand and result types are converted - target.addDynamicallyLegalOp([&](Operation* op) { - if (isa(op)) { - return typeConverter.isLegal(op); - } - return true; - }); - - // Convert control-flow ops (cf.br, cf.cond_br, etc.) - populateBranchOpInterfaceTypeConversionPattern(patterns, typeConverter); - - // Mark unknown ops as legal if they don't require type conversion - target.markUnknownOpDynamicallyLegal([&](Operation* op) { - return isNotBranchOpInterfaceOrReturnLikeOp(op) || - isLegalForBranchOpInterfaceTypeConversionPattern(op, - typeConverter) || - isLegalForReturnOpTypeConversionPattern(op, typeConverter); - }); - - if (failed(applyPartialConversion(module, target, std::move(patterns)))) { - signalPassFailure(); - } - } -}; - } // namespace mqt::ir::conversions diff --git a/lib/mqt-plugin.cpp b/lib/mqt-plugin.cpp index 9bba78d..feb7a31 100644 --- a/lib/mqt-plugin.cpp +++ b/lib/mqt-plugin.cpp @@ -29,7 +29,7 @@ mlirGetDialectPluginInfo() { .pluginName = "QC", .pluginVersion = LLVM_VERSION_STRING, .registerDialectRegistryCallbacks = [](DialectRegistry* registry) { - registry->insert<::mlir::QCDialect>(); + registry->insert<::qc::QCDialect>(); }}; } @@ -43,7 +43,7 @@ extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo mlirGetPassPluginInfo() { // Only register the conversion passes we implement // Note: mqt::ir::opt::registerQCPasses() is not called to avoid // pulling in transpilation transforms that require LLVM 21 - // mqt::ir::conversions::registerCatalystQuantumToQCPasses(); - mqt::ir::conversions::registerQCToCatalystQuantumPasses(); + mqt::ir::conversions::registerCatalystQuantumToQCPasses(); + // mqt::ir::conversions::registerQCToCatalystQuantumPasses(); }}; } From bad41117bb90226140d94b213d43f2689799a790 Mon Sep 17 00:00:00 2001 From: "Kostrzewa, Niklas" Date: Fri, 13 Mar 2026 12:07:37 +0100 Subject: [PATCH 5/7] Added explicit MemRefTypeInterface to core fork, add rest of CustomGates (based of catalyst -> mqtopt conversions) --- cmake/ExternalDependencies.cmake | 4 +- .../CatalystQuantumToQC.td | 4 +- .../CatalystQuantumToQC.cpp | 396 +++++++++--------- 3 files changed, 212 insertions(+), 192 deletions(-) diff --git a/cmake/ExternalDependencies.cmake b/cmake/ExternalDependencies.cmake index 4467352..38a0e5c 100644 --- a/cmake/ExternalDependencies.cmake +++ b/cmake/ExternalDependencies.cmake @@ -15,9 +15,9 @@ set(MQT_CORE_MINIMUM_VERSION 3.4.0 CACHE STRING "MQT Core minimum version") set(MQT_CORE_VERSION 3.4.0 CACHE STRING "MQT Core version") -set(MQT_CORE_REV "main" +set(MQT_CORE_REV "test_Memref_explicit" # fix clang-tidy warnings 7d1d7fb CACHE STRING "MQT Core identifier (tag, branch or commit hash)") -set(MQT_CORE_REPO_OWNER "munich-quantum-toolkit" +set(MQT_CORE_REPO_OWNER "NiklasKostrzewa" CACHE STRING "MQT Core repository owner (change when using a fork)") # cmake-format: on diff --git a/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td index aed4f0d..4c364cd 100644 --- a/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td +++ b/include/mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.td @@ -8,7 +8,7 @@ include "mlir/Pass/PassBase.td" -def CatalystQuantumToMQTOpt : Pass<"catalystquantum-to-qc"> { +def CatalystQuantumToQC : Pass<"catalystquantum-to-qc"> { let summary = "Convert Catalyst's `Quantum` to MQT's `QC` dialect."; let description = [{ @@ -27,6 +27,6 @@ def CatalystQuantumToMQTOpt : Pass<"catalystquantum-to-qc"> { // Define dependent dialects let dependentDialects = [ "catalyst::quantum::QuantumDialect", - "::mlir::qc" + "mlir::qc::QCDialect" ]; } diff --git a/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp index 335f0b1..33f01f9 100644 --- a/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp +++ b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp @@ -413,6 +413,50 @@ struct ConvertQuantumInsert final } }; +struct ConvertQuantumGlobalPhase final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(catalyst::quantum::GlobalPhaseOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operand(s) and attribute(s) + const auto param = adaptor.getParams(); + const auto inCtrlQubits = adaptor.getInCtrlQubits(); + const auto inCtrlValues = adaptor.getInCtrlValues(); + + // Separate positive and negative control qubits + ControlPartitionResult ctrlResult; + if (failed(partitionControlQubits(inCtrlQubits, inCtrlValues, rewriter, + op.getLoc(), ctrlResult))) { + return failure(); + } + + const auto& inPosCtrlQubitsVec = ctrlResult.posCtrlQubits; + const auto& inNegCtrlQubitsVec = ctrlResult.negCtrlQubits; + + // Create the parameter attributes + const SmallVector staticParamsVec; + SmallVector paramsMaskVec; + SmallVector finalParamValues; + + // All parameters are treated as dynamic during conversion + // Constant folding should be done by canonicalization passes + finalParamValues.push_back(param); + paramsMaskVec.push_back(false); + + const auto staticParams = + DenseF64ArrayAttr::get(rewriter.getContext(), staticParamsVec); + const auto paramsMask = + DenseBoolArrayAttr::get(rewriter.getContext(), paramsMaskVec); + + // Replace the original with the new operation + rewriter.create(op.getLoc(), finalParamValues[0]); + rewriter.eraseOp(op); + return success(); + } +}; + struct ConvertQuantumCustomOp final : OpConversionPattern { using OpConversionPattern::OpConversionPattern; @@ -452,240 +496,216 @@ struct ConvertQuantumCustomOp final DenseBoolArrayAttr::get(rewriter.getContext(), paramInfo.paramsMask); // Create the new operation - Operation* QCOp = nullptr; + Operation* qcOp = nullptr; + + // auto qcOp = rewriter.create(op.getLoc(), inQubits[0]); // test + // syntax (Niklas) + +#define CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(GATE_TYPE) \ + rewriter.create(op.getLoc(), inQubits[0]) + +#define CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP(GATE_TYPE) \ + rewriter.create(op.getLoc(), inQubits[0], inQubits[1]) -#define CREATE_GATE_OP(GATE_TYPE) \ - rewriter.create( \ - op.getLoc(), inQubits.getTypes(), \ - ValueRange(additionalPosCtrlQubits).getTypes(), \ - ValueRange(additionalNegCtrlQubits).getTypes(), staticParams, \ - paramsMask, finalParamValues, inQubits, additionalPosCtrlQubits, \ - additionalNegCtrlQubits) +#define CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(GATE_TYPE) \ + rewriter.create(op.getLoc(), inQubits[0], \ + finalParamValues[0]) + +#define CREATE_TWO_TARGET_ONE_PARAMETER_GATE_OP(GATE_TYPE) \ + rewriter.create(op.getLoc(), inQubits[0], inQubits[1], \ + finalParamValues[0]) if (gateName == "Hadamard") { - QCOp = CREATE_GATE_OP(H); + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(H); } else if (gateName == "Identity") { - QCOp = CREATE_GATE_OP(Id); + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Id); } else if (gateName == "PauliX") { - QCOp = CREATE_GATE_OP(X); + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(X); } else if (gateName == "PauliY") { - QCOp = CREATE_GATE_OP(Y); + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Y); } else if (gateName == "PauliZ") { - QCOp = CREATE_GATE_OP(Z); + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Z); } else if (gateName == "S") { if (op.getAdjoint()) { - QCOp = CREATE_GATE_OP(Sdg); + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Sdg); } else { - QCOp = CREATE_GATE_OP(S); + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(S); } } else if (gateName == "T") { if (op.getAdjoint()) { - QCOp = CREATE_GATE_OP(Tdg); + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Tdg); } else { - QCOp = CREATE_GATE_OP(T); + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(T); } } else if (gateName == "SX") { if (op.getAdjoint()) { - QCOp = CREATE_GATE_OP(SXdg); + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(SXdg); } else { - QCOp = CREATE_GATE_OP(SX); + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(SX); } } else if (gateName == "ECR") { - QCOp = CREATE_GATE_OP(ECR); + qcOp = CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP(ECR); } else if (gateName == "SWAP") { - QCOp = CREATE_GATE_OP(SWAP); + qcOp = CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP(SWAP); } else if (gateName == "ISWAP") { - QCOp = CREATE_GATE_OP(iSWAP); + qcOp = CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP(iSWAP); } else if (gateName == "RX") { - QCOp = CREATE_GATE_OP(RX); + qcOp = CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(RX); } else if (gateName == "RY") { - QCOp = CREATE_GATE_OP(RY); + qcOp = CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(RY); } else if (gateName == "RZ") { - QCOp = CREATE_GATE_OP(RZ); + qcOp = CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(RZ); } else if (gateName == "PhaseShift") { - QCOp = CREATE_GATE_OP(P); + qcOp = CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(P); } else if (gateName == "CRX") { - auto controlLists = buildControlLists( - inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), - ValueRange(additionalNegCtrlQubits)); - auto crxOp = rewriter.create( - op.getLoc(), inQubits[1].getType(), - ValueRange(controlLists.posCtrlQubits).getTypes(), - ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, - paramsMask, finalParamValues, inQubits[1], controlLists.posCtrlQubits, - controlLists.negCtrlQubits); - const size_t ctrlCount = - controlLists.posCtrlQubits.size() + controlLists.negCtrlQubits.size(); - rewriter.replaceOp(op, reorderControlledGateResults(crxOp, ctrlCount)); - return success(); + qcOp = rewriter.create( + op.getLoc(), inQubits.take_front(1), [&]() { + rewriter.create(op.getLoc(), inQubits[1], + finalParamValues[0]); + }); } else if (gateName == "CRY") { - auto controlLists = buildControlLists( - inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), - ValueRange(additionalNegCtrlQubits)); - auto cryOp = rewriter.create( - op.getLoc(), inQubits[1].getType(), - ValueRange(controlLists.posCtrlQubits).getTypes(), - ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, - paramsMask, finalParamValues, inQubits[1], controlLists.posCtrlQubits, - controlLists.negCtrlQubits); - const size_t ctrlCount = - controlLists.posCtrlQubits.size() + controlLists.negCtrlQubits.size(); - rewriter.replaceOp(op, reorderControlledGateResults(cryOp, ctrlCount)); - return success(); + qcOp = rewriter.create( + op.getLoc(), inQubits.take_front(1), [&]() { + rewriter.create(op.getLoc(), inQubits[1], + finalParamValues[0]); + }); } else if (gateName == "CRZ") { - auto controlLists = buildControlLists( - inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), - ValueRange(additionalNegCtrlQubits)); - auto crzOp = rewriter.create( - op.getLoc(), inQubits[1].getType(), - ValueRange(controlLists.posCtrlQubits).getTypes(), - ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, - paramsMask, finalParamValues, inQubits[1], controlLists.posCtrlQubits, - controlLists.negCtrlQubits); - const size_t ctrlCount = - controlLists.posCtrlQubits.size() + controlLists.negCtrlQubits.size(); - rewriter.replaceOp(op, reorderControlledGateResults(crzOp, ctrlCount)); - return success(); + qcOp = rewriter.create( + op.getLoc(), inQubits.take_front(1), [&]() { + rewriter.create(op.getLoc(), inQubits[1], + finalParamValues[0]); + }); } else if (gateName == "ControlledPhaseShift") { - auto controlLists = buildControlLists( - inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), - ValueRange(additionalNegCtrlQubits)); - auto cpOp = rewriter.create( - op.getLoc(), inQubits[1].getType(), - ValueRange(controlLists.posCtrlQubits).getTypes(), - ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, - paramsMask, finalParamValues, inQubits[1], controlLists.posCtrlQubits, - controlLists.negCtrlQubits); - const size_t ctrlCount = - controlLists.posCtrlQubits.size() + controlLists.negCtrlQubits.size(); - rewriter.replaceOp(op, reorderControlledGateResults(cpOp, ctrlCount)); - return success(); - } else if (gateName == "IsingXY") { - // PennyLane IsingXY has 1 parameter (phi), OpenQASM XXPlusYY needs 2 - // (theta, beta) Relationship: IsingXY(phi) = XXPlusYY(phi, pi) - // Add pi as second static parameter (since we add it during compilation) - SmallVector isingxyStaticParams(paramInfo.staticParams.begin(), - paramInfo.staticParams.end()); - isingxyStaticParams.push_back(std::numbers::pi); - - SmallVector isingxyParamsMask(paramInfo.paramsMask.begin(), - paramInfo.paramsMask.end()); - isingxyParamsMask.push_back(true); // pi is a compile-time constant - - auto isingxyStaticParamsAttr = - DenseF64ArrayAttr::get(rewriter.getContext(), isingxyStaticParams); - auto isingxyParamsMaskAttr = - DenseBoolArrayAttr::get(rewriter.getContext(), isingxyParamsMask); - - QCOp = rewriter.create( - op.getLoc(), inQubits.getTypes(), - ValueRange(additionalPosCtrlQubits).getTypes(), - ValueRange(additionalNegCtrlQubits).getTypes(), - isingxyStaticParamsAttr, isingxyParamsMaskAttr, finalParamValues, - inQubits, additionalPosCtrlQubits, additionalNegCtrlQubits); + qcOp = rewriter.create( + op.getLoc(), inQubits.take_front(1), [&]() { + rewriter.create(op.getLoc(), inQubits[1], + finalParamValues[0]); + }); } else if (gateName == "IsingXX") { - QCOp = CREATE_GATE_OP(RXX); + qcOp = CREATE_TWO_TARGET_ONE_PARAMETER_GATE_OP(RXX); } else if (gateName == "IsingYY") { - QCOp = CREATE_GATE_OP(RYY); + qcOp = CREATE_TWO_TARGET_ONE_PARAMETER_GATE_OP(RYY); } else if (gateName == "IsingZZ") { - QCOp = CREATE_GATE_OP(RZZ); + qcOp = CREATE_TWO_TARGET_ONE_PARAMETER_GATE_OP(RZZ); } else if (gateName == "CNOT") { - auto controlLists = buildControlLists( - inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), - ValueRange(additionalNegCtrlQubits)); - auto cnotOp = rewriter.create( - op.getLoc(), inQubits[1].getType(), - ValueRange(controlLists.posCtrlQubits).getTypes(), - ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, - paramsMask, finalParamValues, inQubits[1], controlLists.posCtrlQubits, - controlLists.negCtrlQubits); - const size_t ctrlCount = - controlLists.posCtrlQubits.size() + controlLists.negCtrlQubits.size(); - rewriter.replaceOp(op, reorderControlledGateResults(cnotOp, ctrlCount)); - return success(); + qcOp = rewriter.create( + op.getLoc(), inQubits.take_front(1), + [&]() { rewriter.create(op.getLoc(), inQubits[1]); }); } else if (gateName == "CY") { - auto controlLists = buildControlLists( - inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), - ValueRange(additionalNegCtrlQubits)); - auto cyOp = rewriter.create( - op.getLoc(), inQubits[1].getType(), - ValueRange(controlLists.posCtrlQubits).getTypes(), - ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, - paramsMask, finalParamValues, inQubits[1], controlLists.posCtrlQubits, - controlLists.negCtrlQubits); - const size_t ctrlCount = - controlLists.posCtrlQubits.size() + controlLists.negCtrlQubits.size(); - rewriter.replaceOp(op, reorderControlledGateResults(cyOp, ctrlCount)); - return success(); + qcOp = rewriter.create( + op.getLoc(), inQubits.take_front(1), + [&]() { rewriter.create(op.getLoc(), inQubits[1]); }); } else if (gateName == "CZ") { - auto controlLists = buildControlLists( - inQubits.take_front(1), ValueRange(additionalPosCtrlQubits), - ValueRange(additionalNegCtrlQubits)); - auto czOp = rewriter.create( - op.getLoc(), inQubits[1].getType(), - ValueRange(controlLists.posCtrlQubits).getTypes(), - ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, - paramsMask, finalParamValues, inQubits[1], controlLists.posCtrlQubits, - controlLists.negCtrlQubits); - const size_t ctrlCount = - controlLists.posCtrlQubits.size() + controlLists.negCtrlQubits.size(); - rewriter.replaceOp(op, reorderControlledGateResults(czOp, ctrlCount)); - return success(); + qcOp = rewriter.create( + op.getLoc(), inQubits.take_front(1), + [&]() { rewriter.create(op.getLoc(), inQubits[1]); }); } else if (gateName == "Toffoli") { - auto controlLists = buildControlLists( - inQubits.take_front(2), ValueRange(additionalPosCtrlQubits), - ValueRange(additionalNegCtrlQubits)); - auto toffoliOp = rewriter.create( - op.getLoc(), inQubits[2].getType(), - ValueRange(controlLists.posCtrlQubits).getTypes(), - ValueRange(controlLists.negCtrlQubits).getTypes(), staticParams, - paramsMask, finalParamValues, inQubits[2], controlLists.posCtrlQubits, - controlLists.negCtrlQubits); - const size_t ctrlCount = - controlLists.posCtrlQubits.size() + controlLists.negCtrlQubits.size(); - rewriter.replaceOp(op, - reorderControlledGateResults(toffoliOp, ctrlCount)); - return success(); + qcOp = rewriter.create( + op.getLoc(), inQubits.take_front(2), + [&]() { rewriter.create(op.getLoc(), inQubits[2]); }); } else if (gateName == "CSWAP") { - // CSWAP gate: 1 control qubit + 2 target qubits - // inQubits[0] is control, inQubits[1] and inQubits[2] are targets - SmallVector ctrls = {inQubits[0]}; - ctrls.append(additionalPosCtrlQubits.begin(), - additionalPosCtrlQubits.end()); - const SmallVector negCtrls(additionalNegCtrlQubits.begin(), - additionalNegCtrlQubits.end()); - auto cswapOp = rewriter.create( - op.getLoc(), ValueRange{inQubits[1], inQubits[2]}, - ValueRange(ctrls).getTypes(), ValueRange(negCtrls).getTypes(), - staticParams, paramsMask, finalParamValues, - ValueRange{inQubits[1], inQubits[2]}, ctrls, negCtrls); - - // QC SWAP returns (target0, target1, ctrl0, ctrl1, ...) - // Catalyst CSWAP expects (ctrl0, ctrl1, ..., target0, target1) - SmallVector results; - const size_t numTargets = 2; - const size_t totalCtrls = ctrls.size() + negCtrls.size(); - - // Collect all control results first - for (size_t i = 0; i < totalCtrls; ++i) { - results.push_back(cswapOp.getODSResults(numTargets + i)); - } - // Then append target results - results.push_back(cswapOp.getODSResults(0)); - results.push_back(cswapOp.getODSResults(1)); - - rewriter.replaceOp(op, results); - return success(); + qcOp = rewriter.create( + op.getLoc(), inQubits.take_front(1), [&]() { + rewriter.create(op.getLoc(), inQubits[1], inQubits[2]); + }); } else { return op.emitError("Unsupported gate: ") << gateName; } -#undef CREATE_GATE_OP +#undef CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP +#undef CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP +#undef CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP +#undef CREATE_TWO_TARGET_ONE_PARAMETER_GATE_OP // Replace the original with the new operation - rewriter.replaceOp(op, QCOp); + rewriter.replaceOp(op, inQubits); return success(); } }; +struct CatalystQuantumToQC final + : impl::CatalystQuantumToQCBase { + using CatalystQuantumToQCBase::CatalystQuantumToQCBase; + + void runOnOperation() override { + MLIRContext* context = &getContext(); + auto* module = getOperation(); + + ConversionTarget target(*context); + target.addLegalDialect(); + target.addLegalDialect(); + target.addLegalDialect(); + target.addIllegalDialect(); + + // Mark operations legal that have no equivalent in the target dialect + target.addLegalOp< + catalyst::quantum::DeviceInitOp, catalyst::quantum::DeviceReleaseOp, + catalyst::quantum::NamedObsOp, catalyst::quantum::ExpvalOp, + catalyst::quantum::FinalizeOp, catalyst::quantum::ComputationalBasisOp, + catalyst::quantum::StateOp, catalyst::quantum::InitializeOp>(); + + const CatalystQuantumToQCTypeConverter typeConverter(context); + RewritePatternSet patterns(context); + + patterns + .add(typeConverter, + context); + + // Type conversion boilerplate to handle function signatures and control + // flow See: https://www.jeremykun.com/2023/10/23/mlir-dialect-conversion + + // Convert func.func signatures to use the converted types + populateFunctionOpInterfaceTypeConversionPattern( + patterns, typeConverter); + + // Mark func.func as legal only if signature and body types are converted + target.addDynamicallyLegalOp([&](Operation* op) { + if (auto funcOp = dyn_cast(op)) { + return typeConverter.isSignatureLegal(funcOp.getFunctionType()) && + typeConverter.isLegal(&funcOp.getBody()); + } + return true; // Not a FuncOp, treat as legal (not our concern) + }); + + // Convert return ops to match the new function result types + populateReturnOpTypeConversionPattern(patterns, typeConverter); + + // Mark func.return as legal only if operand types match converted types + target.addDynamicallyLegalOp([&](Operation* op) { + if (isa(op)) { + return typeConverter.isLegal(op); + } + return true; + }); + + // Convert call sites to use the converted argument and result types + populateCallOpTypeConversionPattern(patterns, typeConverter); + + // Mark func.call as legal only if operand and result types are converted + target.addDynamicallyLegalOp([&](Operation* op) { + if (isa(op)) { + return typeConverter.isLegal(op); + } + return true; + }); + + // Convert control-flow ops (cf.br, cf.cond_br, etc.) + populateBranchOpInterfaceTypeConversionPattern(patterns, typeConverter); + + // Mark unknown ops as legal if they don't require type conversion + target.markUnknownOpDynamicallyLegal([&](Operation* op) { + return isNotBranchOpInterfaceOrReturnLikeOp(op) || + isLegalForBranchOpInterfaceTypeConversionPattern(op, + typeConverter) || + isLegalForReturnOpTypeConversionPattern(op, typeConverter); + }); + + if (failed(applyPartialConversion(module, target, std::move(patterns)))) { + signalPassFailure(); + } + } +}; + } // namespace mqt::ir::conversions From 473cfdaf8a54efc614c90eb3c404ad55acd1fb6a Mon Sep 17 00:00:00 2001 From: "Kostrzewa, Niklas" Date: Sun, 15 Mar 2026 23:15:22 +0100 Subject: [PATCH 6/7] Changed all filenames for qc -> quantum conversion, added all but gates based on mqtopt -> quantum conversion --- include/mlir/Conversion/CMakeLists.txt | 2 +- .../MQTOptToCatalystQuantum/CMakeLists.txt | 13 - .../QCToCatalystQuantum/CMakeLists.txt | 13 + .../QCToCatalystQuantum.h} | 4 +- .../QCToCatalystQuantum.td} | 8 +- lib/CMakeLists.txt | 6 +- lib/Conversion/CMakeLists.txt | 2 +- .../CatalystQuantumToQC.cpp | 23 +- .../MQTOptToCatalystQuantum.cpp | 1583 ----------------- .../CMakeLists.txt | 24 +- .../QCToCatalystQuantum.cpp | 355 ++++ lib/mqt-plugin.cpp | 4 +- 12 files changed, 412 insertions(+), 1625 deletions(-) delete mode 100644 include/mlir/Conversion/MQTOptToCatalystQuantum/CMakeLists.txt create mode 100644 include/mlir/Conversion/QCToCatalystQuantum/CMakeLists.txt rename include/mlir/Conversion/{MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h => QCToCatalystQuantum/QCToCatalystQuantum.h} (72%) rename include/mlir/Conversion/{MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.td => QCToCatalystQuantum/QCToCatalystQuantum.td} (65%) delete mode 100644 lib/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.cpp rename lib/Conversion/{MQTOptToCatalystQuantum => QCToCatalystQuantum}/CMakeLists.txt (57%) create mode 100644 lib/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.cpp diff --git a/include/mlir/Conversion/CMakeLists.txt b/include/mlir/Conversion/CMakeLists.txt index 062917c..8d7bc6a 100644 --- a/include/mlir/Conversion/CMakeLists.txt +++ b/include/mlir/Conversion/CMakeLists.txt @@ -6,5 +6,5 @@ # # Licensed under the MIT License -add_subdirectory(MQTOptToCatalystQuantum) +add_subdirectory(QCToCatalystQuantum) add_subdirectory(CatalystQuantumToQC) diff --git a/include/mlir/Conversion/MQTOptToCatalystQuantum/CMakeLists.txt b/include/mlir/Conversion/MQTOptToCatalystQuantum/CMakeLists.txt deleted file mode 100644 index b47fdc0..0000000 --- a/include/mlir/Conversion/MQTOptToCatalystQuantum/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2025 - 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 MQTOptToCatalystQuantum.td) -mlir_tablegen(MQTOptToCatalystQuantum.h.inc -gen-pass-decls -name MQTOptToCatalystQuantum) -add_public_tablegen_target(MQTOptToCatalystQuantumIncGen) - -add_mlir_doc(MQTOptToCatalystQuantum MQTOptToCatalystQuantum ./ -gen-pass-doc) diff --git a/include/mlir/Conversion/QCToCatalystQuantum/CMakeLists.txt b/include/mlir/Conversion/QCToCatalystQuantum/CMakeLists.txt new file mode 100644 index 0000000..4c6c52f --- /dev/null +++ b/include/mlir/Conversion/QCToCatalystQuantum/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2025 - 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 QCToCatalystQuantum.td) +mlir_tablegen(QCToCatalystQuantum.h.inc -gen-pass-decls -name QCToCatalystQuantum) +add_public_tablegen_target(QCToCatalystQuantumIncGen) + +add_mlir_doc(QCToCatalystQuantum QCToCatalystQuantum ./ -gen-pass-doc) diff --git a/include/mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h b/include/mlir/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.h similarity index 72% rename from include/mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h rename to include/mlir/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.h index 9aece8d..a1d08ef 100644 --- a/include/mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h +++ b/include/mlir/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.h @@ -15,9 +15,9 @@ namespace mqt::ir::conversions { #define GEN_PASS_DECL -#include "mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h.inc" +#include "mlir/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.h.inc" #define GEN_PASS_REGISTRATION -#include "mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h.inc" +#include "mlir/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.h.inc" } // namespace mqt::ir::conversions diff --git a/include/mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.td b/include/mlir/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.td similarity index 65% rename from include/mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.td rename to include/mlir/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.td index eebda2a..6825b0d 100644 --- a/include/mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.td +++ b/include/mlir/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.td @@ -8,17 +8,17 @@ include "mlir/Pass/PassBase.td" -def MQTOptToCatalystQuantum : Pass<"mqtopt-to-catalystquantum"> { - let summary = "Convert MQT's `MQTOpt` to Catalyst's `Quantum` dialect."; +def QCToCatalystQuantum : Pass<"qc-to-catalystquantum"> { + let summary = "Convert MQT's `QC` to Catalyst's `Quantum` dialect."; let description = [{ - This pass converts MQT's `MQTOpt` to Catalyst's `Quantum` dialect. + This pass converts MQT's `QC` to Catalyst's `Quantum` dialect. }]; let dependentDialects = [ "::catalyst::quantum::QuantumDialect", "::mlir::arith::ArithDialect", "::mlir::func::FuncDialect", "::mlir::memref::MemRefDialect", - "::mqt::ir::opt::MQTOptDialect" + "::mlir::qc::QCDialect" ]; } diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 750b176..37f4394 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -22,10 +22,8 @@ add_subdirectory(Conversion) set(TARGET_NAME mqt-core-plugins-catalyst) -add_llvm_library( - ${TARGET_NAME} MODULE mqt-plugin.cpp LINK_LIBS CatalystQuantumToQC - # QCToCatalystQuantum -) +add_llvm_library(${TARGET_NAME} MODULE mqt-plugin.cpp LINK_LIBS CatalystQuantumToQC + QCToCatalystQuantum) # set required C++ standard target_compile_features(${TARGET_NAME} PUBLIC cxx_std_20) diff --git a/lib/Conversion/CMakeLists.txt b/lib/Conversion/CMakeLists.txt index 062917c..8d7bc6a 100644 --- a/lib/Conversion/CMakeLists.txt +++ b/lib/Conversion/CMakeLists.txt @@ -6,5 +6,5 @@ # # Licensed under the MIT License -add_subdirectory(MQTOptToCatalystQuantum) +add_subdirectory(QCToCatalystQuantum) add_subdirectory(CatalystQuantumToQC) diff --git a/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp index 33f01f9..f738ee8 100644 --- a/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp +++ b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp @@ -498,9 +498,6 @@ struct ConvertQuantumCustomOp final // Create the new operation Operation* qcOp = nullptr; - // auto qcOp = rewriter.create(op.getLoc(), inQubits[0]); // test - // syntax (Niklas) - #define CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(GATE_TYPE) \ rewriter.create(op.getLoc(), inQubits[0]) @@ -581,6 +578,26 @@ struct ConvertQuantumCustomOp final rewriter.create(op.getLoc(), inQubits[1], finalParamValues[0]); }); + } else if (gateName == "IsingXY") { + // PennyLane IsingXY has 1 parameter (phi), OpenQASM XXPlusYY needs 2 + // (theta, beta) Relationship: IsingXY(phi) = XXPlusYY(phi, pi) + // Add pi as second static parameter (since we add it during compilation) + SmallVector isingxyStaticParams(paramInfo.staticParams.begin(), + paramInfo.staticParams.end()); + isingxyStaticParams.push_back(std::numbers::pi); + + SmallVector isingxyParamsMask(paramInfo.paramsMask.begin(), + paramInfo.paramsMask.end()); + isingxyParamsMask.push_back(true); // pi is a compile-time constant + + auto isingxyStaticParamsAttr = + DenseF64ArrayAttr::get(rewriter.getContext(), isingxyStaticParams); + auto isingxyParamsMaskAttr = + DenseBoolArrayAttr::get(rewriter.getContext(), isingxyParamsMask); + + qcOp = rewriter.create(op.getLoc(), inQubits[0], + inQubits[1], finalParamValues[0], + isingxyStaticParamsAttr[0]); } else if (gateName == "IsingXX") { qcOp = CREATE_TWO_TARGET_ONE_PARAMETER_GATE_OP(RXX); } else if (gateName == "IsingYY") { diff --git a/lib/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.cpp b/lib/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.cpp deleted file mode 100644 index a5d5a36..0000000 --- a/lib/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.cpp +++ /dev/null @@ -1,1583 +0,0 @@ -/* - * Copyright (c) 2025 - 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/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h" // NOLINT(misc-include-cleaner) - -#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mqt::ir::conversions { - -#define GEN_PASS_DEF_MQTOPTTOCATALYSTQUANTUM -#include "mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h.inc" - -using namespace mlir; -using namespace mlir::arith; - -// Helper functions to reduce code duplication -namespace { - -/// Helper struct to hold control qubit information -struct ControlInfo { - SmallVector ctrlQubits; - SmallVector ctrlValues; - - ControlInfo() noexcept = default; -}; - -/// Extract and concatenate control qubits and create corresponding control -/// values -ControlInfo extractControlInfo(ValueRange posCtrlQubits, - ValueRange negCtrlQubits, - ConversionPatternRewriter& rewriter, - Location loc) { - ControlInfo info; - - // Concatenate controls: [pos..., neg...] (preserve this order consistently) - info.ctrlQubits.reserve(posCtrlQubits.size() + negCtrlQubits.size()); - info.ctrlQubits.append(posCtrlQubits.begin(), posCtrlQubits.end()); - info.ctrlQubits.append(negCtrlQubits.begin(), negCtrlQubits.end()); - - if (info.ctrlQubits.empty()) { - return info; - } - - // Create control values: 1 for positive controls, 0 for negative controls - const Value one = - rewriter.create(loc, /*value=*/1, - /*width=*/1); - const Value zero = - rewriter.create(loc, /*value=*/0, - /*width=*/1); - - info.ctrlValues.reserve(info.ctrlQubits.size()); - info.ctrlValues.append(posCtrlQubits.size(), one); // +controls => 1 - info.ctrlValues.append(negCtrlQubits.size(), zero); // -controls => 0 - - return info; -} - -/// Helper function to extract operands and control info - for more complex -/// cases -template struct ExtractedOperands { - ValueRange inQubits; - ControlInfo ctrlInfo; -}; - -template -ExtractedOperands -extractOperands(OpAdaptor adaptor, ConversionPatternRewriter& rewriter, - Location loc) { - const ValueRange inQubits = adaptor.getInQubits(); - const ValueRange posCtrlQubits = adaptor.getPosCtrlInQubits(); - const ValueRange negCtrlQubits = adaptor.getNegCtrlInQubits(); - - const ControlInfo ctrlInfo = - extractControlInfo(posCtrlQubits, negCtrlQubits, rewriter, loc); - - return {inQubits, ctrlInfo}; -} - -} // anonymous namespace - -class MQTOptToCatalystQuantumTypeConverter final : public TypeConverter { -public: - explicit MQTOptToCatalystQuantumTypeConverter(MLIRContext* ctx) { - // Identity conversion for types that don't need transformation - addConversion([](const Type type) { return type; }); - - // Convert MemRef of MQTOpt QubitType to Catalyst QuregType - // Also handles memrefs where the element type was already converted - addConversion([ctx](MemRefType memrefType) -> Type { - auto elemType = memrefType.getElementType(); - if (isa(elemType) || - isa(elemType)) { - return catalyst::quantum::QuregType::get(ctx); - } - return memrefType; - }); - - // Convert MQTOpt QubitType to Catalyst QubitType - addConversion([ctx](opt::QubitType /*type*/) -> Type { - return catalyst::quantum::QubitType::get(ctx); - }); - } -}; - -struct ConvertMQTOptAlloc final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(memref::AllocOp op, OpAdaptor /*adaptor*/, - ConversionPatternRewriter& rewriter) const override { - // Only convert memrefs of qubit type - auto memrefType = dyn_cast(op.getType()); - auto elemType = memrefType ? memrefType.getElementType() : Type(); - if (!memrefType || !(isa(elemType) || - isa(elemType))) { - return failure(); - } - - // Only handle ranked memrefs - auto rankedMemrefType = dyn_cast(memrefType); - if (!rankedMemrefType) { - return failure(); - } - - // Prepare the result type(s) - const auto resultType = - catalyst::quantum::QuregType::get(rewriter.getContext()); - - // Get the size from memref type or dynamic operands - Value size = nullptr; - mlir::IntegerAttr nqubitsAttr = nullptr; - - // Check if this is a statically shaped memref - if (rankedMemrefType.hasStaticShape() && - rankedMemrefType.getNumElements() >= 0) { - // For static memref: use attribute (no operand) - nqubitsAttr = - rewriter.getI64IntegerAttr(rankedMemrefType.getNumElements()); - } else { - // For dynamic memref: check if the size is actually a constant - auto dynamicOperands = op.getDynamicSizes(); - const Value dynamicSize = - dynamicOperands.empty() ? nullptr : dynamicOperands[0]; - - if (dynamicSize) { - // Try to recover static size from constant operand - if (auto constOp = - dynamicSize.getDefiningOp()) { - // The size is a constant index, use it as an attribute instead - nqubitsAttr = rewriter.getI64IntegerAttr(constOp.value()); - } else if (auto constOp = - dynamicSize.getDefiningOp()) { - // The size is a constant int, use it as an attribute instead - nqubitsAttr = rewriter.getI64IntegerAttr(constOp.value()); - } else { - // Truly dynamic size - use operand - size = dynamicSize; - // quantum.alloc expects i64, but memref size is index type - if (mlir::isa(size.getType())) { - size = rewriter.create( - op.getLoc(), rewriter.getI64Type(), size); - } - } - } - } - - // Replace with quantum alloc operation - rewriter.replaceOpWithNewOp(op, resultType, - size, nqubitsAttr); - - return success(); - } -}; - -struct ConvertMQTOptDealloc final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(memref::DeallocOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Only convert memrefs of qubit type - auto memrefType = dyn_cast(op.getMemref().getType()); - auto elemType = memrefType ? memrefType.getElementType() : Type(); - if (!memrefType || !(isa(elemType) || - isa(elemType))) { - return failure(); - } - - // Create the new operation - const auto catalystOp = rewriter.create( - op.getLoc(), TypeRange({}), adaptor.getMemref()); - - // Replace the original with the new operation - rewriter.replaceOp(op, catalystOp); - return success(); - } -}; - -struct ConvertMQTOptMeasure final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::MeasureOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - - // Extract operand(s) - auto inQubit = adaptor.getInQubit(); - - // Prepare the result type(s) - auto qubitType = catalyst::quantum::QubitType::get(rewriter.getContext()); - auto bitType = rewriter.getI1Type(); - - // Create the new operation - const auto catalystOp = rewriter.create( - op.getLoc(), bitType, qubitType, inQubit, - /*optional::mlir::IntegerAttr postselect=*/nullptr); - - // Replace all uses of both results and then erase the operation - const auto catalystMeasure = catalystOp->getResult(0); - const auto catalystQubit = catalystOp->getResult(1); - rewriter.replaceOp(op, ValueRange{catalystQubit, catalystMeasure}); - return success(); - } -}; - -struct ConvertMQTOptLoad final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(memref::LoadOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Only convert loads of qubit type - if (!(isa(op.getType()) || - isa(op.getType()))) { - return failure(); - } - - // Prepare the result type(s) - auto resultType = catalyst::quantum::QubitType::get(rewriter.getContext()); - - // Get index (assuming single index for 1D memref) - auto indices = adaptor.getIndices(); - Value index = indices.empty() ? nullptr : indices[0]; - - // Convert index type to i64 if needed - if (index && mlir::isa(index.getType())) { - index = rewriter.create(op.getLoc(), - rewriter.getI64Type(), index); - } - - // Create the new operation - auto catalystOp = rewriter.create( - op.getLoc(), resultType, adaptor.getMemref(), index, nullptr); - - // Replace the load operation with the extracted qubit - rewriter.replaceOp(op, catalystOp.getResult()); - return success(); - } -}; - -struct ConvertMQTOptStore final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(memref::StoreOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Only convert stores to memrefs with qubit element type - auto memrefType = dyn_cast(op.getMemRef().getType()); - auto elemType = memrefType ? memrefType.getElementType() : Type(); - if (!memrefType || !(isa(elemType) || - isa(elemType))) { - return failure(); - } - - // Get indices (assuming single index for 1D memref) - auto indices = adaptor.getIndices(); - Value index = indices.empty() ? nullptr : indices[0]; - - // Convert index type to i64 if needed - if (index && mlir::isa(index.getType())) { - index = rewriter.create(op.getLoc(), - rewriter.getI64Type(), index); - } - - // Prepare the result type(s) - auto resultType = catalyst::quantum::QuregType::get(rewriter.getContext()); - - // Create the new operation - rewriter.create(op.getLoc(), resultType, - adaptor.getMemref(), index, - nullptr, adaptor.getValue()); - - // Erase the original store operation (store has no results to replace) - rewriter.eraseOp(op); - return success(); - } -}; - -struct ConvertMQTOptCast final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(memref::CastOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Only convert if it's a cast between qubit memrefs - auto srcType = dyn_cast(op.getSource().getType()); - auto dstType = dyn_cast(op.getType()); - auto srcElem = srcType ? srcType.getElementType() : Type(); - auto dstElem = dstType ? dstType.getElementType() : Type(); - - if (!srcType || !dstType || - !(isa(srcElem) || - isa(srcElem)) || - !(isa(dstElem) || - isa(dstElem))) { - return failure(); - } - - // Both should convert to !quantum.reg - rewriter.replaceOp(op, adaptor.getSource()); - return success(); - } -}; - -template -struct ConvertMQTOptSimpleGate final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(MQTGateOp op, typename MQTGateOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // BarrierOp has no semantic effect. - if (std::is_same_v) { - rewriter.eraseOp(op); - return success(); - } - - // Extract operands and control information using helper function - auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); - - // Gate name may depend on number of controls - const StringRef gateName = - getGateName(extracted.ctrlInfo.ctrlQubits.size()); - if (gateName.empty()) { - return op->emitError() - << "Unsupported controlled gate for op: " << op->getName(); - } - - // Sanity: lengths must match, or the op verifier will complain. - if (extracted.ctrlInfo.ctrlQubits.size() != - extracted.ctrlInfo.ctrlValues.size()) { - return op->emitError() - << "control qubits and control values size mismatch"; - } - - // Create CustomOp - auto custom = rewriter.create( - op.getLoc(), - /*gate=*/gateName, - /*in_qubits=*/extracted.inQubits, - /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/adaptor.getParams(), - /*adjoint=*/false); - - // ---- Replace: CustomOp results are (out_qubits, out_ctrl_qubits) ---- - SmallVector replacements; - replacements.append(custom.getOutQubits().begin(), - custom.getOutQubits().end()); - replacements.append(custom.getOutCtrlQubits().begin(), - custom.getOutCtrlQubits().end()); - - rewriter.replaceOp(op, replacements); - return success(); - } - -private: - // Is specialized for each gate type - static StringRef getGateName(std::size_t numControls); -}; - -template -struct ConvertMQTOptAdjointGate final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(MQTGateOp op, typename MQTGateOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Get the base gate name and whether it is an adjoint version - const auto& [gateName, adjoint] = getGateInfo(); - - // Extract control information - const ControlInfo ctrlInfo = - extractControlInfo(adaptor.getPosCtrlInQubits(), - adaptor.getNegCtrlInQubits(), rewriter, op.getLoc()); - - // Create CustomOp with adjoint flag - auto catalystOp = rewriter.create( - op.getLoc(), - /*gate=*/gateName, - /*in_qubits=*/adaptor.getInQubits(), - /*in_ctrl_qubits=*/ctrlInfo.ctrlQubits, - /*in_ctrl_values=*/ctrlInfo.ctrlValues, - /*params=*/adaptor.getParams(), - /*adjoint=*/adjoint); - - rewriter.replaceOp(op, catalystOp); - return success(); - } - -private: - template static std::pair getGateInfo() { - if constexpr (std::is_same_v) { - return {"S", true}; - } else if constexpr (std::is_same_v) { - return {"T", true}; - } else if constexpr (std::is_same_v) { - return {"ISWAP", true}; - } else if constexpr (std::is_same_v) { - return {"SX", true}; - } - // Default case - return {"", false}; - } -}; - -// Conversions of unsupported gates, which need decomposition -template <> -struct ConvertMQTOptSimpleGate final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::VOp op, opt::VOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Extract operands and control information using helper function - auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); - - // V = RZ(π/2) RY(π/2) RZ(-π/2) - auto pi2 = rewriter.create( - op.getLoc(), rewriter.getF64FloatAttr(std::numbers::pi / 2.0)); - - // Create the decomposed operations - auto rz1 = rewriter.create( - op.getLoc(), - /*gate_name=*/"RZ", - /*in_qubits=*/extracted.inQubits, - /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{pi2}, - /*adjoint=*/false); - - auto ry = rewriter.create( - op.getLoc(), - /*gate_name=*/"RY", - /*in_qubits=*/rz1.getOutQubits(), - /*in_ctrl_qubits=*/rz1.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{pi2}, - /*adjoint=*/false); - - auto rz2 = rewriter.create( - op.getLoc(), - /*gate_name=*/"RZ", - /*in_qubits=*/ry.getOutQubits(), - /*in_ctrl_qubits=*/ry.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{pi2}, - /*adjoint=*/true); - - // ---- Replace: CustomOp results are (out_qubits, out_ctrl_qubits) ---- - SmallVector replacements; - replacements.append(rz2.getOutQubits().begin(), rz2.getOutQubits().end()); - replacements.append(rz2.getOutCtrlQubits().begin(), - rz2.getOutCtrlQubits().end()); - - rewriter.replaceOp(op, replacements); - return success(); - } -}; - -template <> -struct ConvertMQTOptSimpleGate final - : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::VdgOp op, opt::VdgOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Extract operands and control information using helper function - auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); - - // V† = RZ(π/2) RY(-π/2) RZ(-π/2) - auto negPi2 = rewriter.create( - op.getLoc(), rewriter.getF64FloatAttr(-std::numbers::pi / 2.0)); - - // Create the decomposed operations - auto rz1 = rewriter.create( - op.getLoc(), - /*gate=*/"RZ", - /*in_qubits=*/extracted.inQubits, - /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{negPi2}, - /*adjoint=*/true); - - auto ry = rewriter.create( - op.getLoc(), - /*gate=*/"RY", - /*in_qubits=*/rz1.getOutQubits(), - /*in_ctrl_qubits=*/rz1.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{negPi2}, - /*adjoint=*/false); - - auto rz2 = rewriter.create( - op.getLoc(), - /*gate=*/"RZ", - /*in_qubits=*/ry.getOutQubits(), - /*in_ctrl_qubits=*/ry.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{negPi2}, - /*adjoint=*/false); - - // ---- Replace: CustomOp results are (out_qubits, out_ctrl_qubits) ---- - SmallVector replacements; - replacements.append(rz2.getOutQubits().begin(), rz2.getOutQubits().end()); - replacements.append(rz2.getOutCtrlQubits().begin(), - rz2.getOutCtrlQubits().end()); - - rewriter.replaceOp(op, replacements); - return success(); - } -}; - -template <> -struct ConvertMQTOptSimpleGate final - : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::DCXOp op, opt::DCXOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Extract operands and control information using helper function - auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); - - // DCX = CNOT(q2,q1) CNOT(q1,q2) - auto cnot1 = rewriter.create( - op.getLoc(), - /*gate=*/"CNOT", - /*in_qubits=*/extracted.inQubits, - /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{}, - /*adjoint=*/false); - - auto cnot2 = rewriter.create( - op.getLoc(), - /*gate=*/"CNOT", - /*in_qubits=*/ - ValueRange{cnot1.getOutQubits()[1], cnot1.getOutQubits()[0]}, - /*in_ctrl_qubits=*/cnot1.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{}, - /*adjoint=*/false); - - // ---- Replace: CustomOp results are (out_qubits, out_ctrl_qubits) ---- - SmallVector replacements; - replacements.append(cnot2.getOutQubits().begin(), - cnot2.getOutQubits().end()); - replacements.append(cnot2.getOutCtrlQubits().begin(), - cnot2.getOutCtrlQubits().end()); - - rewriter.replaceOp(op, replacements); - return success(); - } -}; - -template <> -struct ConvertMQTOptSimpleGate final - : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::RZXOp op, opt::RZXOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Extract operands and control information using helper function - auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); - - // RZX(q0, q1; θ) = H(q1) · RZZ(q0, q1; θ) · H(q1) - // H gates stay uncontrolled; they cancel if control on RZZ is not active - - // H on q1 - auto h1 = rewriter.create( - op.getLoc(), - /*gate=*/"Hadamard", - /*in_qubits=*/ValueRange{extracted.inQubits[1]}, - /*in_ctrl_qubits=*/ValueRange{}, - /*in_ctrl_values=*/ValueRange{}, - /*params=*/ValueRange{}, - /*adjoint=*/false); - - // RZZ on (q0, q1') - if (adaptor.getParams().empty()) { - return op.emitError("RZX expects one parameter"); - } - auto rzz = rewriter.create( - op.getLoc(), - /*gate=*/"IsingZZ", - /*in_qubits=*/ValueRange{extracted.inQubits[0], h1.getOutQubits()[0]}, - /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/adaptor.getParams(), - /*adjoint=*/false); - - // H on q1'' - auto h2 = rewriter.create( - op.getLoc(), - /*gate=*/"Hadamard", - /*in_qubits=*/ValueRange{rzz.getOutQubits()[1]}, - /*in_ctrl_qubits=*/ValueRange{}, - /*in_ctrl_values=*/ValueRange{}, - /*params=*/ValueRange{}, - /*adjoint=*/false); - - // Final results in mqt.opt ordering (targets..., controls...) - SmallVector finalResults; - finalResults.push_back(rzz.getOutQubits()[0]); // target0 final - finalResults.push_back(h2.getOutQubits()[0]); // target1 final - finalResults.append(rzz.getOutCtrlQubits().begin(), - rzz.getOutCtrlQubits().end()); // controls - - rewriter.replaceOp(op, finalResults); - return success(); - } -}; - -template <> -struct ConvertMQTOptSimpleGate final - : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::GPhaseOp op, opt::GPhaseOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Extract control information using helper function (no input qubits for - // GPhase) - auto ctrlInfo = - extractControlInfo(adaptor.getPosCtrlInQubits(), - adaptor.getNegCtrlInQubits(), rewriter, op.getLoc()); - const auto params = adaptor.getParams(); - if (params.empty()) { - return op.emitError("GlobalPhaseOp requires exactly one parameter"); - } - - // Create output types for GlobalPhaseOp (control qubits only) - const Type qubitType = - catalyst::quantum::QubitType::get(rewriter.getContext()); - const SmallVector outCtrlTypes(ctrlInfo.ctrlQubits.size(), qubitType); - - auto gphase = rewriter.create( - op.getLoc(), TypeRange(outCtrlTypes), params[0], false, - ctrlInfo.ctrlQubits, ctrlInfo.ctrlValues); - - // Replace the original operation with the decomposition - rewriter.replaceOp(op, gphase.getResults()); - return success(); - } -}; - -template <> -struct ConvertMQTOptSimpleGate final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::UOp op, opt::UOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Extract operands and control information using helper function - auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); - - // Extract parameters - SmallVector paramValues; - auto dynamicParams = adaptor.getParams(); - auto staticParams = op.getStaticParams(); - auto paramMask = op.getParamsMask(); - - // There must be exactly 3 parameters - constexpr size_t numParams = 3; - for (size_t i = 0, dynIdx = 0, statIdx = 0; i < numParams; ++i) { - if (paramMask.has_value()) { - if ((*paramMask)[i]) { - // Static parameter - auto attr = (*staticParams)[statIdx++]; - auto floatAttr = rewriter.getF64FloatAttr(attr); - auto constOp = rewriter.create(op.getLoc(), floatAttr); - paramValues.push_back(constOp); - } else { - // Dynamic parameter - paramValues.push_back(dynamicParams[dynIdx++]); - } - } else if (staticParams.has_value()) { - // All static - auto attr = (*staticParams)[i]; - auto floatAttr = rewriter.getF64FloatAttr(attr); - auto constOp = rewriter.create(op.getLoc(), floatAttr); - paramValues.push_back(constOp); - } else { - // All dynamic - paramValues.push_back(dynamicParams[i]); - } - } - // Now paramValues[0] = θ, [1] = φ, [2] = λ - auto theta = paramValues[0]; - auto phi = paramValues[1]; - auto lambda = paramValues[2]; - - // Based on - // https://docs.quantum.ibm.com/api/qiskit/0.24/qiskit.circuit.library.UGate - // U(θ, φ, λ) = RZ(φ − π⁄2) ⋅ RX(π⁄2) ⋅ RZ(π − θ) ⋅ RX(π⁄2) ⋅ RZ(λ − π⁄2) - // Note: The MQT UOp uses U(θ/2, φ, λ) - auto pi = rewriter.create( - op.getLoc(), rewriter.getF64FloatAttr(std::numbers::pi)); - auto pi2 = rewriter.create( - op.getLoc(), rewriter.getF64FloatAttr(std::numbers::pi / 2.0)); - - // Compute φ - π/2 - auto phiMinusPi2 = rewriter.create(op.getLoc(), phi, pi2); - // Compute π - θ/2 - auto two = - rewriter.create(op.getLoc(), rewriter.getF64FloatAttr(2.0)); - auto theta2 = rewriter.create(op.getLoc(), theta, two); - auto piMinusTheta2 = rewriter.create(op.getLoc(), pi, theta2); - // Compute λ - π/2 - auto lambdaMinusPi2 = rewriter.create(op.getLoc(), lambda, pi2); - - // RZ(λ − π/2) - auto rz1 = rewriter.create( - op.getLoc(), - /*gate=*/"RZ", - /*in_qubits=*/extracted.inQubits, - /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{lambdaMinusPi2}, - /*adjoint=*/false); - - // RX(π/2) - auto rx1 = rewriter.create( - op.getLoc(), - /*gate=*/"RX", - /*in_qubits=*/rz1.getOutQubits(), - /*in_ctrl_qubits=*/rz1.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{pi2}, - /*adjoint=*/false); - - // RZ(π − θ) - auto rz2 = rewriter.create( - op.getLoc(), - /*gate=*/"RZ", - /*in_qubits=*/rx1.getOutQubits(), - /*in_ctrl_qubits=*/rx1.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{piMinusTheta2}, - /*adjoint=*/false); - - // RX(π/2) - auto rx2 = rewriter.create( - op.getLoc(), - /*gate=*/"RX", - /*in_qubits=*/rz2.getOutQubits(), - /*in_ctrl_qubits=*/rz2.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{pi2}, - /*adjoint=*/false); - - // RZ(φ − π/2) - auto rz3 = rewriter.create( - op.getLoc(), - /*gate=*/"RZ", - /*in_qubits=*/rx2.getOutQubits(), - /*in_ctrl_qubits=*/rx2.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{phiMinusPi2}, - /*adjoint=*/false); - - // ---- Replace: CustomOp results are (out_qubits, out_ctrl_qubits) ---- - SmallVector replacements; - replacements.append(rz3.getOutQubits().begin(), rz3.getOutQubits().end()); - replacements.append(rz3.getOutCtrlQubits().begin(), - rz3.getOutCtrlQubits().end()); - - // Replace the original U gate with the decomposed sequence - rewriter.replaceOp(op, replacements); - return success(); - } -}; - -template <> -struct ConvertMQTOptSimpleGate final - : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::U2Op op, opt::U2Op::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Extract operands and control information using helper function - auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); - - // Extract parameters - SmallVector paramValues; - auto dynamicParams = adaptor.getParams(); - auto staticParams = op.getStaticParams(); - auto paramMask = op.getParamsMask(); - - // There must be exactly 2 parameters - constexpr size_t numParams = 2; - for (size_t i = 0, dynIdx = 0, statIdx = 0; i < numParams; ++i) { - if (paramMask.has_value()) { - if ((*paramMask)[i]) { - // Static parameter - auto attr = (*staticParams)[statIdx++]; - auto floatAttr = rewriter.getF64FloatAttr(attr); - auto constOp = rewriter.create(op.getLoc(), floatAttr); - paramValues.push_back(constOp); - } else { - // Dynamic parameter - paramValues.push_back(dynamicParams[dynIdx++]); - } - } else if (staticParams.has_value()) { - // All static - auto attr = (*staticParams)[i]; - auto floatAttr = rewriter.getF64FloatAttr(attr); - auto constOp = rewriter.create(op.getLoc(), floatAttr); - paramValues.push_back(constOp); - } else { - // All dynamic - paramValues.push_back(dynamicParams[i]); - } - } - // Now paramValues [0] = φ, [1] = λ - auto phi = paramValues[0]; - auto lambda = paramValues[1]; - - // U2(φ, λ) = U(π/2, φ, λ) = RZ(φ − π⁄2) ⋅ RX(π⁄2) ⋅ RZ(3/4 π) ⋅ RX(π⁄2) ⋅ - // RZ(λ − π⁄2) - auto pi2 = rewriter.create( - op.getLoc(), rewriter.getF64FloatAttr(std::numbers::pi / 2.0)); - auto pi4 = rewriter.create( - op.getLoc(), rewriter.getF64FloatAttr(std::numbers::pi / 4.0)); - auto three = - rewriter.create(op.getLoc(), rewriter.getF64FloatAttr(3.0)); - auto pi34 = rewriter.create(op.getLoc(), pi4, three); - - // Compute φ - π/2 - auto phiMinusPi2 = rewriter.create(op.getLoc(), phi, pi2); - // Compute λ - π/2 - auto lambdaMinusPi2 = rewriter.create(op.getLoc(), lambda, pi2); - - // RZ(λ − π/2) - auto rz1 = rewriter.create( - op.getLoc(), - /*gate=*/"RZ", - /*in_qubits=*/extracted.inQubits, - /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{lambdaMinusPi2}, - /*adjoint=*/false); - - // RX(π/2) - auto rx1 = rewriter.create( - op.getLoc(), - /*gate=*/"RX", - /*in_qubits=*/rz1.getOutQubits(), - /*in_ctrl_qubits=*/rz1.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{pi2}, - /*adjoint=*/false); - - // RZ(3/4 π) - auto rz2 = rewriter.create( - op.getLoc(), - /*gate=*/"RZ", - /*in_qubits=*/rx1.getOutQubits(), - /*in_ctrl_qubits=*/rx1.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{pi34}, - /*adjoint=*/false); - - // RX(π/2) - auto rx2 = rewriter.create( - op.getLoc(), - /*gate=*/"RX", - /*in_qubits=*/rz2.getOutQubits(), - /*in_ctrl_qubits=*/rz2.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{pi2}, - /*adjoint=*/false); - - // RZ(φ − π/2) - auto rz3 = rewriter.create( - op.getLoc(), - /*gate=*/"RZ", - /*in_qubits=*/rx2.getOutQubits(), - /*in_ctrl_qubits=*/rx2.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{phiMinusPi2}, - /*adjoint=*/false); - - // ---- Replace: CustomOp results are (out_qubits, out_ctrl_qubits) ---- - SmallVector replacements; - replacements.append(rz3.getOutQubits().begin(), rz3.getOutQubits().end()); - replacements.append(rz3.getOutCtrlQubits().begin(), - rz3.getOutCtrlQubits().end()); - - // Replace the original U gate with the decomposed sequence - rewriter.replaceOp(op, replacements); - return success(); - } -}; - -template <> -struct ConvertMQTOptSimpleGate final - : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::PeresOp op, opt::PeresOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Extract operands and control information using helper function - auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); - - // Peres = CNOT(q0, q1) ; X(q0) - - // CNOT(q0, q1) - auto cnot = rewriter.create( - op.getLoc(), - /*gate=*/"CNOT", - /*in_qubits=*/ValueRange{extracted.inQubits[0], extracted.inQubits[1]}, - /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{}, - /*adjoint=*/false); - - const Value q0AfterCnot = cnot.getOutQubits()[0]; - const Value q1AfterCnot = cnot.getOutQubits()[1]; - - // X(q0') - auto x = rewriter.create( - op.getLoc(), - /*gate=*/"PauliX", - /*in_qubits=*/ValueRange{q0AfterCnot}, - /*in_ctrl_qubits=*/cnot.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{}, - /*adjoint=*/false); - - const Value q0Final = x.getOutQubits()[0]; // target0 - const Value q1Final = q1AfterCnot; // target1 - - // Final: (targets..., controls...) - SmallVector finalResults; - finalResults.push_back(q0Final); - finalResults.push_back(q1Final); - finalResults.append(x.getOutCtrlQubits().begin(), - x.getOutCtrlQubits().end()); - - rewriter.replaceOp(op, finalResults); - return success(); - } -}; - -template <> -struct ConvertMQTOptSimpleGate final - : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::PeresdgOp op, opt::PeresdgOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Extract operands and control information using helper function - auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); - - // Peres† = X(q0) ; CNOT(q0, q1) - - // X(q0) - auto x = rewriter.create( - op.getLoc(), - /*gate=*/"PauliX", - /*in_qubits=*/ValueRange{extracted.inQubits[0]}, - /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{}, - /*adjoint=*/false); - - const Value q0AfterX = x.getOutQubits()[0]; - - // CNOT(q0', q1) - auto cnot = rewriter.create( - op.getLoc(), - /*gate=*/"CNOT", - /*in_qubits=*/ValueRange{q0AfterX, extracted.inQubits[1]}, - /*in_ctrl_qubits=*/x.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{}, - /*adjoint=*/false); - - // Final: (targets..., controls...) - SmallVector finalResults; - finalResults.push_back(cnot.getOutQubits()[0]); // q0_final - finalResults.push_back(cnot.getOutQubits()[1]); // q1_final - finalResults.append(cnot.getOutCtrlQubits().begin(), - cnot.getOutCtrlQubits().end()); // controls - - rewriter.replaceOp(op, finalResults); - return success(); - } -}; - -// -- IOp (Identity) -template <> -StringRef ConvertMQTOptSimpleGate::getGateName( - [[maybe_unused]] std::size_t numControls) { - return "Identity"; -} - -// -- XOp (PauliX, CNOT, Toffoli) -template <> -StringRef -ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { - if (numControls == 1) { - return "CNOT"; - } - if (numControls == 2) { - return "Toffoli"; - } - // 0 or 3+ controls - return "PauliX"; -} - -// -- YOp (PauliY, CY for 1 control, PauliY for 2+ controls) -template <> -StringRef -ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { - // CY is the special name for exactly 1 control - if (numControls == 1) { - return "CY"; - } - // 0 or 2+ controls - return "PauliY"; -} - -// -- ZOp (PauliZ, CZ for 1 control, PauliZ for 2+ controls) -template <> -StringRef -ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { - // CZ is the special name for exactly 1 control - if (numControls == 1) { - return "CZ"; - } - // 0 or 2+ controls - return "PauliZ"; -} - -// -- HOp (Hadamard) -template <> -StringRef ConvertMQTOptSimpleGate::getGateName( - [[maybe_unused]] std::size_t numControls) { - return "Hadamard"; -} - -// -- SOP (S) -template <> -StringRef ConvertMQTOptSimpleGate::getGateName( - [[maybe_unused]] std::size_t numControls) { - return "S"; -} - -// -- SXOp (Sqrt X) -template <> -StringRef ConvertMQTOptSimpleGate::getGateName( - [[maybe_unused]] std::size_t numControls) { - return "SX"; -} - -// -- TOP (T) -template <> -StringRef ConvertMQTOptSimpleGate::getGateName( - [[maybe_unused]] std::size_t numControls) { - return "T"; -} - -// -- ECROp (ECR) -template <> -StringRef ConvertMQTOptSimpleGate::getGateName( - [[maybe_unused]] std::size_t numControls) { - return "ECR"; -} - -// -- SWAPOp (SWAP) -template <> -StringRef ConvertMQTOptSimpleGate::getGateName( - const std::size_t numControls) { - if (numControls == 1) { - return "CSWAP"; - } - // 0 or 2+ controls - return "SWAP"; -} - -// -- iSWAPOp (iSWAP) -template <> -StringRef ConvertMQTOptSimpleGate::getGateName( - [[maybe_unused]] std::size_t numControls) { - return "ISWAP"; -} - -// -- RXOp (RX, CRX) -template <> -StringRef -ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { - if (numControls == 1) { - return "CRX"; - } - // 0 or 2+ controls - return "RX"; -} - -// -- RYOp (RY, CRY) -template <> -StringRef -ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { - if (numControls == 1) { - return "CRY"; - } - // 0 or 2+ controls - return "RY"; -} - -// -- RZOp (RZ, CRZ) -template <> -StringRef -ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { - if (numControls == 1) { - return "CRZ"; - } - // 0 or 2+ controls - return "RZ"; -} - -// -- POp (PhaseShift, ControlledPhaseShift) -template <> -StringRef -ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { - if (numControls == 1) { - return "ControlledPhaseShift"; - } - // 0 or 2+ controls - return "PhaseShift"; -} - -// -- RXXOp (IsingXX) -template <> -StringRef ConvertMQTOptSimpleGate::getGateName( - [[maybe_unused]] std::size_t numControls) { - return "IsingXX"; -} - -// -- RYYOp (IsingYY) -template <> -StringRef ConvertMQTOptSimpleGate::getGateName( - [[maybe_unused]] std::size_t numControls) { - return "IsingYY"; -} - -// -- RZZ (IsingZZ) -template <> -StringRef ConvertMQTOptSimpleGate::getGateName( - [[maybe_unused]] std::size_t numControls) { - return "IsingZZ"; -} - -template <> -struct ConvertMQTOptSimpleGate final - : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::XXminusYYOp op, opt::XXminusYYOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Extract operands and control information. - auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); - - // Gather parameters (phi, beta) handling static/dynamic mask - auto params = adaptor.getParams(); - auto staticParams = op.getStaticParams(); - auto paramMask = op.getParamsMask(); - - SmallVector paramValues; - size_t dynamicIdx = 0; - size_t staticIdx = 0; - - for (size_t i = 0; i < 2; ++i) { - if (paramMask && (*paramMask)[i]) { - // Static parameter. - auto floatAttr = rewriter.getF64FloatAttr((*staticParams)[staticIdx++]); - paramValues.push_back( - rewriter.create(op.getLoc(), floatAttr)); - } else { - // Dynamic parameter. - paramValues.push_back(params[dynamicIdx++]); - } - } - - const Value phi = paramValues[0]; // First parameter. - const Value beta = paramValues[1]; // Second parameter. - - // Create constant for pi. - auto pi = rewriter.create( - op.getLoc(), rewriter.getF64FloatAttr(std::numbers::pi)); - - // Compute beta - pi and pi - beta. - auto betaMinusPi = rewriter.create(op.getLoc(), beta, pi); - auto piMinusBeta = rewriter.create(op.getLoc(), pi, beta); - - // Conjugation identity: - // XXminusYY = (X ⊗ I) · (XXplusYY) · (X ⊗ I) - // Apply X on qubit 0, then same decomposition as XXplusYY, then undo - // X. - - // Pre-conjugation X on qubit 0 (respect original control semantics). - auto xPre = rewriter.create( - op.getLoc(), - /*gate=*/"PauliX", - /*in_qubits=*/ValueRange{extracted.inQubits[0]}, - /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{}, - /*adjoint=*/false); - - // Apply RZ(pi - beta) on qubit 1 (second qubit) using control output from - // X. - auto rz1 = rewriter.create( - op.getLoc(), - /*gate=*/"RZ", - /*in_qubits=*/ValueRange{extracted.inQubits[1]}, - /*in_ctrl_qubits=*/xPre.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{piMinusBeta}, - /*adjoint=*/false); - - // Apply IsingXY(phi) on both qubits. - // Use outputs from xPre and rz1 as the inputs to IsingXY to preserve SSA - // flow. - auto isingxy = rewriter.create( - op.getLoc(), - /*gate=*/"IsingXY", - /*in_qubits=*/ValueRange{xPre.getOutQubits()[0], rz1.getOutQubits()[0]}, - /*in_ctrl_qubits=*/rz1.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{phi}, - /*adjoint=*/false); - - // Apply RZ(beta - pi) on qubit 1 after IsingXY. - auto rz2 = rewriter.create( - op.getLoc(), - /*gate=*/"RZ", - /*in_qubits=*/ValueRange{isingxy.getOutQubits()[1]}, - /*in_ctrl_qubits=*/isingxy.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{betaMinusPi}, - /*adjoint=*/false); - - // Post-conjugation X on qubit 0 to undo the pre X. - auto xPost = rewriter.create( - op.getLoc(), - /*gate=*/"PauliX", - /*in_qubits=*/ValueRange{isingxy.getOutQubits()[0]}, - /*in_ctrl_qubits=*/rz2.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{}, - /*adjoint=*/false); - - // Final results: (q0_final, q1_final, controls...) - SmallVector finalResults; - finalResults.push_back(xPost.getOutQubits()[0]); // q0 after undo-X. - finalResults.push_back(rz2.getOutQubits()[0]); // q1 after final RZ. - finalResults.append(xPost.getOutCtrlQubits().begin(), - xPost.getOutCtrlQubits().end()); - - rewriter.replaceOp(op, finalResults); - return success(); - } -}; - -// -- XXplusYY (IsingXY) - Special handling with decomposition -template <> -struct ConvertMQTOptSimpleGate final - : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(opt::XXplusYYOp op, opt::XXplusYYOp::Adaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Extract operands and control information - auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); - - // XXplusYY(phi, beta) = (I ⊗ Rz(beta - pi)) · IsingXY(phi) · (I ⊗ Rz(pi - - // beta)) We need to extract both parameters - - auto params = adaptor.getParams(); - auto staticParams = op.getStaticParams(); - auto paramMask = op.getParamsMask(); - - // Extract parameters: phi (theta) and beta - SmallVector paramValues; - size_t dynamicIdx = 0; - size_t staticIdx = 0; - - for (size_t i = 0; i < 2; ++i) { - if (paramMask && (*paramMask)[i]) { - // Static parameter - auto floatAttr = rewriter.getF64FloatAttr((*staticParams)[staticIdx++]); - paramValues.push_back( - rewriter.create(op.getLoc(), floatAttr)); - } else { - // Dynamic parameter - paramValues.push_back(params[dynamicIdx++]); - } - } - - const Value phi = paramValues[0]; // First parameter - const Value beta = paramValues[1]; // Second parameter - - // Create constants for pi - auto pi = rewriter.create( - op.getLoc(), rewriter.getF64FloatAttr(std::numbers::pi)); - - // Compute beta - pi - auto betaMinusPi = rewriter.create(op.getLoc(), beta, pi); - - // Compute pi - beta - auto piMinusBeta = rewriter.create(op.getLoc(), pi, beta); - - // Apply Rz(pi - beta) on qubit 1 (second qubit) - auto rz1 = rewriter.create( - op.getLoc(), - /*gate=*/"RZ", - /*in_qubits=*/ValueRange{extracted.inQubits[1]}, - /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{piMinusBeta}, - /*adjoint=*/false); - - // Apply IsingXY(phi) on both qubits - auto isingxy = rewriter.create( - op.getLoc(), - /*gate=*/"IsingXY", - /*in_qubits=*/ValueRange{extracted.inQubits[0], rz1.getOutQubits()[0]}, - /*in_ctrl_qubits=*/rz1.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{phi}, - /*adjoint=*/false); - - // Apply Rz(beta - pi) on qubit 1 (second qubit) - auto rz2 = rewriter.create( - op.getLoc(), - /*gate=*/"RZ", - /*in_qubits=*/ValueRange{isingxy.getOutQubits()[1]}, - /*in_ctrl_qubits=*/isingxy.getOutCtrlQubits(), - /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, - /*params=*/ValueRange{betaMinusPi}, - /*adjoint=*/false); - - // Final results: (q0_final, q1_final, controls...) - SmallVector finalResults; - finalResults.push_back(isingxy.getOutQubits()[0]); // q0 from IsingXY - finalResults.push_back(rz2.getOutQubits()[0]); // q1 after final Rz - finalResults.append(rz2.getOutCtrlQubits().begin(), - rz2.getOutCtrlQubits().end()); - - rewriter.replaceOp(op, finalResults); - return success(); - } -}; - -struct MQTOptToCatalystQuantum final - : impl::MQTOptToCatalystQuantumBase { - using MQTOptToCatalystQuantumBase::MQTOptToCatalystQuantumBase; - - void runOnOperation() override { - MLIRContext* context = &getContext(); - auto* module = getOperation(); - - ConversionTarget target(*context); - target.addLegalDialect(); - target.addLegalDialect(); - target.addLegalDialect(); - target.addIllegalDialect(); - - // Mark memref operations on qubits as illegal to trigger conversion - target.addDynamicallyLegalOp([](memref::AllocOp op) { - auto memrefType = dyn_cast(op.getType()); - if (!memrefType) { - return true; - } - auto elementType = memrefType.getElementType(); - return !isa(elementType); - }); - - target.addDynamicallyLegalOp([](memref::DeallocOp op) { - auto memrefType = dyn_cast(op.getMemref().getType()); - if (!memrefType) { - return true; - } - auto elementType = memrefType.getElementType(); - return !isa(elementType); - }); - - target.addDynamicallyLegalOp([](memref::LoadOp op) { - auto memrefType = dyn_cast(op.getMemRef().getType()); - if (!memrefType) { - return true; - } - auto elementType = memrefType.getElementType(); - return !isa(elementType); - }); - - target.addDynamicallyLegalOp([](memref::StoreOp op) { - auto memrefType = dyn_cast(op.getMemRef().getType()); - if (!memrefType) { - return true; - } - auto elementType = memrefType.getElementType(); - return !isa(elementType); - }); - - target.addDynamicallyLegalOp([](memref::CastOp op) { - auto memrefType = dyn_cast(op.getType()); - if (!memrefType) { - return true; - } - auto elementType = memrefType.getElementType(); - return !(isa(elementType) || - isa(elementType)); - }); - - const MQTOptToCatalystQuantumTypeConverter typeConverter(context); - RewritePatternSet patterns(context); - - patterns.add( - typeConverter, context); - - patterns.add>(typeConverter, - context); - patterns.add>(typeConverter, - context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, - context); - - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, context); - patterns.add>(typeConverter, - context); - patterns.add>(typeConverter, - context); - patterns.add>(typeConverter, - context); - - // Type conversion boilerplate to handle function signatures and control - // flow See: https://www.jeremykun.com/2023/10/23/mlir-dialect-conversion - - // Convert func.func signatures to use the converted types - populateFunctionOpInterfaceTypeConversionPattern( - patterns, typeConverter); - - // Mark func.func as legal only if signature and body types are converted - target.addDynamicallyLegalOp([&](Operation* op) { - if (auto funcOp = dyn_cast(op)) { - return typeConverter.isSignatureLegal(funcOp.getFunctionType()) && - typeConverter.isLegal(&funcOp.getBody()); - } - return true; // Not a FuncOp, treat as legal (not our concern) - }); - - // Convert return ops to match the new function result types - populateReturnOpTypeConversionPattern(patterns, typeConverter); - - // Mark func.return as legal only if operand types match converted types - target.addDynamicallyLegalOp([&](Operation* op) { - if (isa(op)) { - return typeConverter.isLegal(op); - } - return true; - }); - - // Convert call sites to use the converted argument and result types - populateCallOpTypeConversionPattern(patterns, typeConverter); - - // Mark func.call as legal only if operand and result types are converted - target.addDynamicallyLegalOp([&](Operation* op) { - if (isa(op)) { - return typeConverter.isLegal(op); - } - return true; - }); - - // Convert control-flow ops (cf.br, cf.cond_br, etc.) - populateBranchOpInterfaceTypeConversionPattern(patterns, typeConverter); - - // Mark unknown ops as legal if they don't require type conversion - target.markUnknownOpDynamicallyLegal([&](Operation* op) { - return isNotBranchOpInterfaceOrReturnLikeOp(op) || - isLegalForBranchOpInterfaceTypeConversionPattern(op, - typeConverter) || - isLegalForReturnOpTypeConversionPattern(op, typeConverter); - }); - - if (failed(applyPartialConversion(module, target, std::move(patterns)))) { - signalPassFailure(); - } - } -}; - -} // namespace mqt::ir::conversions diff --git a/lib/Conversion/MQTOptToCatalystQuantum/CMakeLists.txt b/lib/Conversion/QCToCatalystQuantum/CMakeLists.txt similarity index 57% rename from lib/Conversion/MQTOptToCatalystQuantum/CMakeLists.txt rename to lib/Conversion/QCToCatalystQuantum/CMakeLists.txt index 4665150..0eab06e 100644 --- a/lib/Conversion/MQTOptToCatalystQuantum/CMakeLists.txt +++ b/lib/Conversion/QCToCatalystQuantum/CMakeLists.txt @@ -7,28 +7,28 @@ # Licensed under the MIT License add_mlir_library( - MQTOptToCatalystQuantum - MQTOptToCatalystQuantum.cpp + QCToCatalystQuantum + QCToCatalystQuantum.cpp LINK_LIBS - MLIRMQTOpt + MLIRQCDialect MLIRTransforms MLIRFuncDialect DEPENDS - MQTOptToCatalystQuantumIncGen) + QCToCatalystQuantumIncGen) -target_compile_features(MQTOptToCatalystQuantum PUBLIC cxx_std_20) -target_compile_options(MQTOptToCatalystQuantum +target_compile_features(QCToCatalystQuantum PUBLIC cxx_std_20) +target_compile_options(QCToCatalystQuantum PRIVATE $<$:-fexceptions>) -target_include_directories(MQTOptToCatalystQuantum PRIVATE ${CATALYST_INCLUDE_DIRS}) +target_include_directories(QCToCatalystQuantum PRIVATE ${CATALYST_INCLUDE_DIRS}) file(GLOB_RECURSE CONVERSION_HEADERS_SOURCE - "${MQT_MLIR_PLUGIN_SOURCE_INCLUDE_DIR}/mlir/Conversion/MQTOptToCatalystQuantum/*.h") + "${MQT_MLIR_PLUGIN_SOURCE_INCLUDE_DIR}/mlir/Conversion/QCToCatalystQuantum/*.h") file(GLOB_RECURSE CONVERSION_HEADERS_BUILD - "${MQT_MLIR_PLUGIN_BUILD_INCLUDE_DIR}/mlir/Conversion/MQTOptToCatalystQuantum/*.inc") + "${MQT_MLIR_PLUGIN_BUILD_INCLUDE_DIR}/mlir/Conversion/QCToCatalystQuantum/*.inc") # add public headers using file sets target_sources( - MQTOptToCatalystQuantum + QCToCatalystQuantum PUBLIC FILE_SET HEADERS BASE_DIRS @@ -43,6 +43,6 @@ target_sources( ${CONVERSION_HEADERS_BUILD}) if(ENABLE_COVERAGE) - target_compile_options(MQTOptToCatalystQuantum PRIVATE --coverage -O0) - target_link_options(MQTOptToCatalystQuantum PRIVATE --coverage) + target_compile_options(QCToCatalystQuantum PRIVATE --coverage -O0) + target_link_options(QCToCatalystQuantum PRIVATE --coverage) endif() diff --git a/lib/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.cpp b/lib/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.cpp new file mode 100644 index 0000000..6f7c6cf --- /dev/null +++ b/lib/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.cpp @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2025 - 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/QCToCatalystQuantum/QCToCatalystQuantum.h" // NOLINT(misc-include-cleaner) + +#include "mlir/Dialect/QC/IR/QCDialect.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mqt::ir::conversions { + +#define GEN_PASS_DEF_QCTOCATALYSTQUANTUM +#include "mlir/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.h.inc" + +using namespace mlir; +using namespace mlir::arith; + +// Helper functions to reduce code duplication +namespace { + +/// Helper struct to hold control qubit information +struct ControlInfo { + SmallVector ctrlQubits; + SmallVector ctrlValues; + + ControlInfo() noexcept = default; +}; + +/// Extract and concatenate control qubits and create corresponding control +/// values +ControlInfo extractControlInfo(ValueRange posCtrlQubits, + ValueRange negCtrlQubits, + ConversionPatternRewriter& rewriter, + Location loc) { + ControlInfo info; + + // Concatenate controls: [pos..., neg...] (preserve this order consistently) + info.ctrlQubits.reserve(posCtrlQubits.size() + negCtrlQubits.size()); + info.ctrlQubits.append(posCtrlQubits.begin(), posCtrlQubits.end()); + info.ctrlQubits.append(negCtrlQubits.begin(), negCtrlQubits.end()); + + if (info.ctrlQubits.empty()) { + return info; + } + + // Create control values: 1 for positive controls, 0 for negative controls + const Value one = + rewriter.create(loc, /*value=*/1, + /*width=*/1); + const Value zero = + rewriter.create(loc, /*value=*/0, + /*width=*/1); + + info.ctrlValues.reserve(info.ctrlQubits.size()); + info.ctrlValues.append(posCtrlQubits.size(), one); // +controls => 1 + info.ctrlValues.append(negCtrlQubits.size(), zero); // -controls => 0 + + return info; +} + +/// Helper function to extract operands and control info - for more complex +/// cases +template struct ExtractedOperands { + ValueRange inQubits; + ControlInfo ctrlInfo; +}; + +template +ExtractedOperands +extractOperands(OpAdaptor adaptor, ConversionPatternRewriter& rewriter, + Location loc) { + const ValueRange inQubits = adaptor.getInQubits(); + const ValueRange posCtrlQubits = adaptor.getPosCtrlInQubits(); + const ValueRange negCtrlQubits = adaptor.getNegCtrlInQubits(); + + const ControlInfo ctrlInfo = + extractControlInfo(posCtrlQubits, negCtrlQubits, rewriter, loc); + + return {inQubits, ctrlInfo}; +} + +} // anonymous namespace + +class QCToCatalystQuantumTypeConverter final : public TypeConverter { +public: + explicit QCToCatalystQuantumTypeConverter(MLIRContext* ctx) { + // Identity conversion for types that don't need transformation + addConversion([](const Type type) { return type; }); + + // Convert MemRef of QC QubitType to Catalyst QuregType + // Also handles memrefs where the element type was already converted + addConversion([ctx](MemRefType memrefType) -> Type { + auto elemType = memrefType.getElementType(); + if (isa(elemType) || + isa(elemType)) { + return catalyst::quantum::QuregType::get(ctx); + } + return memrefType; + }); + + // Convert QC QubitType to Catalyst QubitType + addConversion([ctx](qc::QubitType /*type*/) -> Type { + return catalyst::quantum::QubitType::get(ctx); + }); + } +}; + +struct ConvertQCAlloc final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(memref::AllocOp op, OpAdaptor /*adaptor*/, + ConversionPatternRewriter& rewriter) const override { + // Only convert memrefs of qubit type + auto memrefType = dyn_cast(op.getType()); + auto elemType = memrefType ? memrefType.getElementType() : Type(); + if (!memrefType || !(isa(elemType) || + isa(elemType))) { + return failure(); + } + + // Only handle ranked memrefs + auto rankedMemrefType = dyn_cast(memrefType); + if (!rankedMemrefType) { + return failure(); + } + + // Prepare the result type(s) + const auto resultType = + catalyst::quantum::QuregType::get(rewriter.getContext()); + + // Get the size from memref type or dynamic operands + Value size = nullptr; + mlir::IntegerAttr nqubitsAttr = nullptr; + + // Check if this is a statically shaped memref + if (rankedMemrefType.hasStaticShape() && + rankedMemrefType.getNumElements() >= 0) { + // For static memref: use attribute (no operand) + nqubitsAttr = + rewriter.getI64IntegerAttr(rankedMemrefType.getNumElements()); + } else { + // For dynamic memref: check if the size is actually a constant + auto dynamicOperands = op.getDynamicSizes(); + const Value dynamicSize = + dynamicOperands.empty() ? nullptr : dynamicOperands[0]; + + if (dynamicSize) { + // Try to recover static size from constant operand + if (auto constOp = + dynamicSize.getDefiningOp()) { + // The size is a constant index, use it as an attribute instead + nqubitsAttr = rewriter.getI64IntegerAttr(constOp.value()); + } else if (auto constOp = + dynamicSize.getDefiningOp()) { + // The size is a constant int, use it as an attribute instead + nqubitsAttr = rewriter.getI64IntegerAttr(constOp.value()); + } else { + // Truly dynamic size - use operand + size = dynamicSize; + // quantum.alloc expects i64, but memref size is index type + if (mlir::isa(size.getType())) { + size = rewriter.create( + op.getLoc(), rewriter.getI64Type(), size); + } + } + } + } + + // Replace with quantum alloc operation + rewriter.replaceOpWithNewOp(op, resultType, + size, nqubitsAttr); + + return success(); + } +}; + +struct ConvertQCDealloc final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(memref::DeallocOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Only convert memrefs of qubit type + auto memrefType = dyn_cast(op.getMemref().getType()); + auto elemType = memrefType ? memrefType.getElementType() : Type(); + if (!memrefType || !(isa(elemType) || + isa(elemType))) { + return failure(); + } + + // Create the new operation + const auto catalystOp = rewriter.create( + op.getLoc(), TypeRange({}), adaptor.getMemref()); + + // Replace the original with the new operation + rewriter.replaceOp(op, catalystOp); + return success(); + } +}; + +struct ConvertQCMeasure final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(qc::MeasureOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + + // Extract operand(s) + auto inQubit = adaptor.getQubit(); + + // Prepare the result type(s) + auto qubitType = catalyst::quantum::QubitType::get(rewriter.getContext()); + auto bitType = rewriter.getI1Type(); + + // Create the new operation + const auto catalystOp = rewriter.create( + op.getLoc(), bitType, qubitType, inQubit, + /*optional::mlir::IntegerAttr postselect=*/nullptr); + + // Replace all uses of both results and then erase the operation + const auto catalystMeasure = catalystOp->getResult(0); + const auto catalystQubit = catalystOp->getResult(1); + rewriter.replaceOp(op, ValueRange{catalystQubit, catalystMeasure}); + return success(); + } +}; + +struct ConvertQCLoad final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(memref::LoadOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Only convert loads of qubit type + if (!(isa(op.getType()) || + isa(op.getType()))) { + return failure(); + } + + // Prepare the result type(s) + auto resultType = catalyst::quantum::QubitType::get(rewriter.getContext()); + + // Get index (assuming single index for 1D memref) + auto indices = adaptor.getIndices(); + Value index = indices.empty() ? nullptr : indices[0]; + + // Convert index type to i64 if needed + if (index && mlir::isa(index.getType())) { + index = rewriter.create(op.getLoc(), + rewriter.getI64Type(), index); + } + + // Create the new operation + auto catalystOp = rewriter.create( + op.getLoc(), resultType, adaptor.getMemref(), index, nullptr); + + // Replace the load operation with the extracted qubit + rewriter.replaceOp(op, catalystOp.getResult()); + return success(); + } +}; + +struct ConvertQCStore final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(memref::StoreOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Only convert stores to memrefs with qubit element type + auto memrefType = dyn_cast(op.getMemRef().getType()); + auto elemType = memrefType ? memrefType.getElementType() : Type(); + if (!memrefType || !(isa(elemType) || + isa(elemType))) { + return failure(); + } + + // Get indices (assuming single index for 1D memref) + auto indices = adaptor.getIndices(); + Value index = indices.empty() ? nullptr : indices[0]; + + // Convert index type to i64 if needed + if (index && mlir::isa(index.getType())) { + index = rewriter.create(op.getLoc(), + rewriter.getI64Type(), index); + } + + // Prepare the result type(s) + auto resultType = catalyst::quantum::QuregType::get(rewriter.getContext()); + + // Create the new operation + rewriter.create(op.getLoc(), resultType, + adaptor.getMemref(), index, + nullptr, adaptor.getValue()); + + // Erase the original store operation (store has no results to replace) + rewriter.eraseOp(op); + return success(); + } +}; + +struct ConvertQCCast final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(memref::CastOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Only convert if it's a cast between qubit memrefs + auto srcType = dyn_cast(op.getSource().getType()); + auto dstType = dyn_cast(op.getType()); + auto srcElem = srcType ? srcType.getElementType() : Type(); + auto dstElem = dstType ? dstType.getElementType() : Type(); + + if (!srcType || !dstType || + !(isa(srcElem) || + isa(srcElem)) || + !(isa(dstElem) || + isa(dstElem))) { + return failure(); + } + + // Both should convert to !quantum.reg + rewriter.replaceOp(op, adaptor.getSource()); + return success(); + } +}; + +} // namespace mqt::ir::conversions diff --git a/lib/mqt-plugin.cpp b/lib/mqt-plugin.cpp index feb7a31..9c3fc0a 100644 --- a/lib/mqt-plugin.cpp +++ b/lib/mqt-plugin.cpp @@ -9,7 +9,7 @@ */ #include "mlir/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.h" -// #include "mlir/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.h" +#include "mlir/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include @@ -44,6 +44,6 @@ extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo mlirGetPassPluginInfo() { // Note: mqt::ir::opt::registerQCPasses() is not called to avoid // pulling in transpilation transforms that require LLVM 21 mqt::ir::conversions::registerCatalystQuantumToQCPasses(); - // mqt::ir::conversions::registerQCToCatalystQuantumPasses(); + mqt::ir::conversions::registerQCToCatalystQuantumPasses(); }}; } From 0b1a52a5b481947c73d3b93520c352cc7fb61a3a Mon Sep 17 00:00:00 2001 From: "Kostrzewa, Niklas" Date: Wed, 18 Mar 2026 11:45:19 +0100 Subject: [PATCH 7/7] Clean up code, Fixed CTRL Gates of non-standard gates, update test_plugin to test quantum -> qc conversion (commented out qc -> quantum conversion --- lib/CMakeLists.txt | 4 +- .../CatalystQuantumToQC.cpp | 393 +++++++----------- .../QCToCatalystQuantum.cpp | 26 -- lib/mqt-plugin.cpp | 2 +- test/test_plugin.py | 286 ++++++++----- 5 files changed, 330 insertions(+), 381 deletions(-) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 37f4394..bc9f9f4 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -22,8 +22,8 @@ add_subdirectory(Conversion) set(TARGET_NAME mqt-core-plugins-catalyst) -add_llvm_library(${TARGET_NAME} MODULE mqt-plugin.cpp LINK_LIBS CatalystQuantumToQC - QCToCatalystQuantum) +add_llvm_library(${TARGET_NAME} MODULE mqt-plugin.cpp LINK_LIBS CatalystQuantumToQC) +# QCToCatalystQuantum) # set required C++ standard target_compile_features(${TARGET_NAME} PUBLIC cxx_std_20) diff --git a/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp index f738ee8..691a5b7 100644 --- a/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp +++ b/lib/Conversion/CatalystQuantumToQC/CatalystQuantumToQC.cpp @@ -46,138 +46,6 @@ namespace mqt::ir::conversions { using namespace mlir; using namespace mlir::arith; -namespace { - -/// Partition control qubits into positive and negative control based on their -/// control values. -/// Returns the updated control qubits and values that should be used for the -/// operation. -struct ControlPartitionResult { - SmallVector posCtrlQubits; - SmallVector negCtrlQubits; -}; - -struct ControlLists { - SmallVector posCtrlQubits; - SmallVector negCtrlQubits; -}; - -ControlLists buildControlLists(ValueRange baseControls, - ValueRange additionalPos, - ValueRange additionalNeg) { - ControlLists lists; - lists.posCtrlQubits.append(baseControls.begin(), baseControls.end()); - lists.posCtrlQubits.append(additionalPos.begin(), additionalPos.end()); - lists.negCtrlQubits.append(additionalNeg.begin(), additionalNeg.end()); - return lists; -} - -SmallVector reorderControlledGateResults(Operation* op, - size_t totalCtrlCount) { - SmallVector reordered; - reordered.reserve(totalCtrlCount + 1); - for (size_t idx = 0; idx < totalCtrlCount; ++idx) { - reordered.push_back(op->getResult(1 + idx)); - } - reordered.push_back(op->getResult(0)); - return reordered; -} - -LogicalResult partitionControlQubits(ValueRange inCtrlQubits, - ValueRange inCtrlValues, - ConversionPatternRewriter& rewriter, - Location loc, - ControlPartitionResult& result) { - if (inCtrlQubits.size() != inCtrlValues.size()) { - return rewriter.notifyMatchFailure( - loc, "control qubits and control values size mismatch"); - } - for (size_t i = 0; i < inCtrlQubits.size(); ++i) { - bool isPosCtrl = true; // Default to positive control - bool isConstant = false; - - // Check if control value is a compile-time constant - if (auto constOp = inCtrlValues[i].getDefiningOp()) { - isConstant = true; - if (auto boolAttr = dyn_cast(constOp.getValue())) { - isPosCtrl = boolAttr.getValue(); - } else if (auto intAttr = dyn_cast(constOp.getValue())) { - isPosCtrl = (intAttr.getInt() != 0); - } else { - return rewriter.notifyMatchFailure( - loc, "Control value must be a boolean or integer constant"); - } - } - - // Handle the control qubit based on whether value is constant or dynamic - if (isConstant) { - // Constant control value: use standard pos/neg control - if (isPosCtrl) { - result.posCtrlQubits.emplace_back(inCtrlQubits[i]); - } else { - result.negCtrlQubits.emplace_back(inCtrlQubits[i]); - } - } else { - // TODO: something like if (ctrl_value == 0) { apply X }; apply_op; if - // (ctrl_value - // == 0) { apply X } - rewriter.getContext()->getDiagEngine().emit(loc, - DiagnosticSeverity::Warning) - << "Dynamic control values are not fully supported yet. Treating as " - "positive control. Consider constant folding control values " - "before this pass."; - - result.posCtrlQubits.emplace_back(inCtrlQubits[i]); - } - } - return success(); -} - -struct ParameterInfo { - SmallVector staticParams; - SmallVector paramsMask; - SmallVector finalParams; -}; - -FailureOr processParameters(catalyst::quantum::CustomOp op, - ValueRange paramsValues) { - ParameterInfo info; - auto maskAttr = op->getAttrOfType("params_mask"); - auto staticParamsAttr = op->getAttrOfType("static_params"); - - if (maskAttr && staticParamsAttr) { - size_t staticIdx = 0; - size_t dynamicIdx = 0; - const int64_t maskSize = maskAttr.size(); - const size_t staticParamsCount = staticParamsAttr.size(); - - for (int64_t i = 0; i < maskSize; ++i) { - const bool isStatic = maskAttr[i]; - info.paramsMask.emplace_back(isStatic); - - if (isStatic) { - if (staticIdx >= staticParamsCount) { - return op.emitError("Missing static_params for static mask"); - } - info.staticParams.emplace_back(staticParamsAttr[staticIdx++]); - } else { - if (dynamicIdx >= paramsValues.size()) { - return op.emitError("Too few dynamic parameters"); - } - info.finalParams.emplace_back(paramsValues[dynamicIdx++]); - } - } - } else { - for (auto param : paramsValues) { - info.finalParams.push_back(param); - info.paramsMask.push_back(false); - } - } - return info; -} - -} // namespace - class CatalystQuantumToQCTypeConverter final : public TypeConverter { public: explicit CatalystQuantumToQCTypeConverter(MLIRContext* ctx) { @@ -422,36 +290,9 @@ struct ConvertQuantumGlobalPhase final ConversionPatternRewriter& rewriter) const override { // Extract operand(s) and attribute(s) const auto param = adaptor.getParams(); - const auto inCtrlQubits = adaptor.getInCtrlQubits(); - const auto inCtrlValues = adaptor.getInCtrlValues(); - - // Separate positive and negative control qubits - ControlPartitionResult ctrlResult; - if (failed(partitionControlQubits(inCtrlQubits, inCtrlValues, rewriter, - op.getLoc(), ctrlResult))) { - return failure(); - } - - const auto& inPosCtrlQubitsVec = ctrlResult.posCtrlQubits; - const auto& inNegCtrlQubitsVec = ctrlResult.negCtrlQubits; - - // Create the parameter attributes - const SmallVector staticParamsVec; - SmallVector paramsMaskVec; - SmallVector finalParamValues; - - // All parameters are treated as dynamic during conversion - // Constant folding should be done by canonicalization passes - finalParamValues.push_back(param); - paramsMaskVec.push_back(false); - - const auto staticParams = - DenseF64ArrayAttr::get(rewriter.getContext(), staticParamsVec); - const auto paramsMask = - DenseBoolArrayAttr::get(rewriter.getContext(), paramsMaskVec); // Replace the original with the new operation - rewriter.create(op.getLoc(), finalParamValues[0]); + rewriter.create(op.getLoc(), param); rewriter.eraseOp(op); return success(); } @@ -469,31 +310,10 @@ struct ConvertQuantumCustomOp final const auto paramsValues = adaptor.getParams(); const auto inQubits = adaptor.getInQubits(); const auto inCtrlQubits = adaptor.getInCtrlQubits(); - const auto inCtrlValues = adaptor.getInCtrlValues(); - - // Separate positive and negative control qubits - ControlPartitionResult ctrlResult; - if (failed(partitionControlQubits(inCtrlQubits, inCtrlValues, rewriter, - op.getLoc(), ctrlResult))) { - return failure(); - } - // Save controls from inCtrlQubits separately - they will be appended AFTER - // controls from inQubits (for gates that extract controls from inQubits) - SmallVector additionalPosCtrlQubits = ctrlResult.posCtrlQubits; - SmallVector additionalNegCtrlQubits = ctrlResult.negCtrlQubits; - - auto paramInfoOrError = processParameters(op, paramsValues); - if (failed(paramInfoOrError)) { - return failure(); - } - const auto& paramInfo = *paramInfoOrError; - const auto& finalParamValues = paramInfo.finalParams; - - const auto staticParams = - DenseF64ArrayAttr::get(rewriter.getContext(), paramInfo.staticParams); - const auto paramsMask = - DenseBoolArrayAttr::get(rewriter.getContext(), paramInfo.paramsMask); + // llvm::errs() << "DEBUG: Size of inQubits: " << inQubits.size() << "\n"; + // llvm::errs() << "DEBUG: Size of inCtrlQubits: " << inCtrlQubits.size() << + // "\n"; // Create the new operation Operation* qcOp = nullptr; @@ -505,99 +325,187 @@ struct ConvertQuantumCustomOp final rewriter.create(op.getLoc(), inQubits[0], inQubits[1]) #define CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(GATE_TYPE) \ - rewriter.create(op.getLoc(), inQubits[0], \ - finalParamValues[0]) + rewriter.create(op.getLoc(), inQubits[0], paramsValues[0]) #define CREATE_TWO_TARGET_ONE_PARAMETER_GATE_OP(GATE_TYPE) \ rewriter.create(op.getLoc(), inQubits[0], inQubits[1], \ - finalParamValues[0]) + paramsValues[0]) if (gateName == "Hadamard") { - qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(H); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(H); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(H); + }); + } } else if (gateName == "Identity") { qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Id); } else if (gateName == "PauliX") { - qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(X); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(X); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(X); + }); + } } else if (gateName == "PauliY") { - qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Y); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Y); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Y); + }); + } } else if (gateName == "PauliZ") { - qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Z); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Z); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Z); + }); + } } else if (gateName == "S") { if (op.getAdjoint()) { - qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Sdg); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Sdg); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Sdg); + }); + } } else { - qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(S); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(S); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(S); + }); + } } } else if (gateName == "T") { if (op.getAdjoint()) { - qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Tdg); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Tdg); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(Tdg); + }); + } } else { - qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(T); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(T); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(T); + }); + } } } else if (gateName == "SX") { if (op.getAdjoint()) { - qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(SXdg); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(SXdg); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(SXdg); + }); + } } else { - qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(SX); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(SX); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ZERO_PARAMETER_GATE_OP(SX); + }); + } } } else if (gateName == "ECR") { - qcOp = CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP(ECR); + if (inCtrlQubits.empty()) { + qcOp = CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP(ECR); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP(ECR); + }); + } } else if (gateName == "SWAP") { - qcOp = CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP(SWAP); + if (inCtrlQubits.empty()) { + qcOp = CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP(SWAP); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP(SWAP); + }); + } } else if (gateName == "ISWAP") { - qcOp = CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP(iSWAP); + if (inCtrlQubits.empty()) { + qcOp = CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP(iSWAP); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_TWO_TARGET_ZERO_PARAMETER_GATE_OP(iSWAP); + }); + } } else if (gateName == "RX") { - qcOp = CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(RX); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(RX); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(RX); + }); + } } else if (gateName == "RY") { - qcOp = CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(RY); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(RY); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(RY); + }); + } } else if (gateName == "RZ") { - qcOp = CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(RZ); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(RZ); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(RZ); + }); + } } else if (gateName == "PhaseShift") { - qcOp = CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(P); + if (inCtrlQubits.empty()) { + qcOp = CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(P); + } else { + qcOp = rewriter.create(op.getLoc(), inCtrlQubits, [&]() { + CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP(P); + }); + } } else if (gateName == "CRX") { qcOp = rewriter.create( op.getLoc(), inQubits.take_front(1), [&]() { rewriter.create(op.getLoc(), inQubits[1], - finalParamValues[0]); + paramsValues[0]); }); } else if (gateName == "CRY") { qcOp = rewriter.create( op.getLoc(), inQubits.take_front(1), [&]() { rewriter.create(op.getLoc(), inQubits[1], - finalParamValues[0]); + paramsValues[0]); }); } else if (gateName == "CRZ") { qcOp = rewriter.create( op.getLoc(), inQubits.take_front(1), [&]() { rewriter.create(op.getLoc(), inQubits[1], - finalParamValues[0]); + paramsValues[0]); }); } else if (gateName == "ControlledPhaseShift") { qcOp = rewriter.create( op.getLoc(), inQubits.take_front(1), [&]() { - rewriter.create(op.getLoc(), inQubits[1], - finalParamValues[0]); + rewriter.create(op.getLoc(), inQubits[1], paramsValues[0]); }); } else if (gateName == "IsingXY") { // PennyLane IsingXY has 1 parameter (phi), OpenQASM XXPlusYY needs 2 // (theta, beta) Relationship: IsingXY(phi) = XXPlusYY(phi, pi) - // Add pi as second static parameter (since we add it during compilation) - SmallVector isingxyStaticParams(paramInfo.staticParams.begin(), - paramInfo.staticParams.end()); - isingxyStaticParams.push_back(std::numbers::pi); - - SmallVector isingxyParamsMask(paramInfo.paramsMask.begin(), - paramInfo.paramsMask.end()); - isingxyParamsMask.push_back(true); // pi is a compile-time constant - - auto isingxyStaticParamsAttr = - DenseF64ArrayAttr::get(rewriter.getContext(), isingxyStaticParams); - auto isingxyParamsMaskAttr = - DenseBoolArrayAttr::get(rewriter.getContext(), isingxyParamsMask); - - qcOp = rewriter.create(op.getLoc(), inQubits[0], - inQubits[1], finalParamValues[0], - isingxyStaticParamsAttr[0]); + // Add pi as second parameter (since we add it during compilation) + + qcOp = + rewriter.create(op.getLoc(), inQubits[0], inQubits[1], + paramsValues[0], std::numbers::pi); } else if (gateName == "IsingXX") { qcOp = CREATE_TWO_TARGET_ONE_PARAMETER_GATE_OP(RXX); } else if (gateName == "IsingYY") { @@ -605,26 +513,25 @@ struct ConvertQuantumCustomOp final } else if (gateName == "IsingZZ") { qcOp = CREATE_TWO_TARGET_ONE_PARAMETER_GATE_OP(RZZ); } else if (gateName == "CNOT") { - qcOp = rewriter.create( - op.getLoc(), inQubits.take_front(1), - [&]() { rewriter.create(op.getLoc(), inQubits[1]); }); + qcOp = rewriter.create(op.getLoc(), inQubits[0], [&]() { + rewriter.create(op.getLoc(), inQubits[1]); + }); } else if (gateName == "CY") { - qcOp = rewriter.create( - op.getLoc(), inQubits.take_front(1), - [&]() { rewriter.create(op.getLoc(), inQubits[1]); }); + qcOp = rewriter.create(op.getLoc(), inQubits[0], [&]() { + rewriter.create(op.getLoc(), inQubits[1]); + }); } else if (gateName == "CZ") { - qcOp = rewriter.create( - op.getLoc(), inQubits.take_front(1), - [&]() { rewriter.create(op.getLoc(), inQubits[1]); }); + qcOp = rewriter.create(op.getLoc(), inQubits[0], [&]() { + rewriter.create(op.getLoc(), inQubits[1]); + }); } else if (gateName == "Toffoli") { qcOp = rewriter.create( op.getLoc(), inQubits.take_front(2), [&]() { rewriter.create(op.getLoc(), inQubits[2]); }); } else if (gateName == "CSWAP") { - qcOp = rewriter.create( - op.getLoc(), inQubits.take_front(1), [&]() { - rewriter.create(op.getLoc(), inQubits[1], inQubits[2]); - }); + qcOp = rewriter.create(op.getLoc(), inQubits[0], [&]() { + rewriter.create(op.getLoc(), inQubits[1], inQubits[2]); + }); } else { return op.emitError("Unsupported gate: ") << gateName; } @@ -634,8 +541,14 @@ struct ConvertQuantumCustomOp final #undef CREATE_ONE_TARGET_ONE_PARAMETER_GATE_OP #undef CREATE_TWO_TARGET_ONE_PARAMETER_GATE_OP + llvm::SmallVector combined; + combined.reserve(inQubits.size() + inCtrlQubits.size()); + combined.append(inQubits.begin(), inQubits.end()); + combined.append(inCtrlQubits.begin(), inCtrlQubits.end()); + + mlir::ValueRange combinedRange = mlir::ValueRange(combined); // Replace the original with the new operation - rewriter.replaceOp(op, inQubits); + rewriter.replaceOp(op, combinedRange); return success(); } }; diff --git a/lib/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.cpp b/lib/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.cpp index 6f7c6cf..5499ceb 100644 --- a/lib/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.cpp +++ b/lib/Conversion/QCToCatalystQuantum/QCToCatalystQuantum.cpp @@ -326,30 +326,4 @@ struct ConvertQCStore final : OpConversionPattern { } }; -struct ConvertQCCast final : OpConversionPattern { - using OpConversionPattern::OpConversionPattern; - - LogicalResult - matchAndRewrite(memref::CastOp op, OpAdaptor adaptor, - ConversionPatternRewriter& rewriter) const override { - // Only convert if it's a cast between qubit memrefs - auto srcType = dyn_cast(op.getSource().getType()); - auto dstType = dyn_cast(op.getType()); - auto srcElem = srcType ? srcType.getElementType() : Type(); - auto dstElem = dstType ? dstType.getElementType() : Type(); - - if (!srcType || !dstType || - !(isa(srcElem) || - isa(srcElem)) || - !(isa(dstElem) || - isa(dstElem))) { - return failure(); - } - - // Both should convert to !quantum.reg - rewriter.replaceOp(op, adaptor.getSource()); - return success(); - } -}; - } // namespace mqt::ir::conversions diff --git a/lib/mqt-plugin.cpp b/lib/mqt-plugin.cpp index 9c3fc0a..031804b 100644 --- a/lib/mqt-plugin.cpp +++ b/lib/mqt-plugin.cpp @@ -44,6 +44,6 @@ extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo mlirGetPassPluginInfo() { // Note: mqt::ir::opt::registerQCPasses() is not called to avoid // pulling in transpilation transforms that require LLVM 21 mqt::ir::conversions::registerCatalystQuantumToQCPasses(); - mqt::ir::conversions::registerQCToCatalystQuantumPasses(); + // mqt::ir::conversions::registerQCToCatalystQuantumPasses(); }}; } diff --git a/test/test_plugin.py b/test/test_plugin.py index a4c2942..0454e81 100644 --- a/test/test_plugin.py +++ b/test/test_plugin.py @@ -10,7 +10,7 @@ These tests check that the MQT plugin conversion passes execute successfully for various gate categories, mirroring the MLIR conversion tests. They verify -that the full lossless roundtrip (CatalystQuantum → MQTOpt → CatalystQuantum) +that the full lossless roundtrip (CatalystQuantum → QC → CatalystQuantum) works correctly. The tests use FileCheck (from LLVM) to verify the generated MLIR output. Environment Variables: @@ -40,13 +40,13 @@ # Default pipeline for roundtrip conversion tests DEFAULT_PIPELINE = [ ("Init", ["builtin.module(canonicalize)"]), # Trick to get initial CatalystQuantum MLIR - ("ToMQTOpt", ["builtin.module(catalystquantum-to-mqtopt)"]), - ("ToCatalystQuantum", ["builtin.module(mqtopt-to-catalystquantum)"]), + ("ToQC", ["builtin.module(catalystquantum-to-qc)"]), + ("ToCatalystQuantum", ["builtin.module(qc-to-catalystquantum)"]), ] # Standard MLIR file names generated by Catalyst MLIR_FILE_INIT = "1_AfterInit.mlir" -MLIR_FILE_TO_MQTOPT = "2_AfterToMQTOpt.mlir" +MLIR_FILE_TO_QC = "2_AfterToQC.mlir" MLIR_FILE_TO_CATALYST = "3_AfterToCatalystQuantum.mlir" @@ -185,12 +185,12 @@ def _get_mlir_file_paths() -> tuple[Path, Path, Path]: """Get paths to the three expected MLIR intermediate files. Returns: - Tuple of (catalyst_mlir, mlir_to_mqtopt, mlir_to_catalyst) paths + Tuple of (catalyst_mlir, mlir_to_qc, mlir_to_catalyst) paths """ mlir_dir = Path.cwd() return ( mlir_dir / MLIR_FILE_INIT, - mlir_dir / MLIR_FILE_TO_MQTOPT, + mlir_dir / MLIR_FILE_TO_QC, mlir_dir / MLIR_FILE_TO_CATALYST, ) @@ -199,30 +199,30 @@ def _verify_and_read_mlir_files() -> tuple[str, str, str]: """Verify MLIR files exist and read their contents. Returns: - Tuple of (mlir_before, mlir_after_mqtopt, mlir_after_roundtrip) as strings + Tuple of (mlir_before, mlir_after_qc, mlir_after_roundtrip) as strings Raises: FileNotFoundError: If any expected MLIR file is missing """ - catalyst_mlir, mlir_to_mqtopt, mlir_to_catalyst = _get_mlir_file_paths() + catalyst_mlir, mlir_to_qc, mlir_to_catalyst = _get_mlir_file_paths() - if not catalyst_mlir.exists() or not mlir_to_mqtopt.exists() or not mlir_to_catalyst.exists(): + if not catalyst_mlir.exists() or not mlir_to_qc.exists() or not mlir_to_catalyst.exists(): mlir_dir = Path.cwd() available_files = list(mlir_dir.glob("*.mlir")) msg = f"Expected MLIR files not found in {mlir_dir}.\nAvailable files: {[f.name for f in available_files]}" raise FileNotFoundError(msg) mlir_before = catalyst_mlir.read_text(encoding="utf-8") - mlir_after_mqtopt = mlir_to_mqtopt.read_text(encoding="utf-8") + mlir_after_qc = mlir_to_qc.read_text(encoding="utf-8") mlir_after_roundtrip = mlir_to_catalyst.read_text(encoding="utf-8") - return mlir_before, mlir_after_mqtopt, mlir_after_roundtrip + return mlir_before, mlir_after_qc, mlir_after_roundtrip def _verify_roundtrip( test_name: str, check_before: str, - check_mqtopt: str, + check_qc: str, check_after: str, ) -> None: """Verify MLIR roundtrip conversion using FileCheck. @@ -230,21 +230,21 @@ def _verify_roundtrip( Args: test_name: Name of the test (for error messages) check_before: FileCheck patterns for initial CatalystQuantum MLIR - check_mqtopt: FileCheck patterns for CatalystQuantum → MQTOpt conversion - check_after: FileCheck patterns for MQTOpt → CatalystQuantum conversion + check_qc: FileCheck patterns for CatalystQuantum → QC conversion + check_after: FileCheck patterns for QC → CatalystQuantum conversion """ - mlir_before, mlir_after_mqtopt, mlir_after_roundtrip = _verify_and_read_mlir_files() + mlir_before, mlir_after_qc, mlir_after_roundtrip = _verify_and_read_mlir_files() _run_filecheck(mlir_before, check_before, f"{test_name}: CatalystQuantum") - _run_filecheck(mlir_after_mqtopt, check_mqtopt, f"{test_name}: CatalystQuantum to MQTOpt") - _run_filecheck(mlir_after_roundtrip, check_after, f"{test_name}: MQTOpt to CatalystQuantum") + _run_filecheck(mlir_after_qc, check_qc, f"{test_name}: CatalystQuantum to QC") + _run_filecheck(mlir_after_roundtrip, check_after, f"{test_name}: QC to CatalystQuantum") def test_paulix_roundtrip() -> None: """Test roundtrip conversion of the PauliX gate.""" - @apply_pass("mqt.mqtopt-to-catalystquantum") - @apply_pass("mqt.catalystquantum-to-mqtopt") + @apply_pass("mqt.qc-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-qc") @qml.qnode(get_device("lightning.qubit", wires=2)) def circuit() -> None: # Non-controlled @@ -269,15 +269,19 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q10_0:.*]]:2 = quantum.custom "CNOT"() %[[Q10_0:.*]]#0, %[[Q10_0:.*]]#1 : !quantum.bit, !quantum.bit """ - # Verify CatalystQuantum → MQTOpt conversion - check_after_mqtopt = """ - //CHECK: %[[Q0_1:.*]] = mqtopt.x(static [] mask []) %[[Q0_0:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_2:.*]] = mqtopt.x(static [] mask []) %[[Q0_1:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_3:.*]], %[[Q1_1:.*]] = mqtopt.x(static [] mask []) %[[Q0_2:.*]] ctrl %[[Q1_0:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - //CHECK: %[[Q0_4:.*]], %[[Q1_2:.*]] = mqtopt.x(static [] mask []) %[[Q0_3:.*]] ctrl %[[Q1_1:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + # Verify CatalystQuantum → QC conversion + check_after_qc = """ + //CHECK: qc.x %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.x %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.x %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.x %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit """ - # Verify MQTOpt → CatalystQuantum conversion + # Verify QC → CatalystQuantum conversion check_after_catalyst = """ //CHECK: %[[Q0_1:.*]] = quantum.custom "PauliX"() %[[Q0_0:.*]] : !quantum.bit //CHECK: %[[Q0_2:.*]] = quantum.custom "PauliX"() %[[Q0_1:.*]] : !quantum.bit @@ -285,14 +289,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_4:.*]], %[[Q1_2:.*]] = quantum.custom "CNOT"() %[[Q0_3:.*]] ctrls(%[[Q1_1:.*]]) ctrlvals(%true{{(_[0-9]+)?}}) : !quantum.bit ctrls !quantum.bit """ - _verify_roundtrip("PauliX", check_mlir_before, check_after_mqtopt, check_after_catalyst) + _verify_roundtrip("PauliX", check_mlir_before, check_after_qc, check_after_catalyst) def test_pauliy_roundtrip() -> None: """Test roundtrip conversion of the PauliY gate.""" - @apply_pass("mqt.mqtopt-to-catalystquantum") - @apply_pass("mqt.catalystquantum-to-mqtopt") + @apply_pass("mqt.qc-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-qc") @qml.qnode(get_device("lightning.qubit", wires=2)) def circuit() -> None: # Non-controlled @@ -317,15 +321,19 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q10_0:.*]]:2 = quantum.custom "CY"() %[[Q10_0:.*]]#0, %[[Q10_0:.*]]#1 : !quantum.bit, !quantum.bit """ - # Verify CatalystQuantum → MQTOpt conversion - check_after_mqtopt = """ - //CHECK: %[[Q0_1:.*]] = mqtopt.y(static [] mask []) %[[Q0_0:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_2:.*]] = mqtopt.y(static [] mask []) %[[Q0_1:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_3:.*]], %[[Q1_1:.*]] = mqtopt.y(static [] mask []) %[[Q0_2:.*]] ctrl %[[Q1_0:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - //CHECK: %[[Q0_4:.*]], %[[Q1_2:.*]] = mqtopt.y(static [] mask []) %[[Q0_3:.*]] ctrl %[[Q1_1:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + # Verify CatalystQuantum → QC conversion + check_after_qc = """ + //CHECK: qc.y %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.y %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.y %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.y %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit """ - # Verify MQTOpt → CatalystQuantum conversion + # Verify QC → CatalystQuantum conversion check_after_catalyst = """ //CHECK: %[[Q0_1:.*]] = quantum.custom "PauliY"() %[[Q0_0:.*]] : !quantum.bit //CHECK: %[[Q0_2:.*]] = quantum.custom "PauliY"() %[[Q0_1:.*]] : !quantum.bit @@ -333,14 +341,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_4:.*]], %[[Q1_2:.*]] = quantum.custom "CY"() %[[Q0_3:.*]] ctrls(%[[Q1_1:.*]]) ctrlvals(%true{{(_[0-9]+)?}}) : !quantum.bit ctrls !quantum.bit """ - _verify_roundtrip("PauliY", check_mlir_before, check_after_mqtopt, check_after_catalyst) + _verify_roundtrip("PauliY", check_mlir_before, check_after_qc, check_after_catalyst) def test_pauliz_roundtrip() -> None: """Test roundtrip conversion of the PauliZ gate.""" - @apply_pass("mqt.mqtopt-to-catalystquantum") - @apply_pass("mqt.catalystquantum-to-mqtopt") + @apply_pass("mqt.qc-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-qc") @qml.qnode(get_device("lightning.qubit", wires=2)) def circuit() -> None: # Non-controlled @@ -365,15 +373,19 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q10_0:.*]]:2 = quantum.custom "CZ"() %[[Q10_0:.*]]#0, %[[Q10_0:.*]]#1 : !quantum.bit, !quantum.bit """ - # Verify CatalystQuantum → MQTOpt conversion - check_after_mqtopt = """ - //CHECK: %[[Q0_1:.*]] = mqtopt.z(static [] mask []) %[[Q0_0:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_2:.*]] = mqtopt.z(static [] mask []) %[[Q0_1:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_3:.*]], %[[Q1_1:.*]] = mqtopt.z(static [] mask []) %[[Q0_2:.*]] ctrl %[[Q1_0:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - //CHECK: %[[Q0_4:.*]], %[[Q1_2:.*]] = mqtopt.z(static [] mask []) %[[Q0_3:.*]] ctrl %[[Q1_1:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + # Verify CatalystQuantum → QC conversion + check_after_qc = """ + //CHECK: qc.z %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.z %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.z %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.z %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit """ - # Verify MQTOpt → CatalystQuantum conversion + # Verify QC → CatalystQuantum conversion check_after_catalyst = """ //CHECK: %[[Q0_1:.*]] = quantum.custom "PauliZ"() %[[Q0_0:.*]] : !quantum.bit //CHECK: %[[Q0_2:.*]] = quantum.custom "PauliZ"() %[[Q0_1:.*]] : !quantum.bit @@ -381,14 +393,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_4:.*]], %[[Q1_2:.*]] = quantum.custom "CZ"() %[[Q0_3:.*]] ctrls(%[[Q1_1:.*]]) ctrlvals(%true{{(_[0-9]+)?}}) : !quantum.bit ctrls !quantum.bit """ - _verify_roundtrip("PauliZ", check_mlir_before, check_after_mqtopt, check_after_catalyst) + _verify_roundtrip("PauliZ", check_mlir_before, check_after_qc, check_after_catalyst) def test_hadamard_roundtrip() -> None: """Test roundtrip conversion of the Hadamard gate.""" - @apply_pass("mqt.mqtopt-to-catalystquantum") - @apply_pass("mqt.catalystquantum-to-mqtopt") + @apply_pass("mqt.qc-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-qc") @qml.qnode(get_device("lightning.qubit", wires=2)) def circuit() -> None: qml.Hadamard(wires=0) @@ -408,10 +420,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_3:.*]], %[[Q1_2:.*]] = quantum.custom "Hadamard"() %[[Q0_2:.*]] ctrls(%[[Q1_1:.*]]) ctrlvals(%true{{(_[0-9]+)?}}) : !quantum.bit ctrls !quantum.bit """ - check_after_mqtopt = """ - //CHECK: %[[Q0_1:.*]] = mqtopt.h(static [] mask []) %[[Q0_0:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_2:.*]], %[[Q1_1:.*]] = mqtopt.h(static [] mask []) %[[Q0_1:.*]] ctrl %[[Q1_0:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - //CHECK: %[[Q0_3:.*]], %[[Q1_2:.*]] = mqtopt.h(static [] mask []) %[[Q0_2:.*]] ctrl %[[Q1_1:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + check_after_qc = """ + //CHECK: qc.h %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.h %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.h %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit """ check_after_catalyst = """ @@ -420,14 +436,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_3:.*]], %[[Q1_2:.*]] = quantum.custom "Hadamard"() %[[Q0_2:.*]] ctrls(%[[Q1_1:.*]]) ctrlvals(%true{{(_[0-9]+)?}}) : !quantum.bit ctrls !quantum.bit """ - _verify_roundtrip("Hadamard", check_mlir_before, check_after_mqtopt, check_after_catalyst) + _verify_roundtrip("Hadamard", check_mlir_before, check_after_qc, check_after_catalyst) def test_s_gate_roundtrip() -> None: """Test roundtrip conversion of the S gate.""" - @apply_pass("mqt.mqtopt-to-catalystquantum") - @apply_pass("mqt.catalystquantum-to-mqtopt") + @apply_pass("mqt.qc-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-qc") @qml.qnode(get_device("lightning.qubit", wires=2)) def circuit() -> None: qml.S(wires=0) @@ -447,9 +463,12 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_3:.*]], %[[Q1_1:.*]] = quantum.custom "S"() %[[Q0_2:.*]] ctrls(%[[Q1_0:.*]]) ctrlvals(%true{{(_[0-9]+)?}}) : !quantum.bit ctrls !quantum.bit """ - check_after_mqtopt = """ - //CHECK: %[[Q0_1:.*]] = mqtopt.s(static [] mask []) %[[Q0_0:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_2:.*]] = mqtopt.sdg(static [] mask []) %[[Q0_1:.*]] : !mqtopt.Qubit + check_after_qc = """ + //CHECK: qc.s %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.sdg %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.s %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit """ check_after_catalyst = """ @@ -458,14 +477,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_3:.*]], %[[Q1_1:.*]] = quantum.custom "S"() %[[Q0_2:.*]] ctrls(%[[Q1_0:.*]]) ctrlvals(%true{{(_[0-9]+)?}}) : !quantum.bit ctrls !quantum.bit """ - _verify_roundtrip("S", check_mlir_before, check_after_mqtopt, check_after_catalyst) + _verify_roundtrip("S", check_mlir_before, check_after_qc, check_after_catalyst) def test_t_gate_roundtrip() -> None: """Test roundtrip conversion of the T gate.""" - @apply_pass("mqt.mqtopt-to-catalystquantum") - @apply_pass("mqt.catalystquantum-to-mqtopt") + @apply_pass("mqt.qc-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-qc") @qml.qnode(get_device("lightning.qubit", wires=2)) def circuit() -> None: qml.T(wires=0) @@ -485,10 +504,12 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_3:.*]], %[[Q1_1:.*]] = quantum.custom "T"() %[[Q0_2:.*]] ctrls(%[[Q1_0:.*]]) ctrlvals(%true{{(_[0-9]+)?}}) : !quantum.bit ctrls !quantum.bit """ - check_after_mqtopt = """ - //CHECK: %[[Q0_1:.*]] = mqtopt.t(static [] mask []) %[[Q0_0:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_2:.*]] = mqtopt.tdg(static [] mask []) %[[Q0_1:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_3:.*]], %[[Q1_1:.*]] = mqtopt.t(static [] mask []) %[[Q0_2:.*]] ctrl %[[Q1_0:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + check_after_qc = """ + //CHECK: qc.t %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.tdg %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.t %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit """ check_after_catalyst = """ @@ -497,14 +518,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_3:.*]], %[[Q1_1:.*]] = quantum.custom "T"() %[[Q0_2:.*]] ctrls(%[[Q1_0:.*]]) ctrlvals(%true{{(_[0-9]+)?}}) : !quantum.bit ctrls !quantum.bit """ - _verify_roundtrip("T", check_mlir_before, check_after_mqtopt, check_after_catalyst) + _verify_roundtrip("T", check_mlir_before, check_after_qc, check_after_catalyst) def test_rx_gate_roundtrip() -> None: """Test roundtrip conversion of the RX gate.""" - @apply_pass("mqt.mqtopt-to-catalystquantum") - @apply_pass("mqt.catalystquantum-to-mqtopt") + @apply_pass("mqt.qc-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-qc") @qml.qnode(get_device("lightning.qubit", wires=3)) def circuit() -> None: qml.RX(0.5, wires=0) @@ -525,13 +546,22 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q10_0:.*]]:2 = quantum.custom "CRX"({{.*}}) %[[Q1_0:.*]], %[[Q0_1:.*]] : !quantum.bit, !quantum.bit //CHECK: %[[Q11_0:.*]]:2 = quantum.custom "CRX"({{.*}}) %[[Q10_0]]#0, %[[Q10_0:.*]]#1 : !quantum.bit, !quantum.bit //CHECK: %[[Q0_2:.*]], %[[Q21_0:.*]]:2 = quantum.custom "RX"({{.*}}) %[[Q11_0:.*]]#1 ctrls(%[[Q2_0:.*]], %[[Q11_0:.*]]#0) ctrlvals({{.*}}, {{.*}}) : !quantum.bit ctrls !quantum.bit, !quantum.bit + """ - check_after_mqtopt = """ - //CHECK: %[[Q0_1:.*]] = mqtopt.rx({{.*}}) %[[Q0_0:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_2:.*]], %[[Q1_1:.*]] = mqtopt.rx({{.*}}) %[[Q0_1]] ctrl %[[Q1_0:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - //CHECK: %[[Q0_3:.*]], %[[Q1_2:.*]] = mqtopt.rx({{.*}}) %[[Q0_2]] ctrl %[[Q1_1:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - //CHECK: %[[Q0_4:.*]], %[[Q12:.*]]:2 = mqtopt.rx({{.*}}) %[[Q0_3]] ctrl %[[Q2_1:.*]], %[[Q1_2:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit + check_after_qc = """ + //CHECK: qc.rx({{.*}}) %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.rx({{.*}}) %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.rx({{.*}}) %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_2:.*]]) { + qc.ctrl(%[[Q0_1:.*]]) { + qc.rx({{.*}}) %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + } : !qc.qubit """ check_after_catalyst = """ @@ -540,14 +570,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_3:.*]], %[[Q1_2:.*]] = quantum.custom "CRX"({{.*}}) %[[Q0_2:.*]] ctrls(%[[Q1_1:.*]]) ctrlvals({{.*}}) : !quantum.bit ctrls !quantum.bit //CHECK: %[[Q0_4:.*]], %[[Q212:.*]]:2 = quantum.custom "RX"({{.*}}) %[[Q0_3:.*]] ctrls(%[[Q2_1:.*]], %[[Q1_2:.*]]) ctrlvals({{.*}}, {{.*}}) : !quantum.bit ctrls !quantum.bit, !quantum.bit """ - _verify_roundtrip("RX", check_mlir_before, check_after_mqtopt, check_after_catalyst) + _verify_roundtrip("RX", check_mlir_before, check_after_qc, check_after_catalyst) def test_ry_gate_roundtrip() -> None: """Test roundtrip conversion of the RY gate.""" - @apply_pass("mqt.mqtopt-to-catalystquantum") - @apply_pass("mqt.catalystquantum-to-mqtopt") + @apply_pass("mqt.qc-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-qc") @qml.qnode(get_device("lightning.qubit", wires=3)) def circuit() -> None: qml.RY(0.5, wires=0) @@ -568,13 +598,22 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q10_0:.*]]:2 = quantum.custom "CRY"({{.*}}) %[[Q1_0:.*]], %[[Q0_1:.*]] : !quantum.bit, !quantum.bit //CHECK: %[[Q11_0:.*]]:2 = quantum.custom "CRY"({{.*}}) %[[Q10_0]]#0, %[[Q10_0:.*]]#1 : !quantum.bit, !quantum.bit //CHECK: %[[Q0_2:.*]], %[[Q21_0:.*]]:2 = quantum.custom "RY"({{.*}}) %[[Q11_0:.*]]#1 ctrls(%[[Q2_0:.*]], %[[Q11_0:.*]]#0) ctrlvals({{.*}}, {{.*}}) : !quantum.bit ctrls !quantum.bit, !quantum.bit + """ - check_after_mqtopt = """ - //CHECK: %[[Q0_1:.*]] = mqtopt.ry({{.*}}) %[[Q0_0:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_2:.*]], %[[Q1_1:.*]] = mqtopt.ry({{.*}}) %[[Q0_1:.*]] ctrl %[[Q1_0:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - //CHECK: %[[Q0_3:.*]], %[[Q1_2:.*]] = mqtopt.ry({{.*}}) %[[Q0_2:.*]] ctrl %[[Q1_1:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - //CHECK: %[[Q0_4:.*]], %[[Q12:.*]]:2 = mqtopt.ry({{.*}}) %[[Q0_3:.*]] ctrl %[[Q2_1:.*]], %[[Q1_2:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit + check_after_qc = """ + //CHECK: qc.ry({{.*}}) %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.ry({{.*}}) %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.ry({{.*}}) %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_2:.*]]) { + qc.ctrl(%[[Q0_1:.*]]) { + qc.ry({{.*}}) %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + } : !qc.qubit """ check_after_catalyst = """ @@ -583,14 +622,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_3:.*]], %[[Q1_2:.*]] = quantum.custom "CRY"({{.*}}) %[[Q0_2:.*]] ctrls(%[[Q1_1:.*]]) ctrlvals({{.*}}) : !quantum.bit ctrls !quantum.bit //CHECK: %[[Q0_4:.*]], %[[Q212:.*]]:2 = quantum.custom "RY"({{.*}}) %[[Q0_3:.*]] ctrls(%[[Q2_1:.*]], %[[Q1_2:.*]]) ctrlvals({{.*}}, {{.*}}) : !quantum.bit ctrls !quantum.bit, !quantum.bit """ - _verify_roundtrip("RY", check_mlir_before, check_after_mqtopt, check_after_catalyst) + _verify_roundtrip("RY", check_mlir_before, check_after_qc, check_after_catalyst) def test_rz_gate_roundtrip() -> None: """Test roundtrip conversion of the RZ gate.""" - @apply_pass("mqt.mqtopt-to-catalystquantum") - @apply_pass("mqt.catalystquantum-to-mqtopt") + @apply_pass("mqt.qc-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-qc") @qml.qnode(get_device("lightning.qubit", wires=3)) def circuit() -> None: qml.RZ(0.5, wires=0) @@ -609,14 +648,23 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_1:.*]] = quantum.custom "RZ"({{.*}}) %[[Q0_0:.*]] : !quantum.bit //CHECK: %[[Q10_0:.*]]:2 = quantum.custom "CRZ"({{.*}}) %[[Q1_0:.*]], %[[Q0_1:.*]] : !quantum.bit, !quantum.bit //CHECK: %[[Q11_0:.*]]:2 = quantum.custom "CRZ"({{.*}}) %[[Q10_0]]#0, %[[Q10_0:.*]]#1 : !quantum.bit, !quantum.bit - //CHECK: %[[Q0_2:.*]], %[[Q21_0:.*]]:2 = quantum.custom "RZ"({{.*}}) %[[Q11_0:.*]]#1 ctrls(%[[Q2_0:.*]], %[[Q11_0]]#0) ctrlvals({{.*}}, {{.*}}) : !quantum.bit ctrls !quantum.bit, !quantum.bit + //CHECK: %[[Q0_2:.*]], %[[Q21_0:.*]]:2 = quantum.custom "RZ"({{.*}}) %[[Q11_0:.*]]#1 ctrls(%[[Q2_0:.*]], %[[Q11_0:.*]]#0) ctrlvals({{.*}}, {{.*}}) : !quantum.bit ctrls !quantum.bit, !quantum.bit + """ - check_after_mqtopt = """ - //CHECK: %[[Q0_1:.*]] = mqtopt.rz({{.*}}) %[[Q0_0:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_2:.*]], %[[Q1_1:.*]] = mqtopt.rz({{.*}}) %[[Q0_1]] ctrl %[[Q1_0:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - //CHECK: %[[Q0_3:.*]], %[[Q1_2:.*]] = mqtopt.rz({{.*}}) %[[Q0_2]] ctrl %[[Q1_1:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - //CHECK: %[[Q0_4:.*]], %[[Q12:.*]]:2 = mqtopt.rz({{.*}}) %[[Q0_3]] ctrl %[[Q2_1:.*]], %[[Q1_2]] : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit + check_after_qc = """ + //CHECK: qc.rz({{.*}}) %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.rz({{.*}}) %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.rz({{.*}}) %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_2:.*]]) { + qc.ctrl(%[[Q0_1:.*]]) { + qc.rz({{.*}}) %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + } : !qc.qubit """ check_after_catalyst = """ @@ -626,14 +674,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_4:.*]], %[[Q212:.*]]:2 = quantum.custom "RZ"({{.*}}) %[[Q0_3:.*]] ctrls(%[[Q2_1:.*]], %[[Q1_2:.*]]) ctrlvals({{.*}}, {{.*}}) : !quantum.bit ctrls !quantum.bit, !quantum.bit """ - _verify_roundtrip("RZ", check_mlir_before, check_after_mqtopt, check_after_catalyst) + _verify_roundtrip("RZ", check_mlir_before, check_after_qc, check_after_catalyst) def test_phaseshift_gate_roundtrip() -> None: """Test roundtrip conversion of the PhaseShift gate.""" - @apply_pass("mqt.mqtopt-to-catalystquantum") - @apply_pass("mqt.catalystquantum-to-mqtopt") + @apply_pass("mqt.qc-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-qc") @qml.qnode(get_device("lightning.qubit", wires=2)) def circuit() -> None: qml.PhaseShift(0.5, wires=0) @@ -653,10 +701,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q11_0:.*]]:2 = quantum.custom "ControlledPhaseShift"({{.*}}) %[[Q10_0:.*]]#0, %[[Q10_0:.*]]#1 : !quantum.bit, !quantum.bit """ - check_after_mqtopt = """ - //CHECK: %[[Q0_1:.*]] = mqtopt.p({{.*}}) %[[Q0_0:.*]] : !mqtopt.Qubit - //CHECK: %[[Q0_2:.*]], %[[Q1_1:.*]] = mqtopt.p({{.*}}) %[[Q0_1:.*]] ctrl %[[Q1_0:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit - //CHECK: %[[Q0_3:.*]], %[[Q1_2:.*]] = mqtopt.p({{.*}}) %[[Q0_2:.*]] ctrl %[[Q1_1:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + check_after_qc = """ + //CHECK: qc.p({{.*}}) %[[Q0_0:.*]] : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.p({{.*}}) %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_1:.*]]) { + qc.p({{.*}}) %[[Q0_0:.*]] : !qc.qubit + } : !qc.qubit """ check_after_catalyst = """ @@ -665,14 +717,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q0_3:.*]], %[[Q1_2:.*]] = quantum.custom "ControlledPhaseShift"({{.*}}) %[[Q0_2:.*]] ctrls(%[[Q1_1:.*]]) ctrlvals({{.*}}) : !quantum.bit ctrls !quantum.bit """ - _verify_roundtrip("PhaseShift", check_mlir_before, check_after_mqtopt, check_after_catalyst) + _verify_roundtrip("PhaseShift", check_mlir_before, check_after_qc, check_after_catalyst) def test_swap_gate_roundtrip() -> None: """Test roundtrip conversion of the SWAP gate.""" - @apply_pass("mqt.mqtopt-to-catalystquantum") - @apply_pass("mqt.catalystquantum-to-mqtopt") + @apply_pass("mqt.qc-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-qc") @qml.qnode(get_device("lightning.qubit", wires=3)) def circuit() -> None: qml.SWAP(wires=[0, 1]) @@ -692,10 +744,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q201_1:.*]]:3 = quantum.custom "CSWAP"() %[[Q201_0:.*]]#0, %[[Q201_0:.*]]#1, %[[Q201_0:.*]]#2 : !quantum.bit, !quantum.bit, !quantum.bit """ - check_after_mqtopt = """ - //CHECK: %[[Q01_1:.*]]:2 = mqtopt.swap({{.*}}) %[[Q0_0:.*]], %[[Q1_0:.*]] : !mqtopt.Qubit, !mqtopt.Qubit - //CHECK: %[[Q01_2:.*]]:2, %[[Q2_1:.*]] = mqtopt.swap({{.*}}) %[[Q01_1]]#0, %[[Q01_1:.*]]#1 ctrl %[[Q2_0:.*]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit - //CHECK: %[[Q01_3:.*]]:2, %[[Q2_2:.*]] = mqtopt.swap({{.*}}) %[[Q01_2]]#0, %[[Q01_2:.*]]#1 ctrl %[[Q2_1]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + check_after_qc = """ + //CHECK: qc.swap %[[Q0_0:.*]], %[[Q1_0:.*]] : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_2:.*]]) { + qc.swap %[[Q0_0:.*]] %[[Q0_1:.*]] : !qc.qubit + } : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_2:.*]]) { + qc.swap %[[Q0_0:.*]] %[[Q0_1:.*]] : !qc.qubit + } : !qc.qubit """ check_after_catalyst = """ @@ -704,14 +760,14 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q01_3:.*]]:2, %[[Q2_2:.*]] = quantum.custom "CSWAP"() %[[Q01_2:.*]]#0, %[[Q01_2:.*]]#1 ctrls(%[[Q2_1:.*]]) ctrlvals({{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit """ - _verify_roundtrip("SWAP", check_mlir_before, check_after_mqtopt, check_after_catalyst) + _verify_roundtrip("SWAP", check_mlir_before, check_after_qc, check_after_catalyst) def test_toffoli_gate_roundtrip() -> None: """Test roundtrip conversion of the Toffoli gate.""" - @apply_pass("mqt.mqtopt-to-catalystquantum") - @apply_pass("mqt.catalystquantum-to-mqtopt") + @apply_pass("mqt.qc-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-qc") @qml.qnode(get_device("lightning.qubit", wires=4)) def circuit() -> None: qml.Toffoli(wires=[0, 1, 2]) @@ -729,9 +785,15 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q2_1:.*]], %[[Q301_0:.*]]:3 = quantum.custom "PauliX"() %[[Q012_0:.*]]#2 ctrls(%[[Q3_0:.*]], %[[Q012_0:.*]]#0, %[[Q012_0:.*]]#1) ctrlvals({{.*}}, {{.*}}, {{.*}}) : !quantum.bit ctrls !quantum.bit, !quantum.bit, !quantum.bit """ - check_after_mqtopt = """ - //CHECK: %[[Q2_1:.*]], %[[Q01_1:.*]]:2 = mqtopt.x({{.*}}) %[[Q2_0:.*]] ctrl %[[Q0_0:.*]], %[[Q1_0:.*]] : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit - //CHECK: %[[Q2_2:.*]], %[[Q301_1:.*]]:3 = mqtopt.x({{.*}}) %[[Q2_1]] ctrl %[[Q3_0:.*]], %[[Q01_1:.*]]#0, %[[Q01_1:.*]]#1 : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit, !mqtopt.Qubit + check_after_qc = """ + //CHECK: qc.ctrl(%[[Q0_0:.*]], %[[Q0_1:.*]]) { + qc.x %[[Q0_2:.*]] : !qc.qubit + } : !qc.qubit + //CHECK: qc.ctrl(%[[Q0_3:.*]]) { + qc.ctrl(%[[Q0_0:.*]], %[[Q0_1:.*]],) { + qc.x %[[Q0_2:.*]] : !qc.qubit + } : !qc.qubit + } : !qc.qubit """ check_after_catalyst = """ @@ -739,4 +801,4 @@ def module() -> Any: # noqa: ANN401 //CHECK: %[[Q2_2:.*]], %[[Q301_1:.*]]:3 = quantum.custom "PauliX"() %[[Q2_1:.*]] ctrls(%[[Q3_0:.*]], %[[Q01_1:.*]]#0, %[[Q01_1:.*]]#1) ctrlvals({{.*}}, {{.*}}, {{.*}}) : !quantum.bit ctrls !quantum.bit, !quantum.bit, !quantum.bit """ - _verify_roundtrip("Toffoli", check_mlir_before, check_after_mqtopt, check_after_catalyst) + _verify_roundtrip("Toffoli", check_mlir_before, check_after_qc, check_after_catalyst)