-
-
Notifications
You must be signed in to change notification settings - Fork 72
✨ Add decompose-multi-controlled pass for multi-controlled X/Z decomposition
#1810
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 12 commits
bec1fdf
d48be9c
f0de507
08a9dfa
d47641d
e0b53a4
0ef3f71
dff6eeb
9ec9e6f
cf57d47
0292b3e
b2f30eb
55176d1
968d06c
1351927
0b297c5
f782391
0905bd3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might once more just be me, but is there really any value in having a dedicated header file for these functions? Couldn't these simply be part of the respective pass CPP file? Edit: having gone through more of the PR, I think I just don't quite like the idea that so much of the pass logic is actively decoupled from MLIR. For the most part, we have the MLIR pass logic and that then builds some auxiliary data structures, which are passed to a collection of methods that doesn't even really care about MLIR. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| /* | ||
| * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM | ||
| * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH | ||
| * All rights reserved. | ||
| * | ||
| * SPDX-License-Identifier: MIT | ||
| * | ||
| * Licensed under the MIT License | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <mlir/IR/Builders.h> | ||
| #include <mlir/IR/Location.h> | ||
| #include <mlir/IR/Value.h> | ||
| #include <mlir/Support/LLVM.h> | ||
|
|
||
| namespace mlir::qco::decomposition { | ||
|
|
||
| /** | ||
| * @brief Emits a decomposition of a multi-controlled X gate. | ||
| * | ||
| * @details Emits a sequence of one- and two-qubit gates that, taken together, | ||
| * implement the multi-controlled X gate that flips @p target whenever all @p | ||
| * controls are in the @f$|1\rangle@f$ state. The decomposition follows Huang | ||
| * and Palsberg (PLDI 2024), composing Iten et al. (Phys. Rev. A 93, 032318, | ||
| * 2016) dirty-ancilla subcircuits for large control counts. The emitted gates | ||
| * are `h`, `t`, `tdg`, `p`, and `cx`. | ||
| * | ||
| * @note Adapted from ``synth_mcx_noaux_hp24`` and ``synth_mcx_n_dirty_i15`` in | ||
| * the IBM Qiskit framework. | ||
| * (C) Copyright IBM 2025 | ||
| * | ||
| * This code is licensed under the Apache License, Version 2.0. You may | ||
| * obtain a copy of this license in the LICENSE.txt file in the root | ||
| * directory of this source tree or at | ||
| * https://www.apache.org/licenses/LICENSE-2.0. | ||
| * | ||
| * Any modifications or derivative works of this code must retain this | ||
| * copyright notice, and modified files need to carry a notice | ||
| * indicating that they have been altered from the originals. | ||
| * | ||
| * @param builder Builder positioned at the desired insertion point. | ||
| * @param loc Location attached to the emitted operations. | ||
| * @param controls Current SSA values of the control qubits. | ||
| * @param target Current SSA value of the target qubit. | ||
| * @return The updated SSA values, ordered as `[controls..., target]`. | ||
| * | ||
| * @pre @p controls must contain at least two qubits. | ||
| */ | ||
| [[nodiscard]] SmallVector<Value> synthesizeMcx(OpBuilder& builder, Location loc, | ||
| ValueRange controls, | ||
| Value target); | ||
|
|
||
| /** | ||
| * @brief Emits a decomposition of a multi-controlled Z gate. | ||
| * | ||
| * @details Emits the same gate set as @ref synthesizeMcx (`h`, `t`, `tdg`, `p`, | ||
| * and `cx`). For @f$k \ge 2@f$ controls, emits the HP24 MCX core directly, | ||
| * since @f$\mathrm{MCZ} = | ||
| * H_{\text{target}}\,(H_{\text{target}}\,\mathrm{CORE}\, | ||
| * H_{\text{target}})\,H_{\text{target}} = \mathrm{CORE}@f$ and the MCX | ||
| * Hadamard bookends cancel under this conjugation. | ||
| * | ||
| * @param builder Builder positioned at the desired insertion point. | ||
| * @param loc Location attached to the emitted operations. | ||
| * @param controls Current SSA values of the control qubits. | ||
| * @param target Current SSA value of the target qubit. | ||
| * @return The updated SSA values, ordered as `[controls..., target]`. | ||
| * | ||
| * @pre @p controls must contain at least two qubits. | ||
| */ | ||
| [[nodiscard]] SmallVector<Value> synthesizeMcz(OpBuilder& builder, Location loc, | ||
| ValueRange controls, | ||
| Value target); | ||
|
|
||
| } // namespace mlir::qco::decomposition |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -200,4 +200,46 @@ def HadamardLifting : Pass<"hadamard-lifting", "mlir::ModuleOp"> { | |
| }]; | ||
| } | ||
|
|
||
| //===----------------------------------------------------------------------===// | ||
| // Decomposition Passes | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| def DecomposeMultiControlled | ||
| : Pass<"decompose-multi-controlled", "mlir::ModuleOp"> { | ||
| let dependentDialects = ["mlir::qco::QCODialect", | ||
| "::mlir::arith::ArithDialect"]; | ||
| let summary = | ||
| "Decompose multi-controlled gates into one- and two-qubit gates"; | ||
| let description = [{ | ||
| Decomposes multi-controlled gates (`qco.ctrl`) into a sequence of one- and | ||
| two-qubit gates that can subsequently be handled by one- and two-qubit gate | ||
| synthesis methods. | ||
|
|
||
| The pass matches `qco.ctrl` operations with at least `min-controls` control | ||
| qubits whose body is a single `qco.x` or `qco.z` on one target | ||
| (multi-controlled Pauli-X or Pauli-Z). Other multi-controlled gates are left | ||
| unchanged. Support for further base gates will be added gradually. | ||
|
|
||
| Multi-controlled X gates are decomposed using the Huang-Palsberg (PLDI 2024) | ||
| no-ancilla synthesis algorithm with a linear number of CX gates in the | ||
| number of controls, composing Iten et al. (Phys. Rev. A 93, 032318, 2016) | ||
| dirty-ancilla subcircuits for large control counts. Multi-controlled Z gates | ||
| use the same HP24 core (algebraically equivalent to | ||
| @f$H_{\text{target}}\,\mathrm{MCX}\,H_{\text{target}}@f$, without redundant | ||
| Hadamard bookends for @f$k \ge 2@f$). The pass emits `h`, `t`, `tdg`, `p`, | ||
| and `cx` gates. | ||
|
|
||
| By default (`min-controls=2`), gates with at least two controls (e.g. `CCX`, | ||
| `CCZ`, and larger `MCX`/`MCZ`) are decomposed, leaving single-control gates | ||
| (e.g. `CX`, `CZ`) and bare one-qubit gates intact. Increase `min-controls` | ||
| to keep smaller multi-controlled gates intact (e.g., when `CCX` is a native | ||
| gate of the target). | ||
| }]; | ||
| let options = [Option< | ||
| "minControls", "min-controls", "uint64_t", "2", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIRC tablegen also allows to directly place constraints on options/parameters. Maybe it is worth looking into whether this would simplify some of the error handling code throughout this PR. |
||
| "Minimum number of controls for which a controlled gate is " | ||
| "decomposed. Controlled gates with fewer controls are left " | ||
| "unchanged. Must be at least 2.">]; | ||
| } | ||
|
|
||
| #endif // MLIR_DIALECT_QCO_TRANSFORMS_PASSES_TD | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| /* | ||
| * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM | ||
| * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH | ||
| * All rights reserved. | ||
| * | ||
| * SPDX-License-Identifier: MIT | ||
| * | ||
| * Licensed under the MIT License | ||
| */ | ||
|
|
||
| #include "mlir/Dialect/QCO/IR/QCOInterfaces.h" | ||
| #include "mlir/Dialect/QCO/IR/QCOOps.h" | ||
| #include "mlir/Dialect/QCO/Transforms/Decomposition/MultiControlled.h" | ||
| #include "mlir/Dialect/QCO/Transforms/Passes.h" | ||
| #include "mlir/Dialect/Utils/Utils.h" | ||
|
|
||
| #include <mlir/Dialect/Arith/IR/Arith.h> // IWYU pragma: keep (Passes.h.inc) | ||
| #include <mlir/IR/MLIRContext.h> | ||
| #include <mlir/IR/PatternMatch.h> | ||
| #include <mlir/Support/LLVM.h> | ||
| #include <mlir/Support/LogicalResult.h> | ||
| #include <mlir/Transforms/GreedyPatternRewriteDriver.h> | ||
|
|
||
| #include <cstdint> | ||
| #include <utility> | ||
|
|
||
| namespace mlir::qco { | ||
|
|
||
| #define GEN_PASS_DEF_DECOMPOSEMULTICONTROLLED | ||
| #include "mlir/Dialect/QCO/Transforms/Passes.h.inc" | ||
|
|
||
| namespace { | ||
|
|
||
| /** | ||
| * @brief Decomposes a multi-controlled Pauli-X or Pauli-Z gate into elementary | ||
| * one- and two-qubit gates. | ||
| * | ||
| * @details Matches `qco.ctrl` with a single `qco.x` or `qco.z` body when the | ||
| * control count is at least `minControls_` (and at least two, as enforced by | ||
| * the pass). Single-control `CX`/`CZ` and other gates are left unchanged. | ||
| */ | ||
| struct DecomposeMultiControlledPauliPattern final : OpRewritePattern<CtrlOp> { | ||
| explicit DecomposeMultiControlledPauliPattern(MLIRContext* context, | ||
| uint64_t minControls) | ||
| : OpRewritePattern<CtrlOp>(context), minControls_(minControls) {} | ||
|
|
||
| LogicalResult matchAndRewrite(CtrlOp op, | ||
| PatternRewriter& rewriter) const override { | ||
| if (op.getNumControls() < minControls_ || op.getNumTargets() != 1) { | ||
| return failure(); | ||
| } | ||
| auto inner = utils::getSoleBodyUnitary<UnitaryOpInterface>(*op.getBody()); | ||
| if (!inner) { | ||
| return failure(); | ||
| } | ||
|
|
||
| rewriter.setInsertionPoint(op); | ||
| const auto controls = op.getControlsIn(); | ||
| const auto target = op.getInputTarget(0); | ||
| const auto loc = op.getLoc(); | ||
|
|
||
| if (isa<XOp>(inner.getOperation())) { | ||
| rewriter.replaceOp( | ||
| op, decomposition::synthesizeMcx(rewriter, loc, controls, target)); | ||
| return success(); | ||
| } | ||
| if (isa<ZOp>(inner.getOperation())) { | ||
| rewriter.replaceOp( | ||
| op, decomposition::synthesizeMcz(rewriter, loc, controls, target)); | ||
| return success(); | ||
| } | ||
| return failure(); | ||
| } | ||
|
|
||
| private: | ||
| uint64_t minControls_; | ||
| }; | ||
|
|
||
| /** | ||
| * @brief Pass that decomposes multi-controlled X and Z gates into elementary | ||
| * gates. | ||
| */ | ||
| struct DecomposeMultiControlled final | ||
| : impl::DecomposeMultiControlledBase<DecomposeMultiControlled> { | ||
| using DecomposeMultiControlledBase::DecomposeMultiControlledBase; | ||
|
|
||
| protected: | ||
| void runOnOperation() override { | ||
| if (minControls < 2) { | ||
| getOperation().emitError() | ||
| << "decompose-multi-controlled requires min-controls >= 2"; | ||
| signalPassFailure(); | ||
| return; | ||
| } | ||
|
|
||
| RewritePatternSet patterns(&getContext()); | ||
| patterns.add<DecomposeMultiControlledPauliPattern>(&getContext(), | ||
| minControls); | ||
|
|
||
| if (failed(applyPatternsGreedily(getOperation(), std::move(patterns)))) { | ||
| signalPassFailure(); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| } // namespace | ||
|
|
||
| } // namespace mlir::qco |
Uh oh!
There was an error while loading. Please reload this page.