Skip to content

✨ Add support for multi-controlled gates to ZX package#1380

Merged
denialhaag merged 82 commits intomunich-quantum-toolkit:mainfrom
keefehuang:multi_control
Apr 20, 2026
Merged

✨ Add support for multi-controlled gates to ZX package#1380
denialhaag merged 82 commits intomunich-quantum-toolkit:mainfrom
keefehuang:multi_control

Conversation

@keefehuang
Copy link
Copy Markdown
Contributor

@keefehuang keefehuang commented Dec 8, 2025

Description

Add CRZ, MCX, MCZ, MCRZ functionality to ZX package.
Add convenience addCCZ function as it avoids one additional Hardamard edge (optimizes it away).

Fixes #1357

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.

Keefe Huang and others added 2 commits December 8, 2025 16:40
Add CRZ, MCX, MCZ, MCRZ functionality to ZX package.
@codecov
Copy link
Copy Markdown

codecov Bot commented Dec 8, 2025

Codecov Report

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

Files with missing lines Patch % Lines
src/zx/FunctionalityConstruction.cpp 89.2% 24 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 8, 2025

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: d3660d93-4993-4fe6-b2cd-18279ec75af4

📥 Commits

Reviewing files that changed from the base of the PR and between 8031ec0 and 2966d29.

📒 Files selected for processing (1)
  • test/zx/test_zx_functionality.cpp

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Extended ZX package support for multi-controlled gates, including controlled and multi-controlled variants of phase, rotation (RX/RZ), swap, and logical operations. Enables transformation of more complex quantum circuits to and from ZX diagrams for optimization and analysis.

Walkthrough

Added protected helpers and implementations to construct controlled and multi-controlled ZX subgraphs (MCX/MCZ/MCRX/MCRZ/MCSWAP/MCRZZ/etc.), updated parser/transformability logic to dispatch these gates, expanded tests to assert equivalence for many multi-control variants, and updated the changelog. (≤50 words)

Changes

Cohort / File(s) Summary
Header Declaration
include/mqt-core/zx/FunctionalityConstruction.hpp
Added protected static declarations for new controlled/multi-controlled helpers (addMcphase, addMcswap, addMcrzz, addCcz, addCrx, addMcrx, addCrz, addMcrz, addMcx, addMcz) and renamed addSwap first parameter to target1.
Implementation
src/zx/FunctionalityConstruction.cpp
Implemented multi-control decompositions and helpers (recursive/divide-and-conquer MCX, MCRZ/MCRX, MCRZZ, MCSWAP, CCZ, CRZ/CRX), updated addSwap param name and indexing, extended parseOp dispatch and transformableToZX rules for control/target counts, added unconvertedPhase passthrough, and standardized exception messages.
Tests
test/zx/test_zx_functionality.cpp
Added checkEquivalence helper; refactored many tests to use it; added equivalence tests for CRZ/CCZ/MCX/MCZ/MCRZ/MCRX/MCSWAP/MCRzz/etc.; removed obsolete UnsupportedMultiControl expectation and added targeted unsupported-control tests.
Changelog
CHANGELOG.md
Added Unreleased entry for multi-controlled gates support (PR #1380) and updated PR/contributor references.

Sequence Diagram(s)

sequenceDiagram
    participant Parser as parseOp
    participant FC as FunctionalityConstruction
    participant Anc as AncillaPool
    participant ZX as ZXDiagram

    Parser->>FC: receive qc::Operation (e.g., MCX / MCRZ / MCSWAP)
    FC->>FC: select decomposition by control/target counts
    alt needs ancilla
        FC->>Anc: request ancilla
        Anc-->>FC: provide ancilla
    end
    FC->>ZX: construct ZX subgraph via addMcx/addMcrz/addMcswap/addCcz/...
    ZX-->>Parser: ZXDiagram updated (subgraph appended)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • #1429 — Implements multi-controlled ZX construction routines and tests: this PR adds matching MC helper routines and test coverage.
  • #1650 — Requests an O(N) MCX construction: this PR implements a recursive (exponential) addMcx, so it directly relates to the performance/algorithmic improvement requested.

Suggested labels

enhancement, c++

Suggested reviewers

  • burgholzer

Poem

🐇 I hop through wires and phases bright,
I stitch MCX and CCZ by night,
Ancilla crumbs beneath my paws,
Spiders hum and weave the laws,
Hooray — the circuits gleam with light.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the primary change: adding support for multi-controlled gates to the ZX package.
Description check ✅ Passed The description lists specific added functionality (CRZ, MCX, MCZ, MCRZ, addCCZ), references the fixed issue (#1357), and has a reasonably complete checklist with most items marked as done.
Linked Issues check ✅ Passed The PR implements all requirements from issue #1357: multi-controlled X/Z support, CRZ/MCRZ implementations, proper ZX construction integration, and passing tests demonstrating correctness.
Out of Scope Changes check ✅ Passed All changes are directly scoped to multi-controlled gate support: header interface updates, implementations, tests, and changelog entries align with the stated objective.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

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: 4

Caution

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

⚠️ Outside diff range comments (1)
src/zx/FunctionalityConstruction.cpp (1)

872-896: Remove unreachable return statement.

The transformability checks correctly reflect the expanded multi-control support for RZ operations. However, line 896 contains an unreachable return false; statement since the preceding conditions exhaust all cases.

Apply this diff to remove the dead code:

     case qc::OpType::RZ:
       return true;
     default:
       return false;
     }
-    return false;
   }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdb2073 and 0ae2b61.

📒 Files selected for processing (3)
  • include/mqt-core/zx/FunctionalityConstruction.hpp (1 hunks)
  • src/zx/FunctionalityConstruction.cpp (5 hunks)
  • test/zx/test_zx_functionality.cpp (1 hunks)
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.

Applied to files:

  • test/zx/test_zx_functionality.cpp
  • include/mqt-core/zx/FunctionalityConstruction.hpp
  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-12-08T14:55:43.880Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-100
Timestamp: 2025-12-08T14:55:43.880Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), GPhaseOp is a zero-target operation (global phase). When a CtrlOp wraps a GPhaseOp, it only has control qubits and no targets. The CtrlInlineGPhase canonicalization pattern correctly produces outputs only for the positive controls, not targets.

Applied to files:

  • include/mqt-core/zx/FunctionalityConstruction.hpp
  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-10-09T13:13:51.224Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:171-180
Timestamp: 2025-10-09T13:13:51.224Z
Learning: In MQT Core MLIR, UnitaryInterface operations guarantee 1-1 correspondence between input and output qubits in the same order. When cloning or modifying unitary operations (e.g., removing controls), this correspondence is maintained by construction, so yielding getAllInQubits() in else-branches matches the result types from the operation's outputs.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-12-08T12:44:05.874Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:60-70
Timestamp: 2025-12-08T12:44:05.874Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The RemoveTrivialCtrl pattern correctly only checks getNumPosControls() when determining if a CtrlOp should be removed.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
🧬 Code graph analysis (2)
test/zx/test_zx_functionality.cpp (2)
include/mqt-core/zx/FunctionalityConstruction.hpp (2)
  • qc (39-39)
  • qc (50-50)
src/zx/FunctionalityConstruction.cpp (2)
  • buildFunctionality (787-804)
  • buildFunctionality (787-788)
src/zx/FunctionalityConstruction.cpp (2)
src/zx/Rational.cpp (1)
  • PiRational (21-45)
src/ir/operations/OpType.cpp (2)
  • toString (21-35)
  • toString (21-21)
🪛 GitHub Check: 🇨‌ Lint / 🚨 Lint
src/zx/FunctionalityConstruction.cpp

[warning] 388-388: src/zx/FunctionalityConstruction.cpp:388:13 [cppcoreguidelines-init-variables]
variable 'anc' is not initialized


[warning] 384-384: src/zx/FunctionalityConstruction.cpp:384:50 [clang-diagnostic-sign-conversion]
implicit conversion changes signedness: 'size_t' (aka 'unsigned long') to 'difference_type' (aka 'long')


[warning] 384-384: src/zx/FunctionalityConstruction.cpp:384:50 [bugprone-narrowing-conversions]
narrowing conversion from 'size_t' (aka 'unsigned long') to signed type 'difference_type' (aka 'long') is implementation-defined


[warning] 383-383: src/zx/FunctionalityConstruction.cpp:383:67 [clang-diagnostic-sign-conversion]
implicit conversion changes signedness: 'size_t' (aka 'unsigned long') to 'difference_type' (aka 'long')


[warning] 383-383: src/zx/FunctionalityConstruction.cpp:383:67 [bugprone-narrowing-conversions]
narrowing conversion from 'size_t' (aka 'unsigned long') to signed type 'difference_type' (aka 'long') is implementation-defined


[warning] 383-383: src/zx/FunctionalityConstruction.cpp:383:5 [misc-const-correctness]
variable 'first' of type 'std::vector' (aka 'vector') can be declared 'const'


[warning] 382-382: src/zx/FunctionalityConstruction.cpp:382:5 [misc-const-correctness]
variable 'half' of type 'size_t' (aka 'unsigned long') can be declared 'const'


[warning] 356-356: src/zx/FunctionalityConstruction.cpp:356:5 [misc-const-correctness]
variable 'nextControl' of type 'Qubit' (aka 'int') can be declared 'const'


[warning] 339-339: src/zx/FunctionalityConstruction.cpp:339:3 [readability-suspicious-call-argument]
2nd argument 'target' (passed to 'ctrl') looks like it might be swapped with the 3rd, 'control' (passed to 'target')


[warning] 336-336: src/zx/FunctionalityConstruction.cpp:336:3 [readability-suspicious-call-argument]
2nd argument 'target' (passed to 'ctrl') looks like it might be swapped with the 3rd, 'control' (passed to 'target')

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
🔇 Additional comments (7)
include/mqt-core/zx/FunctionalityConstruction.hpp (1)

116-127: LGTM!

The new method declarations for multi-control gates are well-structured and follow the existing API patterns. The signatures are clear and properly typed.

test/zx/test_zx_functionality.cpp (1)

232-359: Excellent test coverage for multi-control operations.

The new tests thoroughly validate the multi-control functionality with:

  • CRZ decomposition equivalence
  • Multi-control X with varying control counts (0, 1, 3)
  • Multi-control RZ with phase cancellation
  • Edge cases for boundary conditions

All tests properly verify identity preservation, zero global phase, and connectivity after reduction.

src/zx/FunctionalityConstruction.cpp (5)

310-330: LGTM!

The addCcz implementation correctly decomposes the CCZ gate using the standard pattern with CNOTs, Z-spiders with appropriate phases, and Hadamard edges. The decomposition is consistent with ZX-calculus conventions.


332-340: LGTM!

The addCrz implementation uses the correct decomposition for controlled-RZ gates. The argument order in addCnot(diag, target, control, qubits) is intentional and follows the standard CRZ decomposition pattern where the roles are reversed.

Note: The static analysis warning about swapped arguments at lines 336 and 339 is a false positive.


680-682: LGTM!

The addition of the RZ case for single-control, single-target operations correctly routes to addCrz, expanding the supported gate set.


723-740: LGTM!

The updates to the two-control branch correctly:

  • Build the controls vector from the operation
  • Route CCZ operations to addCcz
  • Route multi-controlled RZ operations to addMcrz
  • Update error messages to reflect multi-control support

741-762: LGTM!

The new multi-control branch properly handles operations with arbitrary numbers of controls and a single target. The routing to addMcx, addMcz, and addMcrz is correct and extends the ZX functionality to support general multi-controlled gates.

Comment thread test/zx/test_zx_functionality.cpp Outdated
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: 5

Caution

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

⚠️ Outside diff range comments (1)
src/zx/FunctionalityConstruction.cpp (1)

872-896: LGTM: Extended transformableToZX for controlled RZ.

The updates correctly mark controlled-RZ operations as transformable in all control-count branches (1, 2, and N controls).

Minor cleanup - Line 896 has an unreachable return statement:

   } else if (op->getNtargets() == 1) {
     switch (op->getType()) {
     case qc::OpType::X:
     case qc::OpType::Z:
     case qc::OpType::RZ:
       return true;
     default:
       return false;
     }
-    return false;
   }
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdb2073 and 0ae2b61.

📒 Files selected for processing (3)
  • include/mqt-core/zx/FunctionalityConstruction.hpp (1 hunks)
  • src/zx/FunctionalityConstruction.cpp (5 hunks)
  • test/zx/test_zx_functionality.cpp (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-12-08T14:55:43.880Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-100
Timestamp: 2025-12-08T14:55:43.880Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), GPhaseOp is a zero-target operation (global phase). When a CtrlOp wraps a GPhaseOp, it only has control qubits and no targets. The CtrlInlineGPhase canonicalization pattern correctly produces outputs only for the positive controls, not targets.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
  • include/mqt-core/zx/FunctionalityConstruction.hpp
📚 Learning: 2025-10-09T13:13:51.224Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:171-180
Timestamp: 2025-10-09T13:13:51.224Z
Learning: In MQT Core MLIR, UnitaryInterface operations guarantee 1-1 correspondence between input and output qubits in the same order. When cloning or modifying unitary operations (e.g., removing controls), this correspondence is maintained by construction, so yielding getAllInQubits() in else-branches matches the result types from the operation's outputs.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-12-08T12:44:05.874Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:60-70
Timestamp: 2025-12-08T12:44:05.874Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The RemoveTrivialCtrl pattern correctly only checks getNumPosControls() when determining if a CtrlOp should be removed.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
🧬 Code graph analysis (1)
src/zx/FunctionalityConstruction.cpp (1)
include/mqt-core/zx/FunctionalityConstruction.hpp (18)
  • diag (65-68)
  • diag (69-72)
  • diag (74-76)
  • diag (77-78)
  • diag (80-82)
  • diag (83-85)
  • diag (86-87)
  • diag (88-89)
  • diag (91-93)
  • diag (95-97)
  • diag (99-101)
  • diag (102-103)
  • diag (105-108)
  • diag (110-113)
  • diag (114-115)
  • diag (116-117)
  • op (60-60)
  • op (135-135)
🪛 GitHub Check: 🇨‌ Lint / 🚨 Lint
src/zx/FunctionalityConstruction.cpp

[warning] 388-388: src/zx/FunctionalityConstruction.cpp:388:13 [cppcoreguidelines-init-variables]
variable 'anc' is not initialized


[warning] 384-384: src/zx/FunctionalityConstruction.cpp:384:50 [clang-diagnostic-sign-conversion]
implicit conversion changes signedness: 'size_t' (aka 'unsigned long') to 'difference_type' (aka 'long')


[warning] 384-384: src/zx/FunctionalityConstruction.cpp:384:50 [bugprone-narrowing-conversions]
narrowing conversion from 'size_t' (aka 'unsigned long') to signed type 'difference_type' (aka 'long') is implementation-defined


[warning] 383-383: src/zx/FunctionalityConstruction.cpp:383:67 [clang-diagnostic-sign-conversion]
implicit conversion changes signedness: 'size_t' (aka 'unsigned long') to 'difference_type' (aka 'long')


[warning] 383-383: src/zx/FunctionalityConstruction.cpp:383:67 [bugprone-narrowing-conversions]
narrowing conversion from 'size_t' (aka 'unsigned long') to signed type 'difference_type' (aka 'long') is implementation-defined


[warning] 383-383: src/zx/FunctionalityConstruction.cpp:383:5 [misc-const-correctness]
variable 'first' of type 'std::vector' (aka 'vector') can be declared 'const'


[warning] 382-382: src/zx/FunctionalityConstruction.cpp:382:5 [misc-const-correctness]
variable 'half' of type 'size_t' (aka 'unsigned long') can be declared 'const'


[warning] 356-356: src/zx/FunctionalityConstruction.cpp:356:5 [misc-const-correctness]
variable 'nextControl' of type 'Qubit' (aka 'int') can be declared 'const'


[warning] 339-339: src/zx/FunctionalityConstruction.cpp:339:3 [readability-suspicious-call-argument]
2nd argument 'target' (passed to 'ctrl') looks like it might be swapped with the 3rd, 'control' (passed to 'target')


[warning] 336-336: src/zx/FunctionalityConstruction.cpp:336:3 [readability-suspicious-call-argument]
2nd argument 'target' (passed to 'ctrl') looks like it might be swapped with the 3rd, 'control' (passed to 'target')

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
🔇 Additional comments (7)
test/zx/test_zx_functionality.cpp (1)

232-359: Excellent test coverage for multi-control operations.

The new tests comprehensively validate CRZ, MCX, and MCRZ operations by constructing equivalent circuits and verifying they reduce to identity. The test pattern is consistent with existing tests in the file.

src/zx/FunctionalityConstruction.cpp (5)

310-330: CCZ implementation looks correct.

The addCcz function implements a double-controlled Z gate by adapting the CCX decomposition to work in the Z-basis.


332-340: CRZ implementation is correct.

The addCrz function correctly implements controlled-RZ using the standard decomposition. The CNOT operations with target as control are intentional and correct for this decomposition, despite the static analysis warning.


680-682: LGTM: Single-controlled RZ support.

Correctly handles controlled-RZ gates by delegating to the new addCrz function.


723-740: LGTM: Double-controlled gate support.

The updated logic correctly handles CCZ and multi-controlled RZ operations with 2 controls by building a control vector and dispatching to the appropriate implementation functions.


741-762: LGTM: Multi-control gate support.

The new branch properly handles gates with arbitrary numbers of controls, supporting MCX, MCZ, and MCRZ operations through the newly implemented functions.

include/mqt-core/zx/FunctionalityConstruction.hpp (1)

116-127: LGTM: Method declarations are well-structured.

The new method declarations are consistent with existing patterns in the class and correctly match their implementations. Parameter types and const-correctness are appropriate.

Comment thread src/zx/FunctionalityConstruction.cpp Outdated
Comment thread src/zx/FunctionalityConstruction.cpp Outdated
Comment thread test/zx/test_zx_functionality.cpp Outdated
Apply const to constant variables.
Replace for loop with std::ranges::find_if implementation.
Utilize static_cast to explicitly state conversion from unsigned to signed int.
Shift return false of `transformableToZX` to be reachable again.
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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ae2b61 and f8c4503.

📒 Files selected for processing (1)
  • src/zx/FunctionalityConstruction.cpp (5 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-12-08T12:44:05.874Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:60-70
Timestamp: 2025-12-08T12:44:05.874Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The RemoveTrivialCtrl pattern correctly only checks getNumPosControls() when determining if a CtrlOp should be removed.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-11-20T18:19:31.857Z
Learnt from: lsschmid
Repo: munich-quantum-toolkit/qmap PR: 832
File: src/hybridmap/HybridNeutralAtomMapper.cpp:1679-1715
Timestamp: 2025-11-20T18:19:31.857Z
Learning: In src/hybridmap/HybridNeutralAtomMapper.cpp, the compareSwapAndBridge and compareShuttlingAndFlyingAncilla methods intentionally use division by zero to produce infinity when distance reductions are zero or negative. This infinity value marks that option as highly unfavorable in cost comparisons, which is the intended behavior rather than a bug.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-11-27T08:52:06.428Z
Learnt from: lsschmid
Repo: munich-quantum-toolkit/qmap PR: 832
File: src/hybridmap/HybridNeutralAtomMapper.cpp:1379-1383
Timestamp: 2025-11-27T08:52:06.428Z
Learning: In src/hybridmap/HybridNeutralAtomMapper.cpp, the getBestMovePos function throws std::runtime_error when no move position is found (finalBestPos.coords is empty). This behavior is acceptable for current use cases since tight architectures are not a concern. A future improvement would be to fall back to SWAP-based strategies instead of throwing.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-10-09T13:13:51.224Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:171-180
Timestamp: 2025-10-09T13:13:51.224Z
Learning: In MQT Core MLIR, UnitaryInterface operations guarantee 1-1 correspondence between input and output qubits in the same order. When cloning or modifying unitary operations (e.g., removing controls), this correspondence is maintained by construction, so yielding getAllInQubits() in else-branches matches the result types from the operation's outputs.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-12-08T14:55:43.880Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-100
Timestamp: 2025-12-08T14:55:43.880Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), GPhaseOp is a zero-target operation (global phase). When a CtrlOp wraps a GPhaseOp, it only has control qubits and no targets. The CtrlInlineGPhase canonicalization pattern correctly produces outputs only for the positive controls, not targets.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
🪛 GitHub Check: 🇨‌ Lint / 🚨 Lint
src/zx/FunctionalityConstruction.cpp

[warning] 395-395: src/zx/FunctionalityConstruction.cpp:395:9 [readability-else-after-return]
do not use 'else' after 'throw'


[warning] 339-339: src/zx/FunctionalityConstruction.cpp:339:3 [readability-suspicious-call-argument]
2nd argument 'target' (passed to 'ctrl') looks like it might be swapped with the 3rd, 'control' (passed to 'target')


[warning] 336-336: src/zx/FunctionalityConstruction.cpp:336:3 [readability-suspicious-call-argument]
2nd argument 'target' (passed to 'ctrl') looks like it might be swapped with the 3rd, 'control' (passed to 'target')

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
🔇 Additional comments (9)
src/zx/FunctionalityConstruction.cpp (9)

310-330: LGTM!

The addCcz implementation correctly decomposes the CCZ gate using standard T-gate decomposition. The Hadamard edges are appropriately placed at the end (compared to addCcx which has them at start/end) since CCZ = H·CCX·H on the target.


342-364: Declare nextControl as const.

The variable nextControl is not modified after initialization and should be declared const.

-    const Qubit nextControl = controls.back();
+    const Qubit nextControl = controls.back();

Note: This appears to already be addressed in the current code (line 356 shows const). If the static analysis tool is still flagging this, the issue may be resolved.


424-449: LGTM!

The addMcz function correctly implements multi-controlled Z using recursive decomposition with proper base cases for 0, 1, and 2 controls. The nextControl variable is already declared const.


682-684: LGTM!

Correctly routes single-controlled RZ operations to the new addCrz function.


725-742: LGTM!

The two-control parsing correctly handles CCZ and MCRZ operations. The controls vector is appropriately constructed for the MCRZ case while maintaining backward compatibility with the existing CCX/CCZ signatures.


743-760: LGTM!

The multi-control parsing branch correctly handles MCX, MCZ (via H·MCX·H decomposition), and MCRZ operations for gates with more than 2 controls. The Hadamard sandwiching for MCZ is the correct identity.


874-897: LGTM!

The transformableToZX function correctly extends support to recognize RZ operations in single-control, two-control, and multi-control contexts, consistent with the new parsing implementations.


416-419: Declare lastControl as const.

The variable lastControl is not modified after initialization.

-      const Qubit lastControl = controls.back();
+      const Qubit lastControl = controls.back();

Note: Line 416 already shows const in the provided code, so this may already be addressed.


382-384: Consider declaring first as const.

The first vector is not modified after construction and could be declared const for better const-correctness. The second vector cannot be const since it's modified on line 399.

     const auto half = static_cast<std::ptrdiff_t>((controls.size() + 1) / 2);
-    const std::vector<Qubit> first(controls.begin(), controls.begin() + half);
+    const std::vector<Qubit> first(controls.begin(), controls.begin() + half);
     std::vector<Qubit> second(controls.begin() + half, controls.end());

Note: Line 383 already shows const in the provided code, so this appears to be addressed.

Comment thread src/zx/FunctionalityConstruction.cpp
Comment thread src/zx/FunctionalityConstruction.cpp Outdated
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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8c4503 and 3142193.

📒 Files selected for processing (1)
  • test/zx/test_zx_functionality.cpp (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
📚 Learning: 2025-12-08T12:44:05.874Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:60-70
Timestamp: 2025-12-08T12:44:05.874Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The RemoveTrivialCtrl pattern correctly only checks getNumPosControls() when determining if a CtrlOp should be removed.

Applied to files:

  • test/zx/test_zx_functionality.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.

Applied to files:

  • test/zx/test_zx_functionality.cpp
🧬 Code graph analysis (1)
test/zx/test_zx_functionality.cpp (2)
src/zx/FunctionalityConstruction.cpp (2)
  • buildFunctionality (789-806)
  • buildFunctionality (789-790)
test/zx/test_simplify.cpp (1)
  • fullReduce (602-602)
🪛 Cppcheck (2.18.0)
test/zx/test_zx_functionality.cpp

[error] 269-269: syntax error

(syntaxError)

🔇 Additional comments (4)
test/zx/test_zx_functionality.cpp (4)

222-245: CRZ decomposition test is mathematically sound

The CRZ(π/2) vs CX–RZ(-π/4)–CX–RZ(π/4) equivalence is correct (exact, no global phase), and the functionality + reduction checks are appropriate for validating the new CRZ support.


247-268: MultiCZ equivalence to H–MCX–H is correct

Using qc.mcz({1, 2}, 0) and comparing against h(0); mcx({1, 2}, 0); h(0); is the standard construction for MCZ and should be functionally identical with zero global phase; the identity and phase checks nicely exercise the new MCZ path.


269-295: CCZ vs H–MCX–H construction is appropriate

Importing ccz from OpenQASM and comparing it with h(0); mcx({1, 2}, 0); h(0); is a valid way to test CCZ support; this pattern realizes a three-qubit phase flip on |111⟩ with no extra global phase, so the identity and zero-global-phase expectations are well chosen.


296-383: Good coverage of MCX semantics across arities and decompositions

The new MCX tests collectively cover:

  • general MCX construction and inversion (MultiControlX);
  • ancilla-based decomposition on a larger register (MultiControlXLarger);
  • the degenerate 0-control case behaving as plain X (MultiControlX0);
  • the 1-control case matching CX (MultiControlX1).

These are well-structured functionality checks for the new multi-control-X support.

Comment thread test/zx/test_zx_functionality.cpp Outdated
Comment thread test/zx/test_zx_functionality.cpp Outdated
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: 1

♻️ Duplicate comments (1)
test/zx/test_zx_functionality.cpp (1)

385-398: Consider strengthening connectivity assertions.

The MCRZ test currently only checks connectivity for qubit 0. Following the pattern in complexCircuit (lines 165-167), consider verifying connectivity for all involved qubits (0, 1, and 2) to catch potential wiring issues.

Apply this diff to strengthen the test:

   EXPECT_TRUE(d.isIdentity());
   EXPECT_TRUE(d.globalPhaseIsZero());
   EXPECT_TRUE(d.connected(d.getInput(0), d.getOutput(0)));
+  EXPECT_TRUE(d.connected(d.getInput(1), d.getOutput(1)));
+  EXPECT_TRUE(d.connected(d.getInput(2), d.getOutput(2)));
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3142193 and 1d008c6.

📒 Files selected for processing (2)
  • src/zx/FunctionalityConstruction.cpp (5 hunks)
  • test/zx/test_zx_functionality.cpp (1 hunks)
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
📚 Learning: 2025-12-08T12:44:05.874Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:60-70
Timestamp: 2025-12-08T12:44:05.874Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The RemoveTrivialCtrl pattern correctly only checks getNumPosControls() when determining if a CtrlOp should be removed.

Applied to files:

  • test/zx/test_zx_functionality.cpp
  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.

Applied to files:

  • test/zx/test_zx_functionality.cpp
📚 Learning: 2025-12-04T06:59:40.314Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1301
File: mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp:84-85
Timestamp: 2025-12-04T06:59:40.314Z
Learning: In the MQTOpt MLIR routing passes (NaiveRoutingPassSC, AStarRoutingPassSC), the input IR is guaranteed to contain only 1-qubit and 2-qubit gates. All 3+-qubit gates must be decomposed before routing; otherwise the input IR is invalid. This invariant allows skipTwoQubitBlock in LayeredUnit.cpp to safely assert wires.size() == 2.

Applied to files:

  • test/zx/test_zx_functionality.cpp
📚 Learning: 2025-11-01T15:57:31.153Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1283
File: src/qir/runtime/QIR.cpp:196-201
Timestamp: 2025-11-01T15:57:31.153Z
Learning: In the QIR runtime (src/qir/runtime/QIR.cpp), the PRX gate (__quantum__qis__prx__body) is an alias for the R gate (Phased X-Rotation) and should call runtime.apply<qc::R>(theta, phi, qubit), not runtime.apply<qc::RX>() which is a single-parameter rotation gate.

Applied to files:

  • test/zx/test_zx_functionality.cpp
  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.

Applied to files:

  • test/zx/test_zx_functionality.cpp
  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-12-08T23:16:16.276Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-101
Timestamp: 2025-12-08T23:16:16.276Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The CtrlInlineGPhase canonicalization pattern correctly only checks getNumPosControls() and processes only positive controls when inlining a GPhaseOp.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-11-20T18:19:31.857Z
Learnt from: lsschmid
Repo: munich-quantum-toolkit/qmap PR: 832
File: src/hybridmap/HybridNeutralAtomMapper.cpp:1679-1715
Timestamp: 2025-11-20T18:19:31.857Z
Learning: In src/hybridmap/HybridNeutralAtomMapper.cpp, the compareSwapAndBridge and compareShuttlingAndFlyingAncilla methods intentionally use division by zero to produce infinity when distance reductions are zero or negative. This infinity value marks that option as highly unfavorable in cost comparisons, which is the intended behavior rather than a bug.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-12-08T14:55:43.880Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-100
Timestamp: 2025-12-08T14:55:43.880Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), GPhaseOp is a zero-target operation (global phase). When a CtrlOp wraps a GPhaseOp, it only has control qubits and no targets. The CtrlInlineGPhase canonicalization pattern correctly produces outputs only for the positive controls, not targets.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-10-09T13:13:51.224Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:171-180
Timestamp: 2025-10-09T13:13:51.224Z
Learning: In MQT Core MLIR, UnitaryInterface operations guarantee 1-1 correspondence between input and output qubits in the same order. When cloning or modifying unitary operations (e.g., removing controls), this correspondence is maintained by construction, so yielding getAllInQubits() in else-branches matches the result types from the operation's outputs.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
🧬 Code graph analysis (1)
test/zx/test_zx_functionality.cpp (1)
src/zx/FunctionalityConstruction.cpp (2)
  • buildFunctionality (791-808)
  • buildFunctionality (791-792)
🪛 GitHub Check: 🇨‌ Lint / 🚨 Lint
test/zx/test_zx_functionality.cpp

[warning] 277-277: test/zx/test_zx_functionality.cpp:277:3 [misc-const-correctness]
variable 'diag' of type 'ZXDiagram' can be declared 'const'

src/zx/FunctionalityConstruction.cpp

[warning] 340-340: src/zx/FunctionalityConstruction.cpp:340:3 [readability-suspicious-call-argument]
2nd argument 'target' (passed to 'ctrl') looks like it might be swapped with the 3rd, 'control' (passed to 'target')


[warning] 337-337: src/zx/FunctionalityConstruction.cpp:337:3 [readability-suspicious-call-argument]
2nd argument 'target' (passed to 'ctrl') looks like it might be swapped with the 3rd, 'control' (passed to 'target')

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
🔇 Additional comments (15)
test/zx/test_zx_functionality.cpp (6)

222-245: LGTM: CRZ test validates controlled-RZ decomposition.

The test correctly verifies that the CRZ gate implementation matches the expected decomposition using CNOT and RZ gates, and properly checks identity, zero global phase, and connectivity after reduction.


247-268: LGTM: MultiCZ test validates multi-controlled Z construction.

The test correctly verifies that MCZ is equivalent to H-MCX-H construction, with proper identity, phase, and connectivity checks.


296-315: LGTM: Multi-control X now supported.

The test correctly validates that multi-controlled X gates (with 3 controls) are now transformable and produce identity when inverted, confirming the PR objective to add multi-control support.


317-339: LGTM: Ancilla-assisted MCX decomposition validated.

The test correctly verifies the ancilla-assisted decomposition of MCX with 3 controls in a 5-qubit system, ensuring the decomposition is functionally equivalent.


341-361: LGTM: Edge case for 0-control MCX validated.

The test correctly verifies that MCX with no controls reduces to a simple X gate.


363-383: LGTM: Edge case for 1-control MCX validated.

The test correctly verifies that MCX with one control reduces to a CNOT gate.

src/zx/FunctionalityConstruction.cpp (9)

310-330: LGTM: CCZ implementation optimizes away redundant Hadamard edges.

The addCcz function correctly implements a double-controlled Z gate by removing the initial and final Hadamard transformations from the CCX decomposition, providing an optimized construction as mentioned in the PR description.


332-341: LGTM: CRZ decomposition correctly implemented.

The controlled-RZ decomposition correctly uses reversed CNOT direction (target→control) with appropriate phase factors (±θ/2), and includes a clarifying comment per previous review feedback.


343-365: LGTM: MCRZ recursive decomposition is correct.

The multi-controlled RZ implementation correctly handles:

  • Base case 0: Direct RZ spider
  • Base case 1: CRZ
  • Recursive case: Standard decomposition using CRZ(θ/2) interleaved with MCX to implement the controlled phase gate

The recursive pattern matches standard quantum computing decompositions.


367-424: LGTM: MCX decomposition handles all cases correctly.

The multi-controlled X implementation properly handles:

  • Base cases: 0 controls (X), 1 control (CNOT), 2 controls (CCX)
  • Ancilla-assisted recursion when spare qubits are available
  • No-ancilla decomposition using RX + MCZ pattern when qubits are fully utilized

The ancilla search logic and error handling were addressed in previous reviews. The divide-and-conquer strategy is correct.


426-451: LGTM: MCZ recursive decomposition is correct.

The multi-controlled Z implementation correctly handles base cases (0, 1, 2 controls) and uses the standard recursive decomposition with CRZ(π/2) interleaved with MCX for larger control counts.


684-686: LGTM: RZ added to single-control routing.

Correctly routes controlled-RZ operations through the new addCrz function when a single control is present.


727-744: LGTM: Two-control branch extended for Z and RZ.

The two-control branch now:

  • Extracts controls into a vector for use by multi-control functions
  • Routes CCZ through addCcz
  • Routes CCRZ through addMcrz

This correctly extends the transformation capabilities for double-controlled gates.


745-766: LGTM: Multi-control branch handles X, Z, and RZ.

The new multi-control branch (3+ controls, single target) correctly:

  • Routes MCX through addMcx
  • Routes MCZ through H-MCX-H pattern
  • Routes MCRZ through addMcrz
  • Throws for unsupported operations

This implements the core PR objective of supporting multi-controlled gates.


866-899: LGTM: transformableToZX correctly updated for RZ support.

The transformableToZX function now correctly recognizes RZ as transformable in:

  • Single-control scenarios (line 876)
  • Two-control scenarios (line 886)
  • Multi-control scenarios (line 895)

This aligns with the new routing logic in parseOp and fulfills the PR objective.

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: 1

♻️ Duplicate comments (1)
src/zx/FunctionalityConstruction.cpp (1)

332-341: About the suspicious addCrz argument order warnings

Static analysis flags the calls to addCrz as “suspicious call argument” because the function signature is

void FunctionalityConstruction::addCrz(
    ZXDiagram& diag,
    const PiExpression& phase,
    const Qubit target, const Qubit control,
    std::vector<Vertex>& qubits)

while call sites conceptually pass (control, target).

Given the body:

// CRZ decomposition uses reversed CNOT direction
addCnot(diag, control, target, qubits);
addZSpider(diag, target, qubits, -phase / 2);
addZSpider(diag, control, qubits, phase / 2);
addCnot(diag, control, target, qubits);

and how it’s invoked from parseOp and the multi-control helpers, the effective semantics are consistent with the intended CRZ decomposition (as validated by the new CRZ / MCRZ tests). The warnings are therefore a readability issue rather than a correctness bug.

If you want to silence the tools and make the intent clearer, consider flipping the parameter names/order to (const Qubit control, const Qubit target) and updating the call sites accordingly, so callers and implementation agree on ordering and static analysis no longer complains.

Also applies to: 360-363, 446-448, 684-686

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d008c6 and e2b7f7b.

📒 Files selected for processing (2)
  • src/zx/FunctionalityConstruction.cpp (5 hunks)
  • test/zx/test_zx_functionality.cpp (1 hunks)
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1301
File: mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp:84-85
Timestamp: 2025-12-04T06:59:40.314Z
Learning: In the MQTOpt MLIR routing passes (NaiveRoutingPassSC, AStarRoutingPassSC), the input IR is guaranteed to contain only 1-qubit and 2-qubit gates. All 3+-qubit gates must be decomposed before routing; otherwise the input IR is invalid. This invariant allows skipTwoQubitBlock in LayeredUnit.cpp to safely assert wires.size() == 2.
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
  • test/zx/test_zx_functionality.cpp
📚 Learning: 2025-12-08T23:58:03.684Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:80-100
Timestamp: 2025-12-08T23:58:03.684Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), quartz.ctrl uses reference semantics and does not return values, unlike flux.ctrl which uses value semantics and returns transformed qubits. When inlining a GPhaseOp in the CtrlInlineGPhase pattern, it's correct to create POp operations for positive controls and erase the CtrlOp without collecting or replacing result values.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-12-08T12:44:05.874Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:60-70
Timestamp: 2025-12-08T12:44:05.874Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The RemoveTrivialCtrl pattern correctly only checks getNumPosControls() when determining if a CtrlOp should be removed.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
  • test/zx/test_zx_functionality.cpp
📚 Learning: 2025-12-08T23:16:16.276Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-101
Timestamp: 2025-12-08T23:16:16.276Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The CtrlInlineGPhase canonicalization pattern correctly only checks getNumPosControls() and processes only positive controls when inlining a GPhaseOp.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-11-20T18:19:31.857Z
Learnt from: lsschmid
Repo: munich-quantum-toolkit/qmap PR: 832
File: src/hybridmap/HybridNeutralAtomMapper.cpp:1679-1715
Timestamp: 2025-11-20T18:19:31.857Z
Learning: In src/hybridmap/HybridNeutralAtomMapper.cpp, the compareSwapAndBridge and compareShuttlingAndFlyingAncilla methods intentionally use division by zero to produce infinity when distance reductions are zero or negative. This infinity value marks that option as highly unfavorable in cost comparisons, which is the intended behavior rather than a bug.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-11-01T15:57:31.153Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1283
File: src/qir/runtime/QIR.cpp:196-201
Timestamp: 2025-11-01T15:57:31.153Z
Learning: In the QIR runtime (src/qir/runtime/QIR.cpp), the PRX gate (__quantum__qis__prx__body) is an alias for the R gate (Phased X-Rotation) and should call runtime.apply<qc::R>(theta, phi, qubit), not runtime.apply<qc::RX>() which is a single-parameter rotation gate.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
  • test/zx/test_zx_functionality.cpp
📚 Learning: 2025-12-08T14:55:43.880Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-100
Timestamp: 2025-12-08T14:55:43.880Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), GPhaseOp is a zero-target operation (global phase). When a CtrlOp wraps a GPhaseOp, it only has control qubits and no targets. The CtrlInlineGPhase canonicalization pattern correctly produces outputs only for the positive controls, not targets.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-10-09T13:13:51.224Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:171-180
Timestamp: 2025-10-09T13:13:51.224Z
Learning: In MQT Core MLIR, UnitaryInterface operations guarantee 1-1 correspondence between input and output qubits in the same order. When cloning or modifying unitary operations (e.g., removing controls), this correspondence is maintained by construction, so yielding getAllInQubits() in else-branches matches the result types from the operation's outputs.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.

Applied to files:

  • test/zx/test_zx_functionality.cpp
📚 Learning: 2025-12-04T06:59:40.314Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1301
File: mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp:84-85
Timestamp: 2025-12-04T06:59:40.314Z
Learning: In the MQTOpt MLIR routing passes (NaiveRoutingPassSC, AStarRoutingPassSC), the input IR is guaranteed to contain only 1-qubit and 2-qubit gates. All 3+-qubit gates must be decomposed before routing; otherwise the input IR is invalid. This invariant allows skipTwoQubitBlock in LayeredUnit.cpp to safely assert wires.size() == 2.

Applied to files:

  • test/zx/test_zx_functionality.cpp
📚 Learning: 2025-11-20T16:57:24.305Z
Learnt from: lsschmid
Repo: munich-quantum-toolkit/qmap PR: 832
File: test/hybridmap/test_hybridmap.cpp:200-287
Timestamp: 2025-11-20T16:57:24.305Z
Learning: In test/hybridmap/test_hybridmap.cpp, the LongShuttling test is designed as a stress test to catch rare edge cases that only occur after extended shuttling sequences, which is why it maps a long circuit without explicit assertions beyond ensuring no exceptions are thrown.

Applied to files:

  • test/zx/test_zx_functionality.cpp
🧬 Code graph analysis (1)
test/zx/test_zx_functionality.cpp (2)
include/mqt-core/zx/FunctionalityConstruction.hpp (2)
  • qc (39-39)
  • qc (50-50)
src/zx/FunctionalityConstruction.cpp (2)
  • buildFunctionality (791-808)
  • buildFunctionality (791-792)
🪛 GitHub Check: 🇨‌ Lint / 🚨 Lint
src/zx/FunctionalityConstruction.cpp

[warning] 448-448: src/zx/FunctionalityConstruction.cpp:448:5 [readability-suspicious-call-argument]
3rd argument 'nextControl' (passed to 'target') looks like it might be swapped with the 4th, 'target' (passed to 'control')


[warning] 446-446: src/zx/FunctionalityConstruction.cpp:446:5 [readability-suspicious-call-argument]
3rd argument 'nextControl' (passed to 'target') looks like it might be swapped with the 4th, 'target' (passed to 'control')


[warning] 362-362: src/zx/FunctionalityConstruction.cpp:362:5 [readability-suspicious-call-argument]
3rd argument 'nextControl' (passed to 'target') looks like it might be swapped with the 4th, 'target' (passed to 'control')


[warning] 360-360: src/zx/FunctionalityConstruction.cpp:360:5 [readability-suspicious-call-argument]
3rd argument 'nextControl' (passed to 'target') looks like it might be swapped with the 4th, 'target' (passed to 'control')


[warning] 685-685: src/zx/FunctionalityConstruction.cpp:685:7 [readability-suspicious-call-argument]
3rd argument 'ctrl' (passed to 'target') looks like it might be swapped with the 4th, 'target' (passed to 'control')

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
🔇 Additional comments (3)
test/zx/test_zx_functionality.cpp (1)

222-505: New multi-control ZX tests look correct and give solid coverage

The CRZ, CCZ, MCX/MCZ, and MCRZ tests follow the existing testing pattern (build both circuits, compose with inverse, reduce, then check identity/global phase/connectivity) and cover:

  • 0/1/2/many controls for X, Z, and RZ;
  • both direct constructions (mcz/mcx/mcrz/crz) and decompositions via standard gates (CX, H, RZ).

I don’t see functional or structural issues here; these should give good end‑to‑end coverage of the new functionality.

src/zx/FunctionalityConstruction.cpp (2)

310-365: Multi-control CCZ / CRZ / MCRZ / MCZ decompositions look structurally sound

The new helpers

  • addCcz (two controls, Z-like),
  • addCrz (controlled-RZ),
  • addMcrz (multi-controlled RZ, with 0/1-control base cases and recursive step),
  • addMcz (multi-controlled Z with 0/1/2-control base cases and recursive step),

are consistent with known constructions:

  • Base cases (0 controls → single-qubit gate, 1 control → CRZ, 2 controls → CCZ) are explicit.
  • Recursive branches always shrink controls (via controls.pop_back()), so the recursion terminates.
  • The CRZ helper is reused in both the single-control path (in parseOp) and the multi-control decompositions, keeping the semantics aligned.
  • Tests exercise CRZ, CCZ, MCRZ (including k=0/1 and k>1), and MCZ (including k=0/1/2 and larger), and also check global-phase cancellation.

Given that and the existing ZX primitives, the design here looks correct and maintainable.

Also applies to: 426-451


661-767: parseOp and transformableToZX extensions for controlled RZ and multi-control gates are consistent

The changes in:

  • parseOp:

    • add a qc::OpType::RZ case for 1-control/1-target gates using addCrz;
    • route 2-control RZ to addMcrz with a 2-element controls vector;
    • route general multi-control X/Z/RZ (one target) to addMcx, the CZ-style H–MCX–H pattern, and addMcrz respectively.
  • transformableToZX:

    • include qc::OpType::RZ in the supported sets for
      • uncontrolled single-qubit,
      • single-control two-qubit, and
      • multi-control single-target operations,

are aligned: every newly-routed case has a corresponding “supported” entry, and unsupported multi-control types still fall through to the existing exceptions (as exercised by the UnsupportedControl tests).

I don’t see missing or inconsistent cases here.

Also applies to: 876-899

Comment thread src/zx/FunctionalityConstruction.cpp Outdated
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: 1

♻️ Duplicate comments (1)
src/zx/FunctionalityConstruction.cpp (1)

332-341: Consider swapping parameter names to match codebase convention.

The parameter order (target, control) differs from the convention used elsewhere in this file (e.g., addCnot, addCphase use (ctrl, target)). This triggers persistent static analysis warnings at every call site (lines 354, 360, 362, 436, 446, 448, 685).

While the comment clarifies the decomposition intent, renaming the parameters would eliminate linter noise and improve readability.

 void FunctionalityConstruction::addCrz(ZXDiagram& diag,
                                        const PiExpression& phase,
-                                       const Qubit target, const Qubit control,
+                                       const Qubit ctrl, const Qubit target,
                                        std::vector<Vertex>& qubits) {
   // CRZ decomposition uses reversed CNOT direction
-  addCnot(diag, control, target, qubits);
-  addZSpider(diag, target, qubits, -phase / 2);
-  addZSpider(diag, control, qubits, phase / 2);
-  addCnot(diag, control, target, qubits);
+  addCnot(diag, target, ctrl, qubits);
+  addZSpider(diag, ctrl, qubits, -phase / 2);
+  addZSpider(diag, target, qubits, phase / 2);
+  addCnot(diag, target, ctrl, qubits);
 }

This would require updating all call sites to swap arguments accordingly.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d008c6 and e2b7f7b.

📒 Files selected for processing (2)
  • src/zx/FunctionalityConstruction.cpp (5 hunks)
  • test/zx/test_zx_functionality.cpp (1 hunks)
🧰 Additional context used
🧠 Learnings (10)
📚 Learning: 2025-12-08T12:44:05.874Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:60-70
Timestamp: 2025-12-08T12:44:05.874Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The RemoveTrivialCtrl pattern correctly only checks getNumPosControls() when determining if a CtrlOp should be removed.

Applied to files:

  • test/zx/test_zx_functionality.cpp
  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.

Applied to files:

  • test/zx/test_zx_functionality.cpp
📚 Learning: 2025-10-09T13:20:11.483Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/test/Dialect/MQTOpt/Transforms/lift-measurements.mlir:269-288
Timestamp: 2025-10-09T13:20:11.483Z
Learning: In the MQT MLIR dialect, the `rz` gate should not be included in the `DIAGONAL_GATES` set for the `ReplaceBasisStateControlsWithIfPattern` because its operator matrix does not have the required shape | 1 0 | / | 0 x | for the targets-as-controls optimization. It is only included in `LiftMeasurementsAboveGatesPatterns` where the matrix structure requirement differs.

Applied to files:

  • test/zx/test_zx_functionality.cpp
  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-11-20T16:57:24.305Z
Learnt from: lsschmid
Repo: munich-quantum-toolkit/qmap PR: 832
File: test/hybridmap/test_hybridmap.cpp:200-287
Timestamp: 2025-11-20T16:57:24.305Z
Learning: In test/hybridmap/test_hybridmap.cpp, the LongShuttling test is designed as a stress test to catch rare edge cases that only occur after extended shuttling sequences, which is why it maps a long circuit without explicit assertions beyond ensuring no exceptions are thrown.

Applied to files:

  • test/zx/test_zx_functionality.cpp
📚 Learning: 2025-12-08T23:58:03.684Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp:80-100
Timestamp: 2025-12-08T23:58:03.684Z
Learning: In the Quartz dialect (mlir/lib/Dialect/Quartz/IR/Modifiers/CtrlOp.cpp), quartz.ctrl uses reference semantics and does not return values, unlike flux.ctrl which uses value semantics and returns transformed qubits. When inlining a GPhaseOp in the CtrlInlineGPhase pattern, it's correct to create POp operations for positive controls and erase the CtrlOp without collecting or replacing result values.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-12-08T23:16:16.276Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-101
Timestamp: 2025-12-08T23:16:16.276Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), negative controls are not supported at the current stage. The CtrlInlineGPhase canonicalization pattern correctly only checks getNumPosControls() and processes only positive controls when inlining a GPhaseOp.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-11-20T18:19:31.857Z
Learnt from: lsschmid
Repo: munich-quantum-toolkit/qmap PR: 832
File: src/hybridmap/HybridNeutralAtomMapper.cpp:1679-1715
Timestamp: 2025-11-20T18:19:31.857Z
Learning: In src/hybridmap/HybridNeutralAtomMapper.cpp, the compareSwapAndBridge and compareShuttlingAndFlyingAncilla methods intentionally use division by zero to produce infinity when distance reductions are zero or negative. This infinity value marks that option as highly unfavorable in cost comparisons, which is the intended behavior rather than a bug.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-11-01T15:57:31.153Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1283
File: src/qir/runtime/QIR.cpp:196-201
Timestamp: 2025-11-01T15:57:31.153Z
Learning: In the QIR runtime (src/qir/runtime/QIR.cpp), the PRX gate (__quantum__qis__prx__body) is an alias for the R gate (Phased X-Rotation) and should call runtime.apply<qc::R>(theta, phi, qubit), not runtime.apply<qc::RX>() which is a single-parameter rotation gate.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-12-08T14:55:43.880Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1264
File: mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp:78-100
Timestamp: 2025-12-08T14:55:43.880Z
Learning: In the Flux dialect (mlir/lib/Dialect/Flux/IR/Modifiers/CtrlOp.cpp), GPhaseOp is a zero-target operation (global phase). When a CtrlOp wraps a GPhaseOp, it only has control qubits and no targets. The CtrlInlineGPhase canonicalization pattern correctly produces outputs only for the positive controls, not targets.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
📚 Learning: 2025-10-09T13:13:51.224Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:171-180
Timestamp: 2025-10-09T13:13:51.224Z
Learning: In MQT Core MLIR, UnitaryInterface operations guarantee 1-1 correspondence between input and output qubits in the same order. When cloning or modifying unitary operations (e.g., removing controls), this correspondence is maintained by construction, so yielding getAllInQubits() in else-branches matches the result types from the operation's outputs.

Applied to files:

  • src/zx/FunctionalityConstruction.cpp
🧬 Code graph analysis (2)
test/zx/test_zx_functionality.cpp (2)
include/mqt-core/zx/FunctionalityConstruction.hpp (2)
  • qc (39-39)
  • qc (50-50)
src/zx/FunctionalityConstruction.cpp (2)
  • buildFunctionality (791-808)
  • buildFunctionality (791-792)
src/zx/FunctionalityConstruction.cpp (2)
include/mqt-core/ir/operations/OpType.hpp (1)
  • toString (40-40)
src/ir/operations/OpType.cpp (2)
  • toString (21-35)
  • toString (21-21)
🪛 GitHub Check: 🇨‌ Lint / 🚨 Lint
src/zx/FunctionalityConstruction.cpp

[warning] 448-448: src/zx/FunctionalityConstruction.cpp:448:5 [readability-suspicious-call-argument]
3rd argument 'nextControl' (passed to 'target') looks like it might be swapped with the 4th, 'target' (passed to 'control')


[warning] 446-446: src/zx/FunctionalityConstruction.cpp:446:5 [readability-suspicious-call-argument]
3rd argument 'nextControl' (passed to 'target') looks like it might be swapped with the 4th, 'target' (passed to 'control')


[warning] 362-362: src/zx/FunctionalityConstruction.cpp:362:5 [readability-suspicious-call-argument]
3rd argument 'nextControl' (passed to 'target') looks like it might be swapped with the 4th, 'target' (passed to 'control')


[warning] 360-360: src/zx/FunctionalityConstruction.cpp:360:5 [readability-suspicious-call-argument]
3rd argument 'nextControl' (passed to 'target') looks like it might be swapped with the 4th, 'target' (passed to 'control')


[warning] 685-685: src/zx/FunctionalityConstruction.cpp:685:7 [readability-suspicious-call-argument]
3rd argument 'ctrl' (passed to 'target') looks like it might be swapped with the 4th, 'target' (passed to 'control')

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
🔇 Additional comments (13)
src/zx/FunctionalityConstruction.cpp (6)

310-330: LGTM: CCZ decomposition correctly optimizes away Hadamard bookends.

The addCcz implementation is a well-optimized variant of CCX that directly produces the CCZ decomposition. Compared to addCcx, it removes the opening H gate and adjusts the closing sequence to use an H-edge followed by another H-edge, which correctly implements CCZ = H(target) · CCX · H(target) in a more efficient ZX form.


343-365: LGTM: MCRZ recursive decomposition is correct.

The implementation correctly handles base cases (0 and 1 controls) and uses the standard recursive decomposition: CRZ(θ/2) · MCX · CRZ(-θ/2) · MCX for extracting one control at a time.


367-424: LGTM: MCX decomposition with ancilla-based optimization.

The implementation correctly handles all base cases and provides two decomposition paths:

  1. When ancilla qubits are available: efficient divide-and-conquer using the spare qubit
  2. When no ancilla available: RX-based decomposition using MCZ and MCRZ

Previous review issues (uninitialized anc, missing error check) have been properly addressed.


426-451: LGTM: MCZ recursive decomposition is correct.

The implementation properly handles all base cases including the use of addCcz for 2 controls (which is more efficient than the general recursion). The recursive decomposition follows the standard pattern.


684-686: LGTM: parseOp correctly routes multi-control operations to new handlers.

The integration properly:

  1. Routes single-control RZ to addCrz
  2. Builds control vectors from operation controls for 2+ control cases
  3. Delegates to addMcrz, addMcx, addMcz as appropriate

The static analysis warning on line 685 is the same addCrz parameter naming issue noted earlier.

Also applies to: 727-744, 745-766


876-900: LGTM: transformableToZX correctly updated for new multi-control gates.

The transformability checks now properly reflect the expanded parseOp capabilities, allowing RZ in single-control, 2-control, and arbitrary multi-control contexts.

test/zx/test_zx_functionality.cpp (7)

222-245: LGTM: CRZ test properly validates against known decomposition.

The test correctly validates CRZ by comparing against the explicit decomposition: CNOT · RZ(-θ/2) · CNOT · RZ(θ/2).


247-268: LGTM: MultiCZ test validates against H·MCX·H equivalence.

Correct validation that MCZ equals H·MCX·H on the target qubit.


269-293: LGTM: CCZ test validates correctly.

Past review issue (unused diag variable) has been addressed.


316-338: LGTM: Proper validation against ancilla-based decomposition.

This test correctly validates the MCX decomposition by comparing against the explicit ancilla-based construction that the implementation uses internally.


340-382: LGTM: Boundary cases for MCX properly tested.

MultiControlX0 validates 0-control MCX equals X, and MultiControlX1 validates 1-control MCX equals CNOT. These are important boundary condition tests.


384-449: LGTM: Comprehensive MCZ test coverage.

The tests properly validate:

  • MultiControlZ: MCZ equals H·MCX·H
  • MultiControlZ0: 0-control MCZ equals Z
  • MultiControlZ1: 1-control MCZ equals CZ

Good coverage of boundary conditions and correct equivalences.


451-506: LGTM: MCRZ tests cover self-inverse and boundary cases.

The tests validate:

  • MultiControlRZ: MCRZ(θ) · MCRZ(-θ) = I (self-inverse property)
  • MultiControlRZ0: 0-control MCRZ equals RZ
  • MultiControlRZ1: 1-control MCRZ equals CRZ

Good boundary condition coverage.

Signed-off-by: Lukas Burgholzer <burgholzer@me.com>
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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@include/mqt-core/zx/FunctionalityConstruction.hpp`:
- Around line 127-144: The new helper declarations use inconsistent parameter
conventions: for multi-qubit controls change addMcrx, addMcrz, addMcx, addMcz to
accept controls as const std::vector<Qubit>& (matching
addMcphase/addMcswap/addMcrzz) unless the corresponding implementations in
src/zx/FunctionalityConstruction.cpp intentionally mutate the vector—if they do
mutate, keep by-value but add a comment; and remove the top-level const from
by-value scalar Qubit parameters in addCrx, addCrz (and any other declarations
here using const Qubit control/target) so they match the existing style (e.g.,
addCnot) and the header API convention.

In `@src/zx/FunctionalityConstruction.cpp`:
- Around line 364-383: In addCcz, remove the two canceling Hadamard Z-spiders
applied to target: the calls addZSpider(diag, target, qubits,
PiExpression(PiRational(0, 1)), EdgeType::Hadamard) and addZSpider(diag, target,
qubits, PiExpression(), EdgeType::Hadamard); they commute across the intervening
addCnot(diag, ctrl0, ctrl1, qubits) (which only touches ctrl0/ctrl1) and
therefore cancel, so delete those two addZSpider calls and keep the rest of
addCcz unchanged.

In `@test/zx/test_zx_functionality.cpp`:
- Around line 544-608: Tests MCRx0–MCRx3, MCPhase, MCS, MCT currently do
self-comparison (qc and qcPrime built identically) which doesn't validate the ZX
decomposition; instead, update each test to build qc with the original gate call
(e.g. qc.mcrx(...), qc.mcp(...), qc.mcs(...), qc.mct(...)) and build qcPrime
using the known equivalent decomposition before calling checkEquivalence (use
mcp(PI/2, ...) for MCS, mcp(PI/4, ...) for MCT, and H + mcrz(...) + H sequence
for MCRx; for MCPhase keep identity or compare against its expected
decomposition), referencing the test names (MCRx0..MCRx3, MCPhase, MCS, MCT) and
methods (mcrx, mcp, mcs, mct, checkEquivalence) so the tests verify ZX
construction rather than simple invert() cancellation.
🪄 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: 2294fc44-8fc2-48e3-8f0b-ef5788edbe60

📥 Commits

Reviewing files that changed from the base of the PR and between 8599775 and e39cc6f.

📒 Files selected for processing (3)
  • include/mqt-core/zx/FunctionalityConstruction.hpp
  • src/zx/FunctionalityConstruction.cpp
  • test/zx/test_zx_functionality.cpp

Comment thread include/mqt-core/zx/FunctionalityConstruction.hpp Outdated
Comment thread src/zx/FunctionalityConstruction.cpp
Comment thread test/zx/test_zx_functionality.cpp
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: 3

Caution

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

⚠️ Outside diff range comments (1)
src/zx/FunctionalityConstruction.cpp (1)

740-816: 🧹 Nitpick | 🔵 Trivial

Optional: consolidate 2-control and 3+-control 1-target dispatch.

The P, T, S, RZ, RX cases are identical between the getNcontrols() == 2 && getNtargets() == 1 branch (lines 760-776) and the getNtargets() == 1 branch (lines 795-811) — they all call addMcphase/addMcrz/addMcrx with a controls vector. Since addMcx/addMcz already dispatch to addCcx/addCcz at controls.size() == 2, the 2-control special branch could be folded into the general multi-control branch with no loss of efficiency. Keeping the fast paths is fine; flagging for consideration.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/zx/FunctionalityConstruction.cpp` around lines 740 - 816, The two-control
special-case duplicates the P/T/S/RZ/RX handling found in the general
single-target branch; refactor by first building a controls vector (as done in
the getNtargets()==1 branch) and then switch on op->getType() to call
addMcphase/addMcrz/addMcrx (and for X/Z call addMcx/addMcz which already
dispatch to addCcx/addCcz when controls.size()==2), eliminating the separate
getNcontrols()==2 branch or moving only the addCcx/addCcz fast-paths into the
general branch while reusing the common multi-control cases; update the code
around the handling using the functions addCcx, addCcz, addMcphase, addMcrz,
addMcrx, addMcx and addMcz accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/zx/FunctionalityConstruction.cpp`:
- Around line 401-410: The parameter names in FunctionalityConstruction::addCrz
should be renamed from q0,q1 to control,target to match the header and other
methods (e.g. addCrx) and to make the "reversed CNOT" comment clearer; update
the function signature in the definition to use (const Qubit control, const
Qubit target) and replace uses of q0->control and q1->target inside the body
(including the addZSpider and addCnot calls) so the implementation and header
names are aligned.
- Around line 777-816: The thrown ZXException messages for unsupported
multi-control ops concatenate the op type without a separator (e.g. " (3
ctrls)RZ"); update both default throws in the branch handling op->getNtargets()
== 1 so the message inserts a separator after the closing parenthesis — change
the concatenation that uses std::to_string(op->getNcontrols()) + " ctrls)" +
qc::toString(op->getType()) to use " ctrls): " (i.e. ") : " or preferably "): ")
before qc::toString(op->getType()) so the final string becomes "Unsupported
Multi-control operation (N ctrls): <OpType>".
- Around line 236-248: In addMcrzz, make the handling of the optional
unconvertedPhase consistent with the halving of phase: when calling addRzz pass
unconvertedPhase halved (i.e. unconvertedPhase.value() / 2) for the first call
and the negated halved value for the second call; preserve absent-case by
forwarding std::nullopt when unconvertedPhase is not set and use
std::optional<double>(-unconvertedPhase.value() / 2) for the negated second-call
case so both addRzz invocations receive the same scaled angle.

---

Outside diff comments:
In `@src/zx/FunctionalityConstruction.cpp`:
- Around line 740-816: The two-control special-case duplicates the P/T/S/RZ/RX
handling found in the general single-target branch; refactor by first building a
controls vector (as done in the getNtargets()==1 branch) and then switch on
op->getType() to call addMcphase/addMcrz/addMcrx (and for X/Z call addMcx/addMcz
which already dispatch to addCcx/addCcz when controls.size()==2), eliminating
the separate getNcontrols()==2 branch or moving only the addCcx/addCcz
fast-paths into the general branch while reusing the common multi-control cases;
update the code around the handling using the functions addCcx, addCcz,
addMcphase, addMcrz, addMcrx, addMcx and addMcz accordingly.
🪄 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: 9ecbfde0-a123-4423-a0de-830aafe8a6a3

📥 Commits

Reviewing files that changed from the base of the PR and between e39cc6f and 2cad394.

📒 Files selected for processing (2)
  • include/mqt-core/zx/FunctionalityConstruction.hpp
  • src/zx/FunctionalityConstruction.cpp

Comment thread src/zx/FunctionalityConstruction.cpp
Comment thread src/zx/FunctionalityConstruction.cpp
Comment thread src/zx/FunctionalityConstruction.cpp
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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/zx/FunctionalityConstruction.cpp`:
- Around line 763-779: Switch handling multi-control phase gates currently maps
OpType::T and OpType::S to addMcphase but lacks OpType::Tdg and OpType::Sdg,
causing multi-control inverse phases to be unsupported; add cases for
qc::OpType::Tdg and qc::OpType::Sdg in the same switch(s) where T and S are
handled (the branches that call addMcphase) and call addMcphase(diag,
PiExpression{PiRational(-1,4)}, {ctrl0, ctrl1}, target, qubits) for Tdg and
addMcphase(diag, PiExpression{PiRational(-1,2)}, {ctrl0, ctrl1}, target, qubits)
for Sdg; make the identical edits in the other similar switch blocks that
currently handle T/S (the other multi-control dispatch locations referenced in
the review) so negative-phase variants are supported consistently.
🪄 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: 3fc6a773-6865-434b-8a46-5c6e7dd3de99

📥 Commits

Reviewing files that changed from the base of the PR and between 2cad394 and b9dc71c.

📒 Files selected for processing (1)
  • src/zx/FunctionalityConstruction.cpp

Comment thread src/zx/FunctionalityConstruction.cpp
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: 1

Caution

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

⚠️ Outside diff range comments (1)
test/zx/test_zx_functionality.cpp (1)

427-441: ⚠️ Potential issue | 🟠 Major

Fix misplaced H gate on line 438.

Line 438 applies H(1) to qc (the original iSWAP circuit) instead of to qcPrime (the decomposition). The correct iSWAP decomposition—confirmed in test/dd/test_package.cpp and src/zx/FunctionalityConstruction.cpp—is: S q[0]; S q[1]; H q[0]; CX q[0],q[1]; CX q[1],q[0]; H q[1]. As written, the test compares iswap(0,1) · H(1) against an incomplete decomposition (missing the final H), so the two circuits are not equivalent.

🔧 Proposed fix
   auto qcPrime = qc::QuantumComputation(2);
   qcPrime.s(0);
   qcPrime.s(1);
   qcPrime.h(0);
   qcPrime.cx(0, 1);
   qcPrime.cx(1, 0);
-  qc.h(1);
+  qcPrime.h(1);

   checkEquivalence(qc, qcPrime, {0, 1});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/zx/test_zx_functionality.cpp` around lines 427 - 441, The test builds an
iSWAP circuit in qc but mistakenly applies the final H gate to qc instead of the
decomposition qcPrime; fix by moving the H(1) call so the decomposition sequence
on qcPrime is S(0), S(1), H(0), CX(0,1), CX(1,0), H(1) (i.e., call qcPrime.h(1)
rather than qc.h(1)) so qc.iswap(0,1) is compared to the complete qcPrime
decomposition in TEST_F(ZXFunctionalityTest, ISWAP).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/zx/FunctionalityConstruction.cpp`:
- Around line 740-741: The inline comment in the branch that checks
op->getNcontrols() == 2 && op->getNtargets() == 1 is stale: update the comment
near that conditional (in FunctionalityConstruction.cpp around the branch
handling op) to reflect that it now dispatches two-control, single-target gates
beyond just ccx/ccz — e.g., change "three-qubit controlled gates (ccx or ccz)"
to "two-control, single-target gates (ccx, ccz, mcphase/mcrz/mcrx with 2
controls)" or a similar concise description mentioning P/T/S/RZ/RX
multi-controlled variants.

---

Outside diff comments:
In `@test/zx/test_zx_functionality.cpp`:
- Around line 427-441: The test builds an iSWAP circuit in qc but mistakenly
applies the final H gate to qc instead of the decomposition qcPrime; fix by
moving the H(1) call so the decomposition sequence on qcPrime is S(0), S(1),
H(0), CX(0,1), CX(1,0), H(1) (i.e., call qcPrime.h(1) rather than qc.h(1)) so
qc.iswap(0,1) is compared to the complete qcPrime decomposition in
TEST_F(ZXFunctionalityTest, ISWAP).
🪄 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: ff15c6c8-e01e-4ab3-9737-c892a13045b9

📥 Commits

Reviewing files that changed from the base of the PR and between b9dc71c and 3002186.

📒 Files selected for processing (2)
  • src/zx/FunctionalityConstruction.cpp
  • test/zx/test_zx_functionality.cpp

Comment thread src/zx/FunctionalityConstruction.cpp Outdated
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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/zx/FunctionalityConstruction.cpp`:
- Around line 429-466: The addMcx implementation in
FunctionalityConstruction::addMcx (which recursively calls addMcx and
addMcphase) produces exponential growth (~Θ(3^N)) of ZX sub-gates for N
controls; add a concise inline comment above FunctionalityConstruction::addMcx
explaining the recurrence (4×addMcx(⌈N/2⌉)+addMcphase(N−1) with
addMcphase(M)→2×addMcx(M)+addMcphase(M−1)), note concrete blow-up examples
(e.g., N=5→~77, N=8→~2.3k, N=10→~20k), and warn maintainers that this is
exponential and may require switching to a Barenco-style O(N²) ancilla-free
decomposition as a follow-up or when large N is expected; optionally add a TODO
referencing addMcphase and the Barenco decomposition as the preferred
alternative for large controls.
🪄 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: 89677a00-3e27-43e7-9dec-2e105db47b84

📥 Commits

Reviewing files that changed from the base of the PR and between 3002186 and e8efacf.

📒 Files selected for processing (1)
  • src/zx/FunctionalityConstruction.cpp

Comment thread src/zx/FunctionalityConstruction.cpp
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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/zx/test_zx_functionality.cpp`:
- Around line 46-52: Replace non-fatal EXPECT_* checks with fatal ASSERT_*
checks so test helpers stop on precondition failures: use
ASSERT_TRUE(FunctionalityConstruction::transformableToZX(&qc1)) and
ASSERT_TRUE(FunctionalityConstruction::transformableToZX(&qc2)) and
ASSERT_EQ(qc1.getNqubits(), qc2.getNqubits()) before calling
FunctionalityConstruction::buildFunctionality(&qc1), buildFunctionality(&qc2),
and d1.concat(d2.invert()) to avoid cascading exceptions or secondary failures.
🪄 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: 01ae07c1-4bac-47b1-9442-bea0be55c9d6

📥 Commits

Reviewing files that changed from the base of the PR and between e8efacf and 8031ec0.

📒 Files selected for processing (2)
  • src/zx/FunctionalityConstruction.cpp
  • test/zx/test_zx_functionality.cpp

Comment thread test/zx/test_zx_functionality.cpp Outdated
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.

I think this should be pretty much good to go now. A follow-up issue for the more efficient MCX creation has been created.

@denialhaag
Copy link
Copy Markdown
Member

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

✅ Actions performed

Review triggered.

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.

@denialhaag
Copy link
Copy Markdown
Member

Merging this now! Thanks a lot for the contribution, @keefehuang! 🙂

@denialhaag denialhaag merged commit 2497aee into munich-quantum-toolkit:main Apr 20, 2026
33 checks passed
mergify Bot pushed a commit that referenced this pull request Apr 20, 2026
## Description

Add CRZ, MCX, MCZ, MCRZ functionality to ZX package.
Add convenience addCCZ function as it avoids one additional Hardamard
edge (optimizes it away).

Fixes #1357

## Checklist:

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

---------

Signed-off-by: Marcel Walter <marcelwalter.mw@gmail.com>
Signed-off-by: Keefe Huang <30915130+keefehuang@users.noreply.github.com>
Signed-off-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com>
Signed-off-by: Lukas Burgholzer <burgholzer@me.com>
Co-authored-by: Keefe Huang <keefe.huang@tum.de>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Marcel Walter <marcel.walter@tum.de>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Lukas Burgholzer <burgholzer@me.com>
Co-authored-by: Keefe Huang <huangq@cit.tum.de>
Co-authored-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com>
(cherry picked from commit 2497aee)

# Conflicts:
#	CHANGELOG.md
#	test/zx/test_zx_functionality.cpp
mergify Bot added a commit that referenced this pull request Apr 20, 2026
… (#1651)

## Description

Add CRZ, MCX, MCZ, MCRZ functionality to ZX package.
Add convenience addCCZ function as it avoids one additional Hardamard
edge (optimizes it away).

Fixes #1357

## Checklist:


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



(cherry picked from commit 2497aee)

---------

Co-authored-by: Keefe Huang <30915130+keefehuang@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com>
jmoosburger pushed a commit to jmoosburger/mqt-core that referenced this pull request Apr 25, 2026
…m-toolkit#1380)

## Description

Add CRZ, MCX, MCZ, MCRZ functionality to ZX package.
Add convenience addCCZ function as it avoids one additional Hardamard
edge (optimizes it away).

Fixes munich-quantum-toolkit#1357

## Checklist:


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

---------

Signed-off-by: Marcel Walter <marcelwalter.mw@gmail.com>
Signed-off-by: Keefe Huang <30915130+keefehuang@users.noreply.github.com>
Signed-off-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com>
Signed-off-by: Lukas Burgholzer <burgholzer@me.com>
Co-authored-by: Keefe Huang <keefe.huang@tum.de>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Marcel Walter <marcel.walter@tum.de>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Lukas Burgholzer <burgholzer@me.com>
Co-authored-by: Keefe Huang <huangq@cit.tum.de>
Co-authored-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-potential Changes to be backported to the stable branch feature New feature or request ZX Anything related to the ZX package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Add multi-controlled gate support to ZX package

4 participants