Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
ef0dbcf
🐛 fix control qubit ordering
flowerthrower Dec 9, 2025
8e6f04c
🧪 added python tests and fixed control bug
flowerthrower Dec 10, 2025
b10e41a
✨ add entry point for catalyst passes resolution
flowerthrower Dec 10, 2025
c6da667
✨ add get_catalyst_plugin_abs_path and name2pass functions to locate …
flowerthrower Dec 10, 2025
5d126aa
✅ add tests for MQT plugin setup with PennyLane and Catalyst
flowerthrower Dec 10, 2025
13b3856
✅ isolate python tests
flowerthrower Dec 10, 2025
d3e0869
🐛 fix conversion pattern
flowerthrower Dec 10, 2025
f116c0f
🐛 enable cleanup of loose MLIR files in _cleanup_mlir_artifacts function
flowerthrower Dec 10, 2025
a8feafb
🚧 prepare project for packaging
flowerthrower Dec 10, 2025
6211166
🎨 pre-commit fixes
pre-commit-ci[bot] Dec 10, 2025
1bac976
♻️ restructure
flowerthrower Dec 10, 2025
22afe22
Merge remote-tracking branch 'origin/add-python-plugin' into add-pyth…
flowerthrower Dec 10, 2025
b899fed
🎨 add types
flowerthrower Dec 10, 2025
ac3c49d
🚧 use pre-build mlir workflow
flowerthrower Dec 12, 2025
a2ec8a2
🎨 update llvm-version format in CI configuration
flowerthrower Dec 12, 2025
f4777b8
🚧 use long commit hash
flowerthrower Dec 12, 2025
1529457
🚧 use fix commit
flowerthrower Dec 12, 2025
9ccd2cd
🚧 use branch instead of commit
flowerthrower Dec 12, 2025
9191a86
🐛 fix checks
flowerthrower Dec 12, 2025
5298efd
✅ use FileCheck pattern variables
flowerthrower Dec 12, 2025
45c387b
🔀 cherry pick multi control bug fix
flowerthrower Dec 13, 2025
b023252
✅add multi controlled rotation tests
flowerthrower Dec 13, 2025
8a0350a
🎨 pre-commit
flowerthrower Dec 13, 2025
a9e5eb2
Merge branch 'main' into add-python-plugin
flowerthrower Dec 13, 2025
0d75e83
Trigger CI to test if the latest `setup-mlir` version still works
denialhaag Dec 13, 2025
98c9900
🎨 pre-commit fixes
pre-commit-ci[bot] Dec 13, 2025
a046d42
Merge commit '0c2cf7087ac353e765633ade6c3da6d1632b6393' into add-pyth…
flowerthrower Dec 14, 2025
76605d4
updt uv
flowerthrower Dec 14, 2025
d887656
Merge remote-tracking branch 'origin/add_mlir_plugin' into add-python…
flowerthrower Dec 15, 2025
1d964b8
📝 dont build in RTD
flowerthrower Dec 15, 2025
4870131
🚧 no install
flowerthrower Dec 15, 2025
1561418
🚧 no project
flowerthrower Dec 15, 2025
697659a
Merge commit '0b282a7272b2601a14b052319bab299d27c92787' into add-pyth…
flowerthrower Dec 29, 2025
d1676ce
🔧 Update CI workflow to use latest reusable workflows for Python test…
flowerthrower Dec 29, 2025
facf35e
🎨 pre-commit fixes
pre-commit-ci[bot] Dec 29, 2025
5cd6f0a
🔧 Update CMakeLists.txt to ensure LLVM and MLIR are properly included…
flowerthrower Jan 5, 2026
2d84870
🔥 remove mlir tests
flowerthrower Jan 5, 2026
0215b51
🎨 improve lit discovery for CI
flowerthrower Jan 5, 2026
c7335d3
Merge branch 'main' into add-python-plugin
flowerthrower Jan 5, 2026
71ff792
🎨 add rabbit suggestions
flowerthrower Jan 5, 2026
096a6ea
✅ improve code coverage for error scenarios
flowerthrower Jan 5, 2026
3fc39f8
🎨 improve docsting
flowerthrower Jan 5, 2026
3629e6b
🎨 improve docsting
flowerthrower Jan 5, 2026
c31d56f
🎨 address rabbit feedback
flowerthrower Jan 8, 2026
44e42cc
🎨 imporve docstring
flowerthrower Jan 8, 2026
370efbe
🎨 update readme
flowerthrower Jan 8, 2026
606ac3a
Merge branch 'main' into add-python-plugin
burgholzer Jan 9, 2026
ba2dd54
⬆️ update workflows
burgholzer Jan 9, 2026
463973c
🛂 fix permissions
burgholzer Jan 9, 2026
73cd343
♻️ restructure to satisfy ruff
burgholzer Jan 9, 2026
96a3822
📄 update license headers
burgholzer Jan 9, 2026
0cafd3b
⬆️ update MQT Core to latest stable version
burgholzer Jan 9, 2026
dc6e843
♻️ simplify plugin library search
burgholzer Jan 9, 2026
bfa6b27
🔧 run tests in parallel
burgholzer Jan 9, 2026
fe9e964
🚨 explicitly set up filter warnings
burgholzer Jan 9, 2026
c9eeaf3
🔧 reduce `ty` exclusion list
burgholzer Jan 9, 2026
68531ca
⚡ better rebuilds with cache-keys
burgholzer Jan 9, 2026
579b2db
✅ refactor test
burgholzer Jan 9, 2026
1e91d22
🔧 reduce mypy ignore list
burgholzer Jan 9, 2026
3fbebdb
🔧 add `sitecustomize.py` for multiprocess with coverage
burgholzer Jan 9, 2026
f1fee11
🔧 simplify config
burgholzer Jan 9, 2026
4870515
⏪ revert conf.py changes
burgholzer Jan 9, 2026
4468c8b
⏪ revert RtD setup
burgholzer Jan 9, 2026
be501b5
🔥 remove lit from dependencies
burgholzer Jan 9, 2026
b676eac
⏪ revert more PR changes
burgholzer Jan 9, 2026
2c5a4c6
🔧 clean up the CI workflow
burgholzer Jan 9, 2026
2d81bd6
🔧 ensure pre-commit is run on a reasonable version of Python
burgholzer Jan 9, 2026
f3eb902
⏪ add back pennylane to mypy exports
burgholzer Jan 9, 2026
0d83ce5
👌 incorporate feedback from code rabbit
burgholzer Jan 9, 2026
30e1342
🍎 use macos-15 for builds
burgholzer Jan 9, 2026
8d2c5bc
⏪ also add back `catalyst.*`
burgholzer Jan 9, 2026
7fb3d04
📚 Update CHANGELOG
flowerthrower Jan 9, 2026
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: 1 addition & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
strategy:
fail-fast: false
matrix:
runs-on: [ubuntu-24.04, ubuntu-24.04-arm, macos-14]
runs-on: [ubuntu-24.04, ubuntu-24.04-arm, macos-15]
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11
with:
runs-on: ${{ matrix.runs-on }}
Expand Down
56 changes: 41 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,39 @@ jobs:
name: 🔍 Change
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-change-detection.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11

python-tests:
name: 🐍 Test
needs: change-detection
if: fromJSON(needs.change-detection.outputs.run-python-tests)
strategy:
fail-fast: false
matrix:
runs-on: [ubuntu-24.04, ubuntu-24.04-arm, macos-15]
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-tests.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11
with:
runs-on: ${{ matrix.runs-on }}
setup-mlir: true
llvm-version: "f8cb7987c64dcffb72414a40560055cb717dbf74"

python-coverage:
name: 🐍 Coverage
needs: [change-detection, python-tests]
if: fromJSON(needs.change-detection.outputs.run-python-tests)
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-coverage.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11
permissions:
contents: read
id-token: write

python-linter:
name: 🐍 Lint
needs: change-detection
if: fromJSON(needs.change-detection.outputs.run-python-tests)
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-linter.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11
with:
enable-ty: true
setup-mlir: true
llvm-version: "f8cb7987c64dcffb72414a40560055cb717dbf74"

build-sdist:
name: 🚀 CD (sdist)
needs: change-detection
Expand All @@ -32,22 +65,13 @@ jobs:
strategy:
fail-fast: false
matrix:
runs-on: [ubuntu-24.04, ubuntu-24.04-arm, macos-14]
runs-on: [ubuntu-24.04, ubuntu-24.04-arm, macos-15]
uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@d6314c45667c131055a0389afc110e8dedc6da3f # v1.17.11
with:
runs-on: ${{ matrix.runs-on }}
setup-mlir: true
llvm-version: "f8cb7987c64dcffb72414a40560055cb717dbf74"

mlir-tests:
name: 🐉 Test
needs: change-detection
if: fromJSON(needs.change-detection.outputs.run-cpp-tests)
permissions:
contents: read
id-token: write
uses: ./.github/workflows/reusable-mlir-tests.yml

cpp-linter:
name: 🇨‌ Lint
needs: change-detection
Expand All @@ -74,9 +98,11 @@ jobs:
contents: read
needs:
- change-detection
- python-coverage
- python-tests
- python-linter
- build-sdist
- build-wheel
- mlir-tests
- cpp-linter
runs-on: ubuntu-latest
steps:
Expand All @@ -85,12 +111,12 @@ jobs:
with:
allowed-skips: >-
${{
fromJSON(needs.change-detection.outputs.run-cd)
&& '' || 'build-sdist,build-wheel,'
fromJSON(needs.change-detection.outputs.run-python-tests)
&& '' || 'python-tests,python-coverage,python-linter,'
}}
${{
fromJSON(needs.change-detection.outputs.run-cpp-tests)
&& '' || 'mlir-tests,'
fromJSON(needs.change-detection.outputs.run-cd)
&& '' || 'build-sdist,build-wheel,'
}}
${{
fromJSON(needs.change-detection.outputs.run-cpp-linter)
Expand Down
5 changes: 4 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ ci:
autofix_commit_msg: "🎨 pre-commit fixes"
skip: [mypy, ty-check]

default_language_version:
python: "3.13"

repos:
# Ensure uv lock file is up-to-date
- repo: https://github.com/astral-sh/uv-pre-commit
Expand Down Expand Up @@ -77,7 +80,7 @@ repos:
rev: v1.19.1
hooks:
- id: mypy
files: ^(python/mqt|test/python|noxfile.py)
files: ^(python/mqt|test|noxfile.py)
args: []
additional_dependencies:
- nox
Expand Down
5 changes: 2 additions & 3 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ build:
tools:
python: "3.13"
apt_packages:
- jq
- zstd
- graphviz
jobs:
post_checkout:
# Skip docs build if the commit message contains "skip ci"
- (git --no-pager log --pretty="tformat:%s -- %b" -1 | grep -viq "skip ci") || exit 183
# Skip docs build if there are no changes related to docs
- |
if [ "$READTHEDOCS_VERSION_TYPE" = "external" ] && git diff --quiet origin/main -- docs/ include/ python/ .github/contributing* .github/support* .readthedocs.yaml;
if [ "$READTHEDOCS_VERSION_TYPE" = "external" ] && git diff --quiet origin/main -- docs/ include/ lib/ python/ .readthedocs.yaml;
then
exit 183;
fi
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,21 @@ This project adheres to [Semantic Versioning], with the exception that minor rel

### Added

- 🐍 Introduce Python package providing Catalyst plugin utilities and device configuration ([#20]) ([**@flowerthrower**])
- 🧪 Add comprehensive round-trip Python integration tests ([#20]) ([**@flowerthrower**])
- 🔌 Add MLIR plugin for connecting MQT Core with Catalyst ([#3]) ([**@flowerthrower**], [**@burgholzer**])
- 📦 Set up the initial repo structure and configuration ([#1]) ([**@flowerthrower**], [**@burgholzer**])

### Changed

- 🔄 Migrate testing infrastructure from LIT/MLIR-level to Python/pytest ([#20]) ([**@flowerthrower**])
- 👷 Update CI/CD macOS runners to `macos-15` ([#20]) ([**@flowerthrower**])
- 📦 Bump `mqt-core` version to `v3.4.0` ([#20]) ([**@flowerthrower**])

### Removed

- 🗑️ Remove LIT/MLIR test infrastructure and files ([#20]) ([**@flowerthrower**])

## Initial discussions

_📚 Refer to the [original MQT Core PR] for initial discussions and decisions leading to this project._
Expand All @@ -24,6 +36,7 @@ _📚 Refer to the [original MQT Core PR] for initial discussions and decisions

<!-- PR links -->

[#20]: https://github.com/munich-quantum-toolkit/core-plugins-catalyst/pull/20
[#3]: https://github.com/munich-quantum-toolkit/core-plugins-catalyst/pull/3
[#1]: https://github.com/munich-quantum-toolkit/core-plugins-catalyst/pull/1

Expand Down
1 change: 0 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,3 @@ include_directories(${MLIR_INCLUDE_DIRS})

add_subdirectory(include)
add_subdirectory(lib)
add_subdirectory(test)
62 changes: 49 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ uv venv .venv
. .venv/bin/activate

# Install Catalyst and build the plugin
uv pip install pennylane-catalyst>0.12.0
uv pip install pennylane-catalyst==0.13.0

uv sync --verbose --active
--config-settings=cmake.define.CMAKE_BUILD_TYPE=Release
Expand All @@ -124,42 +124,78 @@ uv sync --verbose --active
--config-settings=cmake.define.LLVM_DIR="$LLVM_DIR"
```

### 3) Use the MQT plugin with your PennyLane code
### 3) Use the MQT plugin and explore intermediate MLIR representations

The MQT plugin provides device configuration utilities to prevent Catalyst from decomposing gates into unitary matrices, enabling lossless roundtrip conversions.

**Important:** Use `get_device()` from the MQT plugin instead of `qml.device()` directly:
You can inspect the intermediate MLIR representations during the roundtrip between `CatalystQuantum` and `MQTOpt` dialects.

```python
from __future__ import annotations
from pathlib import Path
from typing import Any

```python3
import catalyst
import pennylane as qml
from catalyst.passes import apply_pass
from mqt.core.plugins.catalyst import get_device

# Use get_device() to configure the device for MQT plugin compatibility
# This prevents gates from being decomposed into unitary matrices
device = get_device("lightning.qubit", wires=2)


# Wrap your circuit with the conversion passes
@apply_pass("mqt.mqtopt-to-catalystquantum")
@apply_pass("mqt.catalystquantum-to-mqtopt")
@qml.qnode(device)
def circuit() -> None:
qml.Hadamard(wires=[0])
qml.CNOT(wires=[0, 1])
# Controlled gates will NOT be decomposed to matrices
qml.ctrl(qml.PauliX(wires=0), control=1)
catalyst.measure(0)
catalyst.measure(1)


# JIT compile using qjit
@qml.qjit(target="mlir", autograph=True)
def module() -> None:
return circuit()


# Get the optimized MLIR representation
mlir_output = module.mlir_opt
# --- Custom pipeline to capture intermediate MLIR ---
custom_pipeline = [
# Only use the two MQT passes for demonstration
("to-mqtopt", ["builtin.module(catalystquantum-to-mqtopt)"]),
("to-catalystquantum", ["builtin.module(mqtopt-to-catalystquantum)"]),
]


# JIT compilation with intermediate MLIR files saved
@qml.qjit(target="mlir", autograph=True, keep_intermediate=2, pipelines=custom_pipeline)
def module() -> Any:
return circuit()


# Trigger compilation and optimized MLIR generation
module.mlir_opt

# Catalyst writes intermediate MLIR files to the current working directory
mlir_dir = Path.cwd()
catalyst_mlir = mlir_dir / "0_catalyst_module.mlir"
mlir_to_mqtopt = mlir_dir / "1_CatalystQuantumToMQTOpt.mlir"
mlir_to_catalyst = mlir_dir / "4_MQTOptToCatalystQuantum.mlir"

# Read MLIR files
with catalyst_mlir.open("r", encoding="utf-8") as f:
mlir_before_conversion = f.read()
with mlir_to_mqtopt.open("r", encoding="utf-8") as f:
mlir_after_conversion = f.read()
with mlir_to_catalyst.open("r", encoding="utf-8") as f:
mlir_after_roundtrip = f.read()

# Print MLIR contents
print("=== MLIR before conversion to MQTOpt ===")
print(mlir_before_conversion)
print("=== MLIR after conversion to MQTOpt ===")
print(mlir_after_conversion)
print("=== MLIR after roundtrip back to CatalystQuantum ===")
print(mlir_after_roundtrip)
```

**Alternative:** You can also configure an existing device:
Expand All @@ -180,7 +216,7 @@ The MQT Core Catalyst Plugin is compatible with Python version 3.11 and newer.
The MQT Core Catalyst Plugin relies on some external dependencies:

- [llvm/llvm-project](https://github.com/llvm/llvm-project): A toolkit for the construction of highly optimized compilers, optimizers, and run-time environments (specific revision: `f8cb7987c64dcffb72414a40560055cb717dbf74`).
- [PennyLaneAI/catalyst](https://github.com/PennyLaneAI/catalyst): A package that enables just-in-time (JIT) compilation of hybrid quantum-classical programs implemented with PennyLane (version > 0.12.0).
- [PennyLaneAI/catalyst](https://github.com/PennyLaneAI/catalyst): A package that enables just-in-time (JIT) compilation of hybrid quantum-classical programs implemented with PennyLane (version == 0.13.0).
- [MQT Core](https://github.com/munich-quantum-toolkit/core-plugins-catalyst): Provides the MQTOpt MLIR dialect and supporting infrastructure.

Note, both LLVM/MLIR and Catalyst are currently restricted to specific versions. You must build LLVM/MLIR locally from the exact revision specified above and configure CMake to use it (see installation instructions).
Expand Down
6 changes: 3 additions & 3 deletions cmake/ExternalDependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
include(FetchContent)

# cmake-format: off
set(MQT_CORE_MINIMUM_VERSION 3.3.3
set(MQT_CORE_MINIMUM_VERSION 3.4.0
CACHE STRING "MQT Core minimum version")
set(MQT_CORE_VERSION 3.3.3
set(MQT_CORE_VERSION 3.4.0
CACHE STRING "MQT Core version")
set(MQT_CORE_REV "8c9f6ab24968401e450812fc0ff7d05b5ae07a63"
set(MQT_CORE_REV "6bcc01e7d135058c6439c64fdd5f14b65ab88816"
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)")
Expand Down
32 changes: 20 additions & 12 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ Issues = "https://github.com/munich-quantum-toolkit/core-plugins-catalyst/issues
Discussions = "https://github.com/munich-quantum-toolkit/core-plugins-catalyst/discussions"
PyPI = "https://pypi.org/project/mqt-core-plugins-catalyst/"

[project.entry-points."catalyst.passes_resolution"]
"mqt.passes" = "mqt.core.plugins.catalyst"

[tool.scikit-build]
# Protect the configuration against future changes in scikit-build-core
minimum-version = "build-system.requires"
Expand Down Expand Up @@ -120,11 +123,15 @@ strict = true
addopts = [
"-ra",
"--numprocesses=auto", # Automatically use all available CPU cores for parallel testing
"--dist=loadfile", # Run tests from the same file sequentially (tests share MLIR files) while parallelizing across files
]
filterwarnings = [
"error",
'ignore:.*The jaxlib.hlo_helpers submodule is deprecated.*:DeprecationWarning:',
"ignore:.*got an unexpected keyword argument '___pyct_anno'.*:DeprecationWarning:",
]
log_level = "INFO"
testpaths = ["test/python"]
testpaths = ["test"]


[tool.coverage]
Expand All @@ -145,7 +152,7 @@ report.exclude_also = [
]

[tool.mypy]
files = ["python/mqt", "test/python", "noxfile.py"]
files = ["python/mqt", "test", "noxfile.py"]
mypy_path = ["$MYPY_CONFIG_FILE_DIR/python"]
python_version = "3.11"
warn_unused_configs = true
Expand All @@ -156,7 +163,7 @@ explicit_package_bases = true
warn_unreachable = true

[[tool.mypy.overrides]]
module = ["pytest_console_scripts.*"]
module = ["pytest_console_scripts.*", "pennylane.*", "catalyst.*"]
ignore_missing_imports = true

[tool.ruff]
Expand Down Expand Up @@ -185,8 +192,8 @@ future-annotations = true
known-first-party = ["mqt.core.plugins.catalyst"]

[tool.ruff.lint.per-file-ignores]
"test/python/**" = ["T20", "INP001"]
"test/lit.cfg.py" = ["INP001"]
"test/**" = ["T20", "INP001"]
"test/test_plugin.py" = ["E501"]
"docs/**" = ["T20", "INP001"]
"noxfile.py" = ["T20", "TID251"]
"*.pyi" = ["D418", "DOC202", "PYI011", "PYI021"]
Expand Down Expand Up @@ -230,8 +237,8 @@ build = "cp3*"
skip = ["*-musllinux_*", "cp314*"] # No CPython 3.14 support yet
archs = "auto64"
test-groups = ["test"]
test-sources = ["test/python"]
test-command = "pytest test/python"
test-sources = ["test"]
test-command = "pytest test"
build-frontend = "build[uv]"

[tool.cibuildwheel.linux]
Expand Down Expand Up @@ -263,7 +270,12 @@ environments = [
"sys_platform == 'darwin' and platform_machine == 'arm64' and python_version < '3.14'",
"sys_platform == 'linux' and python_version < '3.14'",
]
reinstall-package = ["mqt-core-plugins-catalyst"]
cache-keys = [
{ file = "pyproject.toml" },
{ git = { commit = true, tags = true } },
{ file = "lib/**/*"},
{ file = "include/**/*"},
]


[tool.ty.terminal]
Expand All @@ -273,9 +285,6 @@ error-on-warning = true
[tool.ty.src]
exclude = [
"docs/**",
"eval/**",
"mlir/**",
"test/lit.cfg.py",
]


Expand Down Expand Up @@ -310,7 +319,6 @@ test = [
dev = [
{include-group = "build"},
{include-group = "test"},
"lit>=18.1.8",
"nox>=2025.11.12",
"ty==0.0.10",
]
Loading
Loading