Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ ignore:
- "**/python"
- "test/**/*"
- "mlir/unittests/**/*"
# The MLIR Python bindings require an MLIR toolchain built with Python
- "python/mqt/core/mlir/**"
Comment on lines +5 to +6

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my general point above.


coverage:
range: 60..90
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ cmake-build-*
# Distribution / packaging
.Python
/build/
/build_*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change shouldn't be necessary.

Suggested change
/build_*/

/test/*/build
develop-eggs/
dist/
Expand Down
5 changes: 5 additions & 0 deletions bindings/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ add_subdirectory(ir)
add_subdirectory(dd)
add_subdirectory(fomac)
add_subdirectory(na)

# The MLIR bindings require the MLIR submodule to be built.
if(BUILD_MQT_CORE_MLIR)
add_subdirectory(mlir)
endif()
13 changes: 13 additions & 0 deletions bindings/mlir/.clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# This binding module uses the NB_MODULE macro and the MLIR C-API type-casters
# from NanobindAdaptors.h. The include-cleaner, internal-linkage,
# identifier-naming, named-parameter, value-parameter, and unused-alias checks
# cannot reason about the macro expansion and the templated casters, so they
# report false positives here. Disable them for this binding glue.
InheritParentConfig: true
Checks: |
-misc-include-cleaner,
-misc-unused-alias-decls,
-misc-use-internal-linkage,
-performance-unnecessary-value-param,
-readability-identifier-naming,
-readability-named-parameter
34 changes: 34 additions & 0 deletions bindings/mlir/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
# All rights reserved.
#
# SPDX-License-Identifier: MIT
#
# Licensed under the MIT License

if(NOT TARGET ${MQT_CORE_TARGET_NAME}-mlir-bindings)
# collect source files
file(GLOB_RECURSE MLIR_BINDINGS_SOURCES **.cpp)

# declare the Python module
add_mqt_python_binding_nanobind(
CORE
${MQT_CORE_TARGET_NAME}-mlir-bindings
${MLIR_BINDINGS_SOURCES}
MODULE_NAME
_mlir
INSTALL_DIR
./mlir
LINK_LIBS
MQTCoreCAPI
MLIRIR)

# install the Python stub files in editable mode for better IDE support
if(SKBUILD_STATE STREQUAL "editable")
file(GLOB_RECURSE MLIR_PYI_FILES ${PROJECT_SOURCE_DIR}/python/mqt/core/mlir/*.pyi)
install(
FILES ${MLIR_PYI_FILES}
DESTINATION ./mlir
COMPONENT ${MQT_CORE_TARGET_NAME}_Python)
endif()
endif()
59 changes: 59 additions & 0 deletions bindings/mlir/register_mlir.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
* Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
* All rights reserved.
*
* SPDX-License-Identifier: MIT
*
* Licensed under the MIT License
*/

#include "mqt-core-c/Registration.h"

#include <mlir-c/IR.h>
#include <mlir-c/Support.h>
#include <mlir/Bindings/Python/NanobindAdaptors.h>
#include <nanobind/nanobind.h>

#include <stdexcept>
#include <string>

namespace mqt {

namespace nb = nanobind;

NB_MODULE(MQT_CORE_MODULE_NAME, m) {
m.doc() =
R"pb(MQT Core MLIR - Python bindings for the MQT Compiler Collection.

This module exposes the dialects and passes of the MQT Compiler Collection so
that quantum programs can be compiled from Python using MLIR's Python bindings.)pb";

m.def(
"register_dialects",
[](MlirContext context) { mqtRegisterAllDialects(context); },
nb::arg("context"),
"Register and load all MQT Compiler Collection dialects with the given "
"context.");

m.def(
"register_passes", []() { mqtRegisterAllPasses(); },
"Register all MQT Compiler Collection passes with MLIR's global pass "
"registry.");

m.def(
"import_qasm3_to_qc",
[](MlirContext context, const std::string& qasm) {
const MlirModule module = mqtImportQASM3ToQC(
context, mlirStringRefCreate(qasm.data(), qasm.size()));
if (mlirModuleIsNull(module)) {
throw std::runtime_error(
"Failed to import OpenQASM 3 program into the QC dialect");
}
return module;
},
nb::arg("context"), nb::arg("qasm"),
"Import an OpenQASM 3 program into a QC-dialect module.");
}

} // namespace mqt
60 changes: 60 additions & 0 deletions mlir/include/mqt-core-c/Registration.h

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, the path should probably be mlir/include/CAPI/Registration.h. 🤔

Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
* Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
* All rights reserved.
*
* SPDX-License-Identifier: MIT
*
* Licensed under the MIT License
*/

#ifndef MQT_CORE_C_REGISTRATION_H
#define MQT_CORE_C_REGISTRATION_H

#include "mlir-c/IR.h"
#include "mlir-c/Support.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Register and load all dialects of the MQT Compiler Collection with the
* given context.
*
* @details Registers the QC, QCO, QTensor, and Jeff dialects together with the
* upstream dialects (arith, func, scf, cf, memref, llvm) that the dialects,
* conversions, and transformations depend on, and loads them so that modules
* and passes can use them.
*
* @param ctx The MLIR context to populate.
*/
MLIR_CAPI_EXPORTED void mqtRegisterAllDialects(MlirContext ctx);

/**
* @brief Register all conversion and transformation passes of the MQT Compiler
* Collection with MLIR's global pass registry.
*
* @details After this call, every MQT pass can be instantiated by name (e.g.
* via a textual pass pipeline) from Python through the standard MLIR
* PassManager API.
*/
MLIR_CAPI_EXPORTED void mqtRegisterAllPasses(void);

/**
* @brief Import an OpenQASM 3 program into a module of the QC dialect.
*
* @param ctx The MLIR context that owns the resulting module. The QC dialect
* must be registered with this context.
* @param qasm The OpenQASM 3 source program.
* @return The resulting QC-dialect module, or a null module on error. The
* caller owns the returned module.
*/
MLIR_CAPI_EXPORTED MlirModule mqtImportQASM3ToQC(MlirContext ctx,
MlirStringRef qasm);

#ifdef __cplusplus
}
#endif

#endif // MQT_CORE_C_REGISTRATION_H
7 changes: 7 additions & 0 deletions mlir/lib/CAPI/.clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# The C-API registration translation unit includes the dialect, conversion, and
# transformation headers solely to invoke their registration entry points. The
# include-cleaner check cannot trace these uses through the registration
# functions and reports false positives, so it is disabled here.
InheritParentConfig: true
Checks: |
-misc-include-cleaner
46 changes: 46 additions & 0 deletions mlir/lib/CAPI/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
# All rights reserved.
#
# SPDX-License-Identifier: MIT
#
# Licensed under the MIT License

add_mlir_public_c_api_library(
MQTCoreCAPI
Registration.cpp
LINK_LIBS
PUBLIC
# Dialects
MLIRQCDialect
MLIRQCODialect
MLIRQTensorDialect
MLIRJeff
# Conversions
MLIRQCToQCO
MLIRQCOToQC
MLIRQCToQIRBase
MLIRQCToQIRAdaptive
MLIRJeffToQCO
MLIRQCOToJeff
# Transformations
MLIRQCTransforms
MLIRQCOTransforms
MLIRQIRTransforms
MLIRQTensorTransforms
# OpenQASM 3 import
MLIRQCTranslation
MQT::CoreQASM
MQT::CoreIR
# Upstream
MLIRIR
MLIRPass
MLIRTransforms
MLIRArithDialect
MLIRControlFlowDialect
MLIRFuncDialect
MLIRLLVMDialect
MLIRMemRefDialect
MLIRSCFDialect)

mqt_mlir_target_use_project_options(MQTCoreCAPI)
93 changes: 93 additions & 0 deletions mlir/lib/CAPI/Registration.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
* Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
* All rights reserved.
*
* SPDX-License-Identifier: MIT
*
* Licensed under the MIT License
*/

#include "mqt-core-c/Registration.h"

#include "ir/QuantumComputation.hpp"
#include "mlir/Conversion/JeffToQCO/JeffToQCO.h"
#include "mlir/Conversion/QCOToJeff/QCOToJeff.h"
#include "mlir/Conversion/QCOToQC/QCOToQC.h"
#include "mlir/Conversion/QCToQCO/QCToQCO.h"
#include "mlir/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.h"
#include "mlir/Conversion/QCToQIR/QIRBase/QCToQIRBase.h"
#include "mlir/Dialect/QC/IR/QCDialect.h"
#include "mlir/Dialect/QC/Transforms/Passes.h"
#include "mlir/Dialect/QC/Translation/TranslateQuantumComputationToQC.h"
#include "mlir/Dialect/QCO/IR/QCODialect.h"
#include "mlir/Dialect/QCO/Transforms/Passes.h"
#include "mlir/Dialect/QIR/Transforms/Passes.h"
#include "mlir/Dialect/QTensor/IR/QTensorDialect.h"
#include "mlir/Dialect/QTensor/Transforms/Passes.h"
#include "qasm3/Importer.hpp"

#include <jeff/IR/JeffDialect.h>
#include <mlir/CAPI/IR.h>
#include <mlir/Dialect/Arith/IR/Arith.h>
#include <mlir/Dialect/ControlFlow/IR/ControlFlow.h>
#include <mlir/Dialect/Func/IR/FuncOps.h>
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
#include <mlir/Dialect/MemRef/IR/MemRef.h>
#include <mlir/Dialect/SCF/IR/SCF.h>
#include <mlir/IR/BuiltinOps.h>
#include <mlir/IR/DialectRegistry.h>
#include <mlir/IR/MLIRContext.h>
#include <mlir/IR/OwningOpRef.h>
#include <mlir/Transforms/Passes.h>

#include <exception>
#include <string>

void mqtRegisterAllDialects(MlirContext ctx) {
mlir::MLIRContext* context = unwrap(ctx);
mlir::DialectRegistry registry;
registry.insert<mlir::qc::QCDialect, mlir::qco::QCODialect,
mlir::qtensor::QTensorDialect, mlir::jeff::JeffDialect,
mlir::arith::ArithDialect, mlir::cf::ControlFlowDialect,
mlir::func::FuncDialect, mlir::LLVM::LLVMDialect,
mlir::memref::MemRefDialect, mlir::scf::SCFDialect>();
context->appendDialectRegistry(registry);
context->loadAllAvailableDialects();
}

void mqtRegisterAllPasses() {
// Common upstream transformations (canonicalization, CSE, ...) used by the
// MQT cleanup pipelines.
mlir::registerTransformsPasses();

// Conversions between the MQT dialects.
mlir::registerQCToQCOPasses();
mlir::registerQCOToQCPasses();
mlir::registerQCToQIRBasePasses();
mlir::registerQCToQIRAdaptivePasses();
mlir::registerJeffToQCOPasses();
mlir::registerQCOToJeffPasses();

// Dialect-specific transformations.
mlir::qc::registerQCPasses();
mlir::qco::registerQCOPasses();
mlir::qir::registerQIRPasses();
mlir::qtensor::registerQTensorPasses();
}

MlirModule mqtImportQASM3ToQC(MlirContext ctx, MlirStringRef qasm) {
mlir::MLIRContext* context = unwrap(ctx);
try {
const ::qc::QuantumComputation qc =
qasm3::Importer::imports(std::string(qasm.data, qasm.length));
mlir::OwningOpRef<mlir::ModuleOp> module =
mlir::translateQuantumComputationToQC(context, qc);
if (!module) {
return MlirModule{nullptr};
}
return wrap(module.release());
} catch (const std::exception&) {
return MlirModule{nullptr};
}
}
5 changes: 5 additions & 0 deletions mlir/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ add_subdirectory(Conversion)
add_subdirectory(Compiler)
add_subdirectory(Dialect)
add_subdirectory(Support)

# The C-API library backs the Python bindings.
if(BUILD_MQT_CORE_BINDINGS)
add_subdirectory(CAPI)
endif()
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ testpaths = ["test/python"]
run.source = ["mqt.core"]
run.omit = [
'*/_compat/*',
# The MLIR bindings require an MLIR toolchain built with Python bindings.
'*/mqt/core/mlir/*',
Comment on lines +183 to +184

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my general point above.

]
run.disable_warnings = [
"no-sysmon",
Expand Down
35 changes: 35 additions & 0 deletions python/mqt/core/mlir/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
# All rights reserved.
#
# SPDX-License-Identifier: MIT
#
# Licensed under the MIT License

"""Python bindings for the MQT Compiler Collection.

Exposes the dialects and passes of the MQT Compiler Collection to Python via
MLIR's Python bindings. OpenQASM 3 programs are imported into the QC dialect and
returned as native :class:`mlir.ir.Module` objects, which can then be run
through any MQT pass pipeline.
"""

from __future__ import annotations

from .pipeline import (
QASMProgram,
create_context,
read_qasm,
run_pipeline,
transform_to_qco,
translate_to_qc,
)

__all__ = [
"QASMProgram",
"create_context",
"read_qasm",
"run_pipeline",
"transform_to_qco",
"translate_to_qc",
]
Loading