✨ Add Hadamard Lifting#1605
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Signed-off-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com>
# Conflicts: # CHANGELOG.md
Signed-off-by: Lukas Burgholzer <burgholzer@me.com>
burgholzer
left a comment
There was a problem hiding this comment.
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.
| ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───────┐ | ||
| ─┤ Y ├─┤ H ├─ => ─┤ H ├─┤ Y ├─┤ G(pi) ├─ | ||
| └───┘ └───┘ └───┘ └───┘ └───────┘ |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
You are right, I changed the notation
There was a problem hiding this comment.
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
📒 Files selected for processing (4)
CHANGELOG.mdmlir/include/mlir/Dialect/QCO/Transforms/Passes.tdmlir/lib/Dialect/QCO/Transforms/Optimizations/HadamardLifting.cppmlir/tools/mqt-cc/mqt-cc.cpp
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:
zfollowed byhtohfollowed byx.xfollowed byhtohfollowed byz.yfollowed byhtohfollowed byy.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
zgate: 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:

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
If PR contains AI-assisted content:
Assisted-by: [Model Name] via [Tool Name]footer.