Skip to content

✨ Add Hadamard Lifting#1605

Open
LiRem101 wants to merge 75 commits intomunich-quantum-toolkit:mainfrom
LiRem101:mlir/hadamard-lifting
Open

✨ Add Hadamard Lifting#1605
LiRem101 wants to merge 75 commits intomunich-quantum-toolkit:mainfrom
LiRem101:mlir/hadamard-lifting

Conversation

@LiRem101
Copy link
Copy Markdown
Collaborator

@LiRem101 LiRem101 commented Apr 2, 2026

Description

This PR introduces the optimization pass Hadamard lifting.

The optimization pass lifts Hadamard gates in front of Pauli gates in order to improve the results bu measurement-lifting. The implemented liftings are:
z followed by h to h followed by x.
x followed by h to h followed by z.
y followed by h to h followed by y.

The transformation are also applied on controlled gates, provided both participating gates have the same target qubit and the same control qubits.
An exception is the z gate: The transformation exchanges the target and control qubits of a controlled z gate, if it is followed by a controlled Hadamard gate it can be swapped with afterwards.

Additionally, a transformation routine is implemented which swaps the target and control qubits of a CNOT operation (by adding four Hadamard gates before/after the target/control qubits). This is applied if a Hadamard gate is applied to the CNOT target, and afterwards the qubit is measured. The whole transformation routine is:
image

After the transformation routine, measurement-lifting can be used to lift the measurement in front of the control and to remove a costly two-qubit gate.

The paper in which the routine is published is currently under review.

Microsoft Copilot has been occasionally used to draft first versions of code, but was not integrated into an IDE.

Fixes #1386

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.

@LiRem101 LiRem101 added the MLIR Anything related to MLIR label Apr 2, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@mergify mergify Bot removed the conflict label Apr 17, 2026
@mergify mergify Bot added the conflict label Apr 21, 2026
@mergify mergify Bot removed the conflict label Apr 22, 2026
@LiRem101 LiRem101 requested a review from burgholzer April 22, 2026 08:08
denialhaag and others added 5 commits April 29, 2026 17:07
Signed-off-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com>
Signed-off-by: Lukas Burgholzer <burgholzer@me.com>
@burgholzer burgholzer marked this pull request as ready for review May 6, 2026 14:54
Copy link
Copy Markdown
Member

@burgholzer burgholzer left a comment

Choose a reason for hiding this comment

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

As discussed already offline, this LGTM now.
I pushed a couple of changes to simplify the pass implementation.
There's also one more open comment about an illustration in the documentation. That should be pretty quick to address though.
Potentially, CodeRabbit will also have a handful of comments since I switched the PR back to ready-for-review.
Afterwards, this should be good to go.

Comment on lines +134 to +136
┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───────┐
─┤ Y ├─┤ H ├─ => ─┤ H ├─┤ Y ├─┤ G(pi) ├─
└───┘ └───┘ └───┘ └───┘ └───────┘
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.

The notation for the global phase here is slightly misleading as it looks like the phase would apply to a qubit; but it is a global phase.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

You are right, I changed the notation

Comment thread mlir/include/mlir/Compiler/CompilerPipeline.h
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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/Optimizations/HadamardLifting.cpp`:
- Around line 170-182: The control-selection loop in HadamardLifting.cpp assumes
there is at least one control and that getOutputControl(i).getUsers() is
non-empty; add a guard at the start to return failure if
cnotGate.getNumControls() == 0, and inside the for-loop check that
cnotGate.getOutputControl(i).getUsers() is not empty (e.g., begin() != end() or
users.empty() check) before dereferencing; if users is empty treat that control
as invalid (skip or return failure appropriately) and continue scanning, and if
no suitable non-measure control is found then return failure—ensure controlIndex
is only used after a valid control is selected (references:
cnotGate.getNumControls(), cnotGate.getOutputControl(i).getUsers(),
cnotGate.getInputControl(0)/getOutputControl(0)).
- Around line 105-112: The code dereferences op->getUsers().begin() without
checking for empty users, which can crash; in matchAndRewrite guard that the
result of op has at least one user before calling op->getUsers().begin() (e.g.,
check op->getUsers().empty() or use auto it = op->getUsers().begin(); if
(it==op->getUsers().end()) return failure()). Then use dyn_cast<HOp>(*it) as
before and proceed to call swapPauliWithHadamard(op, hadamardGate, rewriter)
only when hadamardGate is valid; ensure this check is applied in the same
function that currently contains the dereference (the block using dyn_cast<HOp>,
hadamardGate, and swapPauliWithHadamard) to avoid UB when matchAndRewrite runs
on partial/malformed IR.
🪄 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: 47118222-6a71-4811-a973-90f428290558

📥 Commits

Reviewing files that changed from the base of the PR and between 9afaacd and 0fcf1e6.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • mlir/include/mlir/Dialect/QCO/Transforms/Passes.td
  • mlir/lib/Dialect/QCO/Transforms/Optimizations/HadamardLifting.cpp
  • mlir/tools/mqt-cc/mqt-cc.cpp

Comment thread mlir/lib/Dialect/QCO/Transforms/Optimizations/HadamardLifting.cpp
Comment thread mlir/lib/Dialect/QCO/Transforms/Optimizations/HadamardLifting.cpp
@LiRem101 LiRem101 requested a review from burgholzer May 8, 2026 07:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request MLIR Anything related to MLIR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ MLIR - Add Hadamard lifting to support measurement lifting

3 participants