-
Notifications
You must be signed in to change notification settings - Fork 41
MLIR Plugin #881
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
MLIR Plugin #881
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Now that #879 is merged, this needs to be rebased |
# Conflicts: # .gitignore # mlir/include/mlir/Dialect/MQTOpt/IR/MQTOptTraits.h # mlir/lib/Dialect/MQTOpt/Transforms/FromQuantumComputationPattern.cpp # mlir/lib/Dialect/MQTOpt/Transforms/ToQuantumComputationPattern.cpp # mlir/tools/quantum-opt/quantum-opt.cpp
Signed-off-by: burgholzer <[email protected]>
Signed-off-by: burgholzer <[email protected]>
Signed-off-by: burgholzer <[email protected]>
Signed-off-by: burgholzer <[email protected]>
Signed-off-by: burgholzer <[email protected]>
Alright. I considerably cleaned up the implementation today. The biggest thing missing is a packaging setup that would allow us to release packages to PyPI. I'll try to get this done asap. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@flowerthrower @BertiFlorea Tagging you here explicitly so you hopefully see the comments. 😌
# name: 🧩 Test MLIR Catalyst Plugin with LLVM@${{ matrix.llvm-version }} on ${{ matrix.runs-on }} | ||
# runs-on: ${{ matrix.runs-on }} | ||
# strategy: | ||
# matrix: | ||
# runs-on: [ubuntu-24.04, ubuntu-24.04-arm, macos-14] | ||
# llvm-version: [19, 20] | ||
# fail-fast: false | ||
# env: | ||
# CMAKE_BUILD_PARALLEL_LEVEL: 4 | ||
# CTEST_PARALLEL_LEVEL: 4 | ||
# FORCE_COLOR: 3 | ||
# steps: | ||
# # Check out the repository | ||
# - uses: actions/checkout@v4 | ||
# with: | ||
# fetch-depth: 0 | ||
# | ||
# # install a specific version of the LLVM toolchain | ||
# - name: Install llvm and mlir | ||
# if: runner.os == 'Linux' | ||
# run: | | ||
# sudo apt-get update | ||
# wget https://apt.llvm.org/llvm.sh -O ${{ runner.temp }}/llvm_install.sh | ||
# chmod +x ${{ runner.temp }}/llvm_install.sh | ||
# if sudo ${{ runner.temp }}/llvm_install.sh ${{ matrix.llvm-version }}; then | ||
# sudo apt-get install -y libmlir-${{ matrix.llvm-version }}-dev \ | ||
# mlir-${{ matrix.llvm-version }}-tools \ | ||
# clang-${{ matrix.llvm-version}} \ | ||
# clang-tools-${{ matrix.llvm-version }} \ | ||
# || exit 1 | ||
# else | ||
# echo "Installation from script failed." | ||
# exit 1 | ||
# fi | ||
# echo "CC=gcc-14" >> $GITHUB_ENV | ||
# echo "CXX=g++-14" >> $GITHUB_ENV | ||
# echo "MLIR_DIR=/usr/lib/llvm-${{ matrix.llvm-version }}/lib/cmake/mlir" >> $GITHUB_ENV | ||
# echo "LLVM_DIR=/usr/lib/llvm-${{ matrix.llvm-version }}/lib/cmake/llvm" >> $GITHUB_ENV | ||
# | ||
# # Install llvm and ninja | ||
# - name: Install llvm@${{ matrix.llvm-version }} and Ninja via Homebrew | ||
# if: runner.os == 'macOS' | ||
# run: brew install llvm@${{ matrix.llvm-version }} ninja | ||
# | ||
# # Set compiler and environment variables | ||
# - name: Set compiler and CMake environment variables | ||
# if: runner.os == 'macOS' | ||
# run: | | ||
# if [ "$(uname -m)" = "x86_64" ]; then | ||
# LLVM_PREFIX="/usr/local/opt/llvm@${{ matrix.llvm-version }}" | ||
# else | ||
# LLVM_PREFIX="/opt/homebrew/opt/llvm@${{ matrix.llvm-version }}" | ||
# fi | ||
# | ||
# echo "CC=$LLVM_PREFIX/bin/clang" >> $GITHUB_ENV | ||
# echo "CXX=$LLVM_PREFIX/bin/clang++" >> $GITHUB_ENV | ||
# echo "LLVM_DIR=$LLVM_PREFIX/lib/cmake/llvm" >> $GITHUB_ENV | ||
# echo "MLIR_DIR=$LLVM_PREFIX/lib/cmake/mlir" >> $GITHUB_ENV | ||
# | ||
# # set up ccache for faster C++ builds | ||
# - name: Setup ccache | ||
# uses: Chocobo1/setup-ccache-action@v1 | ||
# with: | ||
# prepend_symlinks_to_path: false | ||
# override_cache_key: mlir-plugin-${{ matrix.runs-on }}-llvm-${{ matrix.llvm-version }} | ||
# | ||
# # Set up uv for faster Python package management | ||
# - name: Install the latest version of uv | ||
# uses: astral-sh/setup-uv@v6 | ||
# with: | ||
# python-version: 3.13 | ||
# activate-environment: true | ||
# | ||
# # Build the plugin package | ||
# - name: Build mqt-core-catalyst-plugin | ||
# working-directory: plugins/catalyst | ||
# run: uv sync --verbose --active | ||
# | ||
# # Print shared library dependencies for the plugin | ||
# - name: Print shared library dependencies for mqt-core-catalyst-plugin | ||
# run: | | ||
# if [ "$(uname)" = "Linux" ]; then | ||
# ldd .venv/lib/python3.13/site-packages/mqt/core/plugins/catalyst/mqt-core-catalyst-plugin.so || true | ||
# elif [ "$(uname)" = "Darwin" ]; then | ||
# otool -l .venv/lib/python3.13/site-packages/mqt/core/plugins/catalyst/mqt-core-catalyst-plugin.dylib || true | ||
# fi | ||
# | ||
# # Print shared library dependencies for Catalyst | ||
# - name: Print shared library dependencies for Catalyst | ||
# run: | | ||
# if [ "$(uname)" = "Linux" ]; then | ||
# ldd $(which catalyst) || true | ||
# elif [ "$(uname)" = "Darwin" ]; then | ||
# otool -l $(which catalyst) || true | ||
# fi | ||
# | ||
# # Run the plugin tests | ||
# - name: Run pytest for mqt-core-catalyst-plugin | ||
# working-directory: plugins/catalyst | ||
# run: python -m pytest -s --verbose |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd still like to investigate at some point whether there is anything we can do to make this work with pre-compiled LLVM versions. But this will have to wait for a future PR. It would be great to create a tracking issue for this though.
# MQT Core Catalyst MLIR Plugin | ||
|
||
This sub-package of MQT Core provides a [Catalyst](https://github.com/PennyLaneAI/catalyst) plugin for the [MLIR](https://mlir.llvm.org/) framework. | ||
It allows you to use MQT Core's MLIR dialects and transformations within the Catalyst framework, enabling advanced quantum circuit optimizations and transformations. | ||
|
||
TODO: extend this section with more details about the Catalyst plugin, its features, and how to use it. | ||
|
||
If you have any questions, feel free to create a [discussion](https://github.com/munich-quantum-toolkit/core/discussions) or an [issue](https://github.com/munich-quantum-toolkit/core/issues) on [GitHub](https://github.com/munich-quantum-toolkit/core). | ||
|
||
## Contributors and Supporters | ||
|
||
The _[Munich Quantum Toolkit (MQT)](https://mqt.readthedocs.io)_ is developed by the [Chair for Design Automation](https://www.cda.cit.tum.de/) at the [Technical University of Munich](https://www.tum.de/) and supported by the [Munich Quantum Software Company (MQSC)](https://munichquantum.software). | ||
Among others, it is part of the [Munich Quantum Software Stack (MQSS)](https://www.munich-quantum-valley.de/research/research-areas/mqss) ecosystem, which is being developed as part of the [Munich Quantum Valley (MQV)](https://www.munich-quantum-valley.de) initiative. | ||
|
||
<p align="center"> | ||
<picture> | ||
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/munich-quantum-toolkit/.github/refs/heads/main/docs/_static/mqt-logo-banner-dark.svg" width="90%"> | ||
<img src="https://raw.githubusercontent.com/munich-quantum-toolkit/.github/refs/heads/main/docs/_static/mqt-logo-banner-light.svg" width="90%" alt="MQT Partner Logos"> | ||
</picture> | ||
</p> | ||
|
||
Thank you to all the contributors who have helped make MQT Core a reality! | ||
|
||
<p align="center"> | ||
<a href="https://github.com/munich-quantum-toolkit/core/graphs/contributors"> | ||
<img src="https://contrib.rocks/image?repo=munich-quantum-toolkit/core" /> | ||
</a> | ||
</p> | ||
|
||
## Getting Started | ||
|
||
TODO | ||
|
||
## System Requirements | ||
|
||
TODO | ||
|
||
## Cite This | ||
|
||
If you want to cite MQT Core, please use the following BibTeX entry: | ||
|
||
```bibtex | ||
TODO | ||
``` | ||
|
||
--- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still pretty much to be done
mlirGetDialectPluginInfo() { | ||
return {MLIR_PLUGIN_API_VERSION, "MQTOpt", LLVM_VERSION_STRING, | ||
[](DialectRegistry* registry) { | ||
registry->insert<::mqt::ir::opt::MQTOptDialect>(); | ||
}}; | ||
} | ||
|
||
/// The pass plugin registration mechanism. | ||
/// Necessary symbol to register the pass plugin. | ||
extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo mlirGetPassPluginInfo() { | ||
return {MLIR_PLUGIN_API_VERSION, "MQTOptPasses", LLVM_VERSION_STRING, []() { | ||
mqt::ir::opt::registerMQTOptPasses(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the future, we will most likely want to expose all of our dialects and transformations/conversions here. Could be worth tracking that in an issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These files are here, but they are not at all executed.
I'd very much argue that we need more explicit testing for the plugin.
Either in the form of C++ tests running these FileCheck files or via more extensive Python-side tests. I am fairly confident that some of the conversions are not yet correct.
It might be easier to write FileCheck tests than Python tests. Although I am not 100% sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The amount of testing of the plugin is clearly still lacking quite a bit.
This has become quite obvious because it didn't even catch a bug that would flip the controls and targets of any controlled operation that got translated leading to incorrect programs.
So this definitely needs more tests that actually test more than the simple error-free execution of the passes.
* Licensed under the MIT License | ||
*/ | ||
|
||
#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We still need to do something about clang-tidy not working correctly for the plugin.
The main reason for that is that the plugin build is completely loose from the main mqt-core build. Hence, there is no compilation database, and clang-tidy has problems finding the headers in the build directory.
As a first step, we should probably figure out how to best ignore the plugins
directory from the clang-tidy run. However, mid-term, I would really want to have a clang-tidy check running properly over the code here. CLion locally revealed dozens of warnings that were fixed in the last couple of commits.
// RZX(θ) = H(q2) CNOT(q1,q2) RZ(θ)(q2) CNOT(q1,q2) H(q2) | ||
auto h1 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{}, inQubitsValues, | ||
"H", false, inCtrlQubits, ValueRange{}); | ||
|
||
auto cnot1 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{}, h1.getResults(), | ||
"CNOT", false, inCtrlQubits, ValueRange{}); | ||
|
||
auto rz = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{theta}, | ||
cnot1.getResults(), "RZ", false, inCtrlQubits, ValueRange{}); | ||
|
||
auto cnot2 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{}, rz.getResults(), | ||
"CNOT", false, inCtrlQubits, ValueRange{}); | ||
|
||
auto h2 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{}, | ||
cnot2.getResults(), "H", false, inCtrlQubits, ValueRange{}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This (as well as some of the constructions below) cannot be correct yet as the number of qubits in the various operations is not correct.
A Hadamard only acts on one qubit, so it cannot take both input qubits as an argument. Similarly, the CNOT can not work just on the qubit resulting from the CNOT.
Proper testing would have revealed that.
In this case, the code can even be simplified: the sequence of CNOT, Rz, CNOT forms an RZZ gate. May as well use that. And the controls only need to be applied to that RZZ gate; both H gates can remain uncontrolled because they cancel whenever the controls of the RZZ gate are not activated.
// XXminusYY(θ,β) = RX(π/2)(q1) RY(π/2)(q2) CNOT(q1,q2) RZ(θ)(q2) | ||
// CNOT(q1,q2) RZ(β)(q1) RZ(β)(q2) RX(-π/2)(q1) RY(-π/2)(q2) | ||
auto pi2 = rewriter.create<ConstantOp>(op.getLoc(), | ||
rewriter.getF64FloatAttr(M_PI_2)); | ||
auto negPi2 = rewriter.create<ConstantOp>( | ||
op.getLoc(), rewriter.getF64FloatAttr(-M_PI_2)); | ||
|
||
auto rx1 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{pi2}, | ||
inQubitsValues, "RX", false, inCtrlQubits, ValueRange{}); | ||
|
||
auto ry1 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{pi2}, | ||
rx1.getResults(), "RY", false, inCtrlQubits, ValueRange{}); | ||
|
||
auto cnot1 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{}, ry1.getResults(), | ||
"CNOT", false, inCtrlQubits, ValueRange{}); | ||
|
||
auto rz1 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{theta}, | ||
cnot1.getResults(), "RZ", false, inCtrlQubits, ValueRange{}); | ||
|
||
auto cnot2 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{}, rz1.getResults(), | ||
"CNOT", false, inCtrlQubits, ValueRange{}); | ||
|
||
auto rz2 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{beta}, | ||
cnot2.getResults(), "RZ", false, inCtrlQubits, ValueRange{}); | ||
|
||
auto rz3 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{beta}, | ||
rz2.getResults(), "RZ", false, inCtrlQubits, ValueRange{}); | ||
|
||
auto rx2 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{negPi2}, | ||
rz3.getResults(), "RX", false, inCtrlQubits, ValueRange{}); | ||
|
||
auto ry2 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{negPi2}, | ||
rx2.getResults(), "RY", false, inCtrlQubits, ValueRange{}); | ||
|
||
rewriter.replaceOp(op, ry2.getResults()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This construction also does not work.
// Peres = Toffoli(q2,q1,q0) CNOT(q2,q1) | ||
auto ccnot1 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{}, inQubitsValues, | ||
"Toffoli", false, inCtrlQubits, ValueRange{}); | ||
|
||
auto cnot2 = rewriter.create<catalyst::quantum::CustomOp>( | ||
op.getLoc(), outQubitTypes, TypeRange{}, ValueRange{}, | ||
ccnot1.getResults(), "CNOT", false, inCtrlQubits, ValueRange{}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not correct. The Peres Gate in mqt-core is a two-target gate that decomposes down to a CNOT and an X gate. It does not use 3 qubits. Same holds for the inverse.
// -- GPhaseOp (gphase) | ||
template <> | ||
StringRef ConvertMQTOptSimpleGate<opt::GPhaseOp>::getGateName( | ||
[[maybe_unused]] std::size_t numControls) { | ||
return "gphase"; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not 100% sure this is correct. If I remember correctly, Catalyst has its separate operation for the global phase. I might be mistaken, though.
Description
This PR introduces a MLIR plguin such that the round-trip pass can be executed with Pennylane Catalyst.
It additionally provides the plugin as a wheel.
Checklist: