Skip to content

✨ Add two-qubit Weyl (KAK) decomposition library#1803

Open
simon1hofmann wants to merge 17 commits into
mainfrom
decomp/weyl
Open

✨ Add two-qubit Weyl (KAK) decomposition library#1803
simon1hofmann wants to merge 17 commits into
mainfrom
decomp/weyl

Conversation

@simon1hofmann

@simon1hofmann simon1hofmann commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Description

Adds a two-qubit Weyl (KAK) decomposition on top of Matrix4x4. A 4×4 unitary is factored as (K1l ⊗ K1r) · U_canon(a,b,c) · (K2l ⊗ K2r) up to global phase, with optional canonical-gate specialization and basis-gate (CX/CZ) synthesis.
This PR is intentionally scoped to the decomposition library + tests. It does not wire an MLIR pass, emit gate sequences, or add NativeProfile / fuse-pass integration — those are planned follow-ups.

New API (mlir/Dialect/QCO/Transforms/Decomposition/Weyl.h)

  • TwoQubitWeylDecomposition — KAK decomposition with Weyl-chamber reduction and 10 canonical-gate specializations (identity, SWAP, partial-SWAP ±, controlled, mirror-controlled, fSim variants)
  • TwoQubitBasisDecomposer — decomposes a Weyl result into single-qubit factors + basis-gate count (0–3 uses)

Supporting changes

  • Matrix.h / Matrix.cpp — shared gate matrix factories: rx/ry/rz, iPauliX/Y/Z, rxx/ryy/rzz
  • Euler.h / Euler.cpp — promote EulerAngles and anglesFromUnitary() to public API (used by Weyl specialization for controlled / fSim cases)

AI Assistance

Used Composer 2.5 via Cursor for parts of this change. I reviewed the full
diff and take responsibility for everything in this PR.

Checklist

  • The pull request only contains commits that are focused and relevant to this change.
  • I have added appropriate tests that cover the new/changed functionality.
  • I have updated the documentation to reflect these changes.
  • I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.
  • I have added migration instructions to the upgrade guide (if needed).
  • The changes follow the project's style guidelines and introduce no new warnings.
  • The changes are fully tested and pass the CI checks.
  • I have reviewed my own code changes.

If PR contains AI-assisted content:

  • I have disclosed the use of AI tools in the PR description as per our AI Usage Guidelines.
  • AI-assisted commits include an Assisted-by: [Model Name] via [Tool Name] footer.
  • I confirm that I have personally reviewed and understood all AI-generated content, and accept full responsibility for it.

@codecov

codecov Bot commented Jun 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.87629% with 24 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
.../lib/Dialect/QCO/Transforms/Decomposition/Weyl.cpp 95.7% 15 Missing ⚠️
...t/QCO/Transforms/Decomposition/BasisDecomposer.cpp 94.7% 9 Missing ⚠️

📢 Thoughts on this report? Let us know!

@simon1hofmann

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a full two-qubit Weyl decomposition and basis-gate synthesis pipeline to the MLIR QCO dialect. New rotation and i-Pauli matrix primitives are introduced in the Matrix utility layer, EulerAngles is promoted to a public header, and new Weyl.h/Weyl.cpp and BasisDecomposer.cpp files implement TwoQubitWeylDecomposition, TwoQubitBasisDecomposer, and supporting numerical helpers. Comprehensive GTest validation is added.

Changes

Weyl Two-Qubit Decomposition Pipeline

Layer / File(s) Summary
Rotation and i-Pauli matrix primitives
mlir/include/mlir/Dialect/QCO/Utils/Matrix.h, mlir/lib/Dialect/QCO/Utils/Matrix.cpp
Declares and implements rxMatrix/ryMatrix/rzMatrix, iPauliX/iPauliY/iPauliZ, and rxxMatrix/ryyMatrix/rzzMatrix as new free functions returning rotation matrices and cached i-times-Pauli references.
EulerAngles promoted to public header
mlir/include/mlir/Dialect/QCO/Transforms/Decomposition/Euler.h, mlir/lib/Dialect/QCO/Transforms/Decomposition/Euler.cpp
Moves the EulerAngles struct from an anonymous namespace in Euler.cpp into Euler.h and changes anglesFromUnitary from a static internal helper to a public non-static function.
Weyl decomposition public API header
mlir/include/mlir/Dialect/QCO/Transforms/Decomposition/Weyl.h
Defines WEYL_TOLERANCE, TwoQubitWeylDecomposition, TwoQubitNativeDecomposition, TwoQubitBasisDecomposer, and the decomposeTwoQubitWithBasis convenience function as the complete public interface for two-qubit Weyl decomposition.
Weyl numerical helpers and coordinate extraction
mlir/lib/Dialect/QCO/Transforms/Decomposition/Weyl.cpp
Implements remEuclid, traceToFidelity, magicBasisTransform, closestPartialSwap, diagonalizeComplexSymmetric, decomposeTwoQubitProductGate, getTrace, bestSpecialization, chamber state construction, and Pauli-conjugation normalizations as internal numerical substrate for Weyl extraction.
TwoQubitWeylDecomposition::create, specialization, and canonical matrix
mlir/lib/Dialect/QCO/Transforms/Decomposition/Weyl.cpp
Implements create orchestrating SU(4) projection, magic-basis transform, eigenphase extraction, Weyl-chamber mapping, local factor decomposition, specialization, and phase adjustment with reconstruction validation; defines canonical RZZ·RYY·RXX matrix construction and applySpecialization for all equivalence-class rewrites.
TwoQubitBasisDecomposer implementation
mlir/lib/Dialect/QCO/Transforms/Decomposition/BasisDecomposer.cpp
Implements TwoQubitBasisDecomposer::create with template precomputation and super-controlled detection; decomposeTarget and twoQubitDecompose with trace-based fidelity scoring and basis-gate-count selection; decomp0–3 template instantiations; traces computation; and decomposeTwoQubitWithBasis wrapper.
Unit tests and build configuration
mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_weyl_decomposition.cpp, test_euler_decomposition.cpp, CMakeLists.txt, mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt
Adds parameterized Weyl reconstruction and basis-decomposer tests, randomized stress tests (5000 Weyl, 2000 basis targets), forced basis-count validation, and canonical specialization checks; extends Euler tests with coverage cases for anglesFromUnitary edge conditions; removes now-redundant local matrix helpers; expands CMake target to build and link the new test file and additional QCO library components.

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant decomposeTwoQubitWithBasis
  participant TwoQubitWeylDecomposition
  participant TwoQubitBasisDecomposer
  participant MagicBasis

  Caller->>decomposeTwoQubitWithBasis: target, basisMatrix, basisFidelity
  decomposeTwoQubitWithBasis->>TwoQubitBasisDecomposer: create(basisMatrix, basisFidelity)
  TwoQubitBasisDecomposer->>TwoQubitWeylDecomposition: create(basisMatrix, fidelity)
  TwoQubitWeylDecomposition->>MagicBasis: magicBasisTransform
  MagicBasis-->>TwoQubitWeylDecomposition: eigenphases
  TwoQubitWeylDecomposition->>TwoQubitWeylDecomposition: extract Weyl params a,b,c
  TwoQubitWeylDecomposition->>TwoQubitWeylDecomposition: applySpecialization
  TwoQubitWeylDecomposition-->>TwoQubitBasisDecomposer: basis Weyl decomposition

  decomposeTwoQubitWithBasis->>TwoQubitWeylDecomposition: create(target, nullopt)
  TwoQubitWeylDecomposition-->>decomposeTwoQubitWithBasis: target Weyl decomposition

  decomposeTwoQubitWithBasis->>TwoQubitBasisDecomposer: twoQubitDecompose(targetWeyl)
  TwoQubitBasisDecomposer->>TwoQubitBasisDecomposer: compute traces → score 0..3 basis uses
  TwoQubitBasisDecomposer->>TwoQubitBasisDecomposer: select best basis count
  TwoQubitBasisDecomposer->>TwoQubitBasisDecomposer: decomp0/1/2/3 → factors
  TwoQubitBasisDecomposer-->>decomposeTwoQubitWithBasis: TwoQubitNativeDecomposition
  decomposeTwoQubitWithBasis-->>Caller: TwoQubitNativeDecomposition
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • munich-quantum-toolkit/core#1774: Introduces the core Matrix types that this PR extends with rotation and gate-matrix constructors.
  • munich-quantum-toolkit/core#1802: Expands mlir/Dialect/QCO/Utils/Matrix.{h,cpp} with core matrix operations like kron and eigen helpers that the Weyl decomposition infrastructure builds upon.

Suggested labels

feature, c++, MLIR

Suggested reviewers

  • burgholzer

🐇 Two qubits waltz in Weyl's bright chamber,
Magic basis twirls through the numerical ember.
RXX, RYY, RZZ in rotation's dance,
Eigenphases sing as the decomposers prance.
One gate, two gates, or maybe just three—
The bunny ensures unitaries match perfectly! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning PR description is incomplete; missing changelog entries, documentation updates, and some checklist items remain unchecked. Add changelog entries, update documentation, and complete the unchecked checklist items (especially documentation updates and changelog entries marked as incomplete).
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary change: addition of a two-qubit Weyl decomposition library, which aligns with the main objectives of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch decomp/weyl

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mlir/lib/Dialect/QCO/Transforms/Decomposition/Weyl.cpp`:
- Around line 112-118: The hardcoded constants 1.2602066112249388 and
0.22317849046722027 assigned to randA and randB in the if (i == 0) block lack
documentation about their origin and purpose. Add a comment above the if
statement explaining that these are perturbation coefficients specifically
chosen for the first diagonalization attempt, likely derived from a reference
implementation such as Qiskit, to clarify why these specific values are used for
the initial iteration while subsequent iterations use randomly generated values
from the distribution.

In
`@mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_weyl_decomposition.cpp`:
- Around line 189-193: The test fixture classes WeylDecompositionTest and
BasisDecomposerTest are defined at translation unit scope without internal
linkage, triggering a clang-tidy warning. Wrap these class definitions (and any
related helper code that is only used within this test file) in an anonymous
namespace to limit their visibility to internal linkage and resolve the warning.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b251ecb4-6a99-454e-a2b7-dc6a2ce6d4c6

📥 Commits

Reviewing files that changed from the base of the PR and between 6e15803 and 61ed245.

📒 Files selected for processing (9)
  • mlir/include/mlir/Dialect/QCO/Transforms/Decomposition/Euler.h
  • mlir/include/mlir/Dialect/QCO/Transforms/Decomposition/Weyl.h
  • mlir/include/mlir/Dialect/QCO/Utils/Matrix.h
  • mlir/lib/Dialect/QCO/Transforms/Decomposition/Euler.cpp
  • mlir/lib/Dialect/QCO/Transforms/Decomposition/Weyl.cpp
  • mlir/lib/Dialect/QCO/Utils/Matrix.cpp
  • mlir/unittests/Dialect/QCO/Transforms/Decomposition/CMakeLists.txt
  • mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_euler_decomposition.cpp
  • mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_weyl_decomposition.cpp
💤 Files with no reviewable changes (1)
  • mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_euler_decomposition.cpp

Comment thread mlir/lib/Dialect/QCO/Transforms/Decomposition/Weyl.cpp
@simon1hofmann

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@simon1hofmann

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
mlir/lib/Dialect/QCO/Transforms/Decomposition/Weyl.cpp (1)

465-479: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not enforce reconstruction only with assert.

These checks are the public decomposition contract, but they disappear in release builds. If specialization or numeric drift breaks reconstruction, callers receive invalid factors; keep the post-specialization check as a runtime error.

Proposed fix
-  assert((Matrix4x4::kron(decomposition.k1l_, decomposition.k1r_) *
-          decomposition.getCanonicalMatrix() *
-          Matrix4x4::kron(decomposition.k2l_, decomposition.k2r_) *
-          std::exp(Complex{0.0, 1.0} * decomposition.globalPhase_))
-             .isApprox(unitaryMatrix, WEYL_TOLERANCE));
+  const auto restored =
+      Matrix4x4::kron(decomposition.k1l_, decomposition.k1r_) *
+      decomposition.getCanonicalMatrix() *
+      Matrix4x4::kron(decomposition.k2l_, decomposition.k2r_) *
+      std::exp(Complex{0.0, 1.0} * decomposition.globalPhase_);
+  if (!restored.isApprox(unitaryMatrix, WEYL_TOLERANCE)) {
+    llvm::reportFatalInternalError(
+        "TwoQubitWeylDecomposition: reconstructed matrix does not match input");
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Dialect/QCO/Transforms/Decomposition/Weyl.cpp` around lines 465 -
479, The reconstruction checks around decomposition.applySpecialization and
decomposition.finalizeSpecializationPhase are currently enforced only with
assert, so they vanish in release builds. Replace the post-specialization assert
with a runtime validation that always runs and raises an error if the
reconstructed matrix does not match unitaryMatrix within WEYL_TOLERANCE, while
keeping the existing reconstruction expression and using the same decomposition
fields to locate the failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mlir/include/mlir/Dialect/QCO/Transforms/Decomposition/Weyl.h`:
- Around line 132-134: The public method twoQubitDecompose is missing required
Doxygen documentation. Add a Doxygen-style comment block (using /// or /** */)
immediately before the twoQubitDecompose method declaration that documents its
purpose, parameters, return type, and behavior. Specifically document what the
numBasisGateUses parameter represents including its valid range, and clarify
what happens when std::nullopt is passed for this optional parameter.
- Around line 119-130: The TwoQubitBasisDecomposer class currently has an
implicit public default constructor that allows users to instantiate it without
using the create() factory method, bypassing the necessary Weyl decomposition
and template initialization. Add an explicit private default constructor to the
TwoQubitBasisDecomposer class to prevent direct instantiation and ensure all
instances are properly initialized through the create() factory method.

In `@mlir/lib/Dialect/QCO/Transforms/Decomposition/BasisDecomposer.cpp`:
- Around line 82-84: The TwoQubitBasisDecomposer construction path accepts
basisFidelity without validating its domain, which can lead to invalid ranking
behavior when the value is non-finite or out of range. Update
TwoQubitBasisDecomposer::create (and the other affected construction sites
mentioned in the comment) to enforce that basisFidelity is finite and within the
expected bounded interval before storing or using it, and reject invalid inputs
consistently at the point of construction.
- Around line 185-209: The default case in the switch statement handling
different basis decomposition cases calls llvm::reportFatalInternalError and
llvm_unreachable, which crashes the program for invalid bestNbasis values. Since
bestNbasis is derived from externally supplied numBasisGateUses input rather
than an internal invariant, invalid values should be handled gracefully. Replace
the fatal error and unreachable calls in the default case with a return
std::nullopt statement, consistent with the pattern already established in the
function for validation failures on line 187-189.

In `@mlir/lib/Dialect/QCO/Transforms/Decomposition/Weyl.cpp`:
- Around line 449-451: The public entry point TwoQubitWeylDecomposition::create
currently forwards fidelity directly into specialization selection, which lets
negative or non-finite values skew candidate acceptance and produce invalid
decompositions. Add validation at the start of create to reject any fidelity
that is not finite or is outside the [0, 1] range before any specialization is
chosen, and keep the rest of the selection logic unchanged.

---

Outside diff comments:
In `@mlir/lib/Dialect/QCO/Transforms/Decomposition/Weyl.cpp`:
- Around line 465-479: The reconstruction checks around
decomposition.applySpecialization and decomposition.finalizeSpecializationPhase
are currently enforced only with assert, so they vanish in release builds.
Replace the post-specialization assert with a runtime validation that always
runs and raises an error if the reconstructed matrix does not match
unitaryMatrix within WEYL_TOLERANCE, while keeping the existing reconstruction
expression and using the same decomposition fields to locate the failure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1c0f9ce6-a169-4d2d-882e-da6288121059

📥 Commits

Reviewing files that changed from the base of the PR and between eaa1097 and 341cfc8.

📒 Files selected for processing (6)
  • mlir/include/mlir/Dialect/QCO/Transforms/Decomposition/Weyl.h
  • mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt
  • mlir/lib/Dialect/QCO/Transforms/Decomposition/BasisDecomposer.cpp
  • mlir/lib/Dialect/QCO/Transforms/Decomposition/Weyl.cpp
  • mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_euler_decomposition.cpp
  • mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_weyl_decomposition.cpp

Comment thread mlir/include/mlir/Dialect/QCO/Transforms/Decomposition/Weyl.h
Comment thread mlir/include/mlir/Dialect/QCO/Transforms/Decomposition/Weyl.h
Comment thread mlir/lib/Dialect/QCO/Transforms/Decomposition/BasisDecomposer.cpp
Comment thread mlir/lib/Dialect/QCO/Transforms/Decomposition/BasisDecomposer.cpp
Comment thread mlir/lib/Dialect/QCO/Transforms/Decomposition/Weyl.cpp
@simon1hofmann

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@simon1hofmann simon1hofmann marked this pull request as ready for review June 23, 2026 14:47
@simon1hofmann simon1hofmann requested a review from burgholzer June 23, 2026 15:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant