Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e5280c5
✨ Add Euler decomposition and supporting decomposition primitives
simon1hofmann Apr 28, 2026
01ff591
🐇 Address Rabbit's Comments
simon1hofmann Apr 28, 2026
2c74981
📝 Update factorization documentation in EulerDecomposition
simon1hofmann Apr 28, 2026
b7307a3
📝 Update docs and comments
simon1hofmann Apr 29, 2026
e978f16
🐇 Address Rabbit's Comments
simon1hofmann Apr 29, 2026
d1a85d3
Merge branch 'main' into decomp/euler
denialhaag Apr 29, 2026
1d1ba72
Adapt to new include style
denialhaag Apr 29, 2026
3c7370d
Merge branch 'main' into decomp/euler
simon1hofmann May 19, 2026
ab35f91
✨ Add QCO UnitaryMatrixOpInterface for compile-time unitary matrices
simon1hofmann May 19, 2026
6cb4af2
♻️ Use UnitaryMatrixOpInterface in CtrlOp and InvOp matrix expansion
simon1hofmann May 19, 2026
abd87fc
♻️ Consolidate QCO Euler decomposition into Euler.h/cpp
simon1hofmann May 19, 2026
9c10cb6
✨ Add fuse-single-qubit-unitary-runs pass for Euler resynthesis
simon1hofmann May 19, 2026
54ca07d
✅ Add tests for Euler synthesis and fuse-single-qubit-unitary-runs
simon1hofmann May 19, 2026
1c01e71
📝 Document fuse-single-qubit-unitary-runs in CHANGELOG
simon1hofmann May 19, 2026
a92a2b6
🚨 Fix linter warnings
simon1hofmann May 19, 2026
577c094
🚨 Fix linter warnings
simon1hofmann May 19, 2026
6e7fa7f
🐇 Address Rabbit's comments
simon1hofmann May 19, 2026
ebc984f
Merge branch 'main' into decomp/euler
simon1hofmann Jun 2, 2026
418b17e
✅ Refactor QCO decomposition tests and update dependencies
simon1hofmann Jun 3, 2026
7da85cd
🎨 Refactor Euler basis handling in QCO transformations
simon1hofmann Jun 3, 2026
5e2cbdc
🎨 Refactor Euler decomposition operations in QCO
simon1hofmann Jun 3, 2026
8eb937c
🎨 Refactor matrix handling and wire start collection in QCO transform…
simon1hofmann Jun 3, 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: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel

### Added

- ✨ Add a `fuse-single-qubit-unitary-runs` pass for fusing compile-time single-qubit unitary runs via Euler resynthesis ([#1672]) ([**@simon1hofmann**])
- 🚸 Add [CMake presets] to provide a standardized and reproducible way to configure builds ([#1660]) ([**@denialhaag**])
- ✨ Add a `quantum-loop-unroll` pass for unrolling for-loop operations containing quantum operations ([#1718]) ([**@MatthiasReumann**])
- ✨ Add a `hadamard-lifting` pass for lifting Hadamard gates above Pauli gates ([#1605]) ([**@lirem101**], [**@burgholzer**])
Expand Down Expand Up @@ -421,6 +422,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool
[#1675]: https://github.com/munich-quantum-toolkit/core/pull/1675
[#1674]: https://github.com/munich-quantum-toolkit/core/pull/1674
[#1673]: https://github.com/munich-quantum-toolkit/core/pull/1673
[#1672]: https://github.com/munich-quantum-toolkit/core/pull/1672
[#1664]: https://github.com/munich-quantum-toolkit/core/pull/1664
[#1662]: https://github.com/munich-quantum-toolkit/core/pull/1662
[#1660]: https://github.com/munich-quantum-toolkit/core/pull/1660
Expand Down
3 changes: 3 additions & 0 deletions mlir/include/mlir/Dialect/QCO/IR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

add_mlir_dialect(QCOOps qco)
add_mlir_interface(QCOInterfaces)
add_mlir_interface(QCOUnitaryMatrixInterfaces)

add_mlir_doc(QCOOps QCODialect Dialects/ -gen-dialect-doc)
add_mlir_doc(QCOInterfaces QCOInterfaces Dialects/ -gen-op-interface-docs -dialect=qco)
add_mlir_doc(QCOUnitaryMatrixInterfaces QCOUnitaryMatrixInterfaces Dialects/ -gen-op-interface-docs
-dialect=qco)
1 change: 0 additions & 1 deletion mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

#pragma once

#include <Eigen/Core>
#include <llvm/Support/ErrorHandling.h>
#include <mlir/IR/OpDefinition.h>
#include <mlir/Support/LLVM.h>
Expand Down
111 changes: 1 addition & 110 deletions mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.td
Original file line number Diff line number Diff line change
Expand Up @@ -28,62 +28,6 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> {

let cppNamespace = "::mlir::qco";

// Generic implementation body for getUnitaryMatrix methods
defvar unitaryMatrixMethodBody = [{
auto process = [&]<typename MatrixType>(MatrixType&& m) -> bool {
using TargetT = std::remove_cvref_t<decltype(out)>;
using SourceT = std::remove_cvref_t<MatrixType>;

constexpr bool isTargetDynamic =
(TargetT::SizeAtCompileTime == Eigen::Dynamic);
constexpr bool isSourceDynamic =
(SourceT::SizeAtCompileTime == Eigen::Dynamic);

// Case 1: Target is Dynamic. Always accepts source.
if constexpr (isTargetDynamic) {
out = std::forward<MatrixType>(m);
return true;
}
// Case 2: Target is Fixed.
else {
// Case 2a: Source is Dynamic. Runtime dimension check required.
if constexpr (isSourceDynamic) {
if (m.rows() == static_cast<Eigen::Index>(TargetT::RowsAtCompileTime) &&
m.cols() == static_cast<Eigen::Index>(TargetT::ColsAtCompileTime))
[[likely]] {
out = std::forward<MatrixType>(m);
return true;
}
}
// Case 2b: Source is Fixed. Compile-time check.
else if constexpr (static_cast<Eigen::Index>(
SourceT::RowsAtCompileTime) ==
static_cast<Eigen::Index>(
TargetT::RowsAtCompileTime) &&
static_cast<Eigen::Index>(
SourceT::ColsAtCompileTime) ==
static_cast<Eigen::Index>(
TargetT::ColsAtCompileTime)) {
out = std::forward<MatrixType>(m);
return true;
}
}
return false;
};


if constexpr (requires { $_op.getUnitaryMatrix().has_value(); }) {
if (auto&& matrix = $_op.getUnitaryMatrix()) {
return process(std::move(*matrix));
}
return false;
} else if constexpr (requires { $_op.getUnitaryMatrix(); }) {
return process($_op.getUnitaryMatrix());
} else {
llvm::reportFatalUsageError("Operation '" + $_op.getBaseSymbol() + "' has no unitary matrix definition!");
}
}];

let methods = [
// Qubit accessors
InterfaceMethod<
Expand Down Expand Up @@ -152,60 +96,7 @@ def UnitaryOpInterface : OpInterface<"UnitaryOpInterface"> {

// Identification
InterfaceMethod<"Returns the base symbol/mnemonic of the operation.",
"StringRef", "getBaseSymbol", (ins)>,

// Unitary matrix helpers
InterfaceMethod<"Populates the given 1x1 unitary matrix if possible.",
"bool", "getUnitaryMatrix1x1",
(ins "Eigen::Matrix<std::complex<double>, 1, 1>&":$out),
unitaryMatrixMethodBody>,
InterfaceMethod<"Populates the given 2x2 unitary matrix if possible.",
"bool", "getUnitaryMatrix2x2",
(ins "Eigen::Matrix2cd&":$out), unitaryMatrixMethodBody>,
InterfaceMethod<"Populates the given 4x4 unitary matrix if possible.",
"bool", "getUnitaryMatrix4x4",
(ins "Eigen::Matrix4cd&":$out), unitaryMatrixMethodBody>,
InterfaceMethod<"Populates the given dynamic unitary matrix.", "bool",
"getUnitaryMatrixDynamic", (ins "Eigen::MatrixXcd&":$out),
unitaryMatrixMethodBody>];

let extraClassDeclaration = [{
template<typename MatrixType>
std::optional<MatrixType> getUnitaryMatrix() {
MatrixType out;
bool result = false;

// Dispatch to the appropriate fixed-size or dynamic method based on the
// matrix type.
if constexpr (MatrixType::RowsAtCompileTime == 1 &&
MatrixType::ColsAtCompileTime == 1) {
result = this->getUnitaryMatrix1x1(out);
} else if constexpr (MatrixType::RowsAtCompileTime == 2 &&
MatrixType::ColsAtCompileTime == 2) {
result = this->getUnitaryMatrix2x2(out);
} else if constexpr (MatrixType::RowsAtCompileTime == 4 &&
MatrixType::ColsAtCompileTime == 4) {
result = this->getUnitaryMatrix4x4(out);
} else if constexpr (MatrixType::SizeAtCompileTime == Eigen::Dynamic) {
result = this->getUnitaryMatrixDynamic(out);
} else {
// Fallback: Try obtaining dynamic matrix and see if size matches
Eigen::MatrixXcd dynamicOut;
if (this->getUnitaryMatrixDynamic(dynamicOut)) {
if (dynamicOut.rows() == MatrixType::RowsAtCompileTime &&
dynamicOut.cols() == MatrixType::ColsAtCompileTime) {
out = dynamicOut;
result = true;
}
}
}

if (result) {
return out;
}
return std::nullopt;
}
}];
"StringRef", "getBaseSymbol", (ins)>];
}

#endif // MLIR_DIALECT_QCO_IR_QCOINTERFACES_TD
1 change: 1 addition & 0 deletions mlir/include/mlir/Dialect/QCO/IR/QCOOps.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.

Just to question the added value of the separate header a little bit and because I am curious: Does this really bring us a meaningful advantage in terms of not having to include Eigen (transitively) as often? How often do we only import the QCODialect or QCOInterfaces header, but not the QCOOps header?

Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include "mlir/Dialect/QCO/IR/QCODialect.h"
#include "mlir/Dialect/QCO/IR/QCOInterfaces.h"
#include "mlir/Dialect/QCO/IR/QCOUnitaryMatrixInterfaces.h"

#include <mlir/Bytecode/BytecodeOpInterface.h>
#include <mlir/Interfaces/ControlFlowInterfaces.h>
Expand Down
Loading
Loading