From 52727bcb0a6b47378c765b9ee11a97bcccb3952b Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Fri, 12 Jun 2026 12:47:47 +0200 Subject: [PATCH 01/18] =?UTF-8?q?=E2=9C=A8=20Enhance=20QCO=20operations=20?= =?UTF-8?q?with=20new=20merging=20patterns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Dialect/QCO/QCOUtils.h | 327 ++++++++++++++++-- .../QCO/IR/Operations/StandardGates/POp.cpp | 15 +- .../QCO/IR/Operations/StandardGates/ROp.cpp | 15 +- .../QCO/IR/Operations/StandardGates/RZOp.cpp | 15 +- .../QCO/IR/Operations/StandardGates/SOp.cpp | 17 +- .../QCO/IR/Operations/StandardGates/SdgOp.cpp | 17 +- .../QCO/IR/Operations/StandardGates/TOp.cpp | 17 +- .../QCO/IR/Operations/StandardGates/TdgOp.cpp | 17 +- .../Operations/StandardGates/XXMinusYYOp.cpp | 43 +-- .../Operations/StandardGates/XXPlusYYOp.cpp | 43 +-- mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp | 137 +++++--- mlir/unittests/programs/qco_programs.cpp | 118 +++++++ mlir/unittests/programs/qco_programs.h | 62 ++++ 13 files changed, 690 insertions(+), 153 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index e2ec9740ba..eacab73f8c 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -10,12 +10,188 @@ #pragma once +#include "mlir/Dialect/QCO/IR/QCOOps.h" + +#include #include +#include #include #include +#include + +#include namespace mlir::qco { +/** + * @brief Check whether two parameter values are equal, using tolerance for + * constants. + * + * @param lhs The first parameter value. + * @param rhs The second parameter value. + * @return true if the values match. + */ +static inline bool valuesMatchWithinTolerance(Value lhs, Value rhs) { + const auto lhsVal = utils::valueToDouble(lhs); + const auto rhsVal = utils::valueToDouble(rhs); + if (lhsVal.has_value() && rhsVal.has_value()) { + return std::abs(*lhsVal - *rhsVal) <= utils::TOLERANCE; + } + return lhs == rhs; +} + +/** + * @brief Sum two floating-point parameter values, folding constants when + * possible. + * + * @param rewriter The pattern rewriter. + * @param loc The location for newly created operations. + * @param lhs The first summand. + * @param rhs The second summand. + * @return Value The sum, as a constant or `arith.addf` result. + */ +static inline Value addFloatParameters(PatternRewriter& rewriter, Location loc, + Value lhs, Value rhs) { + const auto lhsVal = utils::valueToDouble(lhs); + const auto rhsVal = utils::valueToDouble(rhs); + if (lhsVal.has_value() && rhsVal.has_value()) { + return utils::constantFromScalar(rewriter, loc, *lhsVal + *rhsVal); + } + return arith::AddFOp::create(rewriter, loc, lhs, rhs).getResult(); +} + +/** + * @brief Find a same-type partner operation reachable through `ctrl` hops on a + * control wire. + * + * @details + * Returns failure when no partner exists or when the partner is directly + * adjacent on the same wire (zero `ctrl` hops). + * + * @tparam OpType The type of the operation to be merged. + * @param op The first operation instance. + * @return FailureOr The partner operation, or failure. + */ +template +static FailureOr findPartnerThroughCtrlControlChain(OpType op) { + Value v = op->getResult(0); + if (!llvm::isa(v.getType())) { + return failure(); + } + + unsigned hops = 0; + while (v.hasOneUse()) { + Operation* user = *v.getUsers().begin(); + if (auto next = llvm::dyn_cast(user); + next && next->getOperand(0) == v) { + if (hops == 0) { + return failure(); + } + return next; + } + auto ctrl = llvm::dyn_cast(user); + if (!ctrl || !llvm::is_contained(ctrl.getControlsIn(), v)) { + return failure(); + } + v = ctrl.getOutputForInput(v); + ++hops; + } + return failure(); +} + +/** + * @brief Shared implementation for merging two-target, one-parameter + * operations. + * + * @details + * When @p swappedTargets is false, the successor must consume the same target + * wires in the same order. When true, the successor may consume swapped + * targets. The parameter at operand index 2 is summed. + * + * @tparam OpType The type of the operation to be merged. + * @param op The first operation instance. + * @param rewriter The pattern rewriter. + * @param swappedTargets Whether the successor consumes swapped target wires. + * @return LogicalResult Success or failure of the merge. + */ +template +static LogicalResult mergeTwoTargetOneParameterImpl(OpType op, + PatternRewriter& rewriter, + bool swappedTargets) { + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); + if (!nextOp) { + return failure(); + } + + if (swappedTargets) { + if (op.getOutputQubit(0) != nextOp.getInputQubit(1) || + op.getOutputQubit(1) != nextOp.getInputQubit(0)) { + return failure(); + } + } else if (op.getOutputQubit(1) != nextOp.getInputQubit(1)) { + return failure(); + } + + auto newParameter = addFloatParameters( + rewriter, op.getLoc(), op.getOperand(2), nextOp.getOperand(2)); + op->setOperand(2, newParameter); + if (swappedTargets) { + rewriter.replaceOp(nextOp, {op.getOutputQubit(1), op.getOutputQubit(0)}); + } else { + rewriter.replaceOp(nextOp, op.getResults()); + } + return success(); +} + +/** + * @brief Shared implementation for merging two-target, two-parameter + * operations. + * + * @details + * Requires matching secondary parameters (e.g., `beta` for `XXPlusYYOp` and + * `XXMinusYYOp`). The primary parameter (`theta`) at operand index 2 is summed. + * Wire-order requirements are the same as + * @ref mergeTwoTargetOneParameterImpl. + * + * @tparam OpType The type of the operation to be merged. + * @param op The first operation instance. + * @param rewriter The pattern rewriter. + * @param swappedTargets Whether the successor consumes swapped target wires. + * @return LogicalResult Success or failure of the merge. + */ +template +static LogicalResult mergeTwoTargetTwoParameterImpl(OpType op, + PatternRewriter& rewriter, + bool swappedTargets) { + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); + if (!nextOp) { + return failure(); + } + + if (swappedTargets) { + if (op.getOutputQubit(0) != nextOp.getInputQubit(1) || + op.getOutputQubit(1) != nextOp.getInputQubit(0)) { + return failure(); + } + } else if (op.getOutputQubit(1) != nextOp.getInputQubit(1)) { + return failure(); + } + + if (!valuesMatchWithinTolerance(op.getBeta(), nextOp.getBeta())) { + return failure(); + } + + auto newTheta = addFloatParameters(rewriter, op.getLoc(), op.getOperand(2), + nextOp.getOperand(2)); + op->setOperand(2, newTheta); + if (swappedTargets) { + rewriter.replaceOp(nextOp, {op.getOutputQubit(1), op.getOutputQubit(0)}); + } else { + rewriter.replaceOp(nextOp, op.getResults()); + } + return success(); +} + /** * @brief Remove a pair of inverse one-target, zero-parameter operations * @@ -153,28 +329,27 @@ LogicalResult mergeOneTargetZeroParameter(OpType op, */ template LogicalResult mergeOneTargetOneParameter(OpType op, PatternRewriter& rewriter) { - // Check if the successor is the same operation auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } // Compute and set the new parameter - auto newParameter = arith::AddFOp::create( + auto newParameter = addFloatParameters( rewriter, op.getLoc(), op.getOperand(1), nextOp.getOperand(1)); - op->setOperand(1, newParameter.getResult()); + op->setOperand(1, newParameter); // Replace the second operation with the result of the first operation rewriter.replaceOp(nextOp, op.getResult()); - return success(); } /** - * @brief Merge two compatible two-target, one-parameter operations + * @brief Merge two compatible one-target, two-parameter operations * * @details - * The new parameter is computed as the sum of the two original parameters. + * Requires matching secondary parameters (e.g., `phi` for `ROp`). The primary + * parameter (`theta`) is summed. * * @tparam OpType The type of the operation to be merged. * @param op The operation instance. @@ -182,29 +357,94 @@ LogicalResult mergeOneTargetOneParameter(OpType op, PatternRewriter& rewriter) { * @return LogicalResult Success or failure of the merge. */ template -LogicalResult mergeTwoTargetOneParameter(OpType op, PatternRewriter& rewriter) { - // Check if the successor is the same operation +LogicalResult mergeOneTargetTwoParameter(OpType op, PatternRewriter& rewriter) { auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } - // Confirm operations act on the same qubits - if (op.getOutputQubit(1) != nextOp.getInputQubit(1)) { + if (!valuesMatchWithinTolerance(op.getPhi(), nextOp.getPhi())) { return failure(); } - // Compute and set the new parameter - auto newParameter = arith::AddFOp::create( - rewriter, op.getLoc(), op.getOperand(2), nextOp.getOperand(2)); - op->setOperand(2, newParameter.getResult()); + auto newTheta = addFloatParameters(rewriter, op.getLoc(), op.getOperand(1), + nextOp.getOperand(1)); + op->setOperand(1, newTheta); + rewriter.replaceOp(nextOp, op.getResult()); + return success(); +} - // Replace the second operation with the result of the first operation - rewriter.replaceOp(nextOp, op.getResults()); +/** + * @brief Merge Z-diagonal one-target, one-parameter gate angles through `ctrl` + * control wires. + * + * @tparam OpType The type of the Z-diagonal operation to be merged. + * @param op The operation instance. + * @param rewriter The pattern rewriter. + * @return LogicalResult Success or failure of the merge. + */ +template +LogicalResult +mergeOneTargetOneParameterThroughCtrlControlChain(OpType op, + PatternRewriter& rewriter) { + auto partner = findPartnerThroughCtrlControlChain(op); + if (failed(partner)) { + return failure(); + } + + const Location loc = op.getLoc(); + rewriter.setInsertionPoint(op); + const Value newTheta = + addFloatParameters(rewriter, loc, op.getTheta(), partner->getTheta()); + rewriter.modifyOpInPlace(op, [&] { op.getThetaMutable().assign(newTheta); }); + rewriter.replaceOp(*partner, partner->getOperand(0)); + return success(); +} + +/** + * @brief Merge Z-diagonal one-target, zero-parameter gates through a chain of + * `ctrl` hops on control wires. + * + * @details + * Replaces `op; …; op` on a control wire with `square; …` (e.g. `s; ctrl; s` → + * `z; ctrl`). + * + * @tparam SquareOpType Result of squaring the gate (e.g. `ZOp` for `SOp`). + * @tparam OpType The Z-diagonal operation to be merged. + * @param op The operation instance. + * @param rewriter The pattern rewriter. + * @return LogicalResult Success or failure of the merge. + */ +template +LogicalResult +mergeOneTargetZeroParameterThroughCtrlControlChain(OpType op, + PatternRewriter& rewriter) { + auto partner = findPartnerThroughCtrlControlChain(op); + if (failed(partner)) { + return failure(); + } + rewriter.replaceOpWithNewOp(op, op.getInputQubit(0)); + rewriter.replaceOp(*partner, partner->getInputQubit(0)); return success(); } +/** + * @brief Merge two compatible two-target, one-parameter operations + * + * @details + * The new parameter is computed as the sum of the two original parameters. + * + * @tparam OpType The type of the operation to be merged. + * @param op The operation instance. + * @param rewriter The pattern rewriter. + * @return LogicalResult Success or failure of the merge. + */ +template +LogicalResult mergeTwoTargetOneParameter(OpType op, PatternRewriter& rewriter) { + return mergeTwoTargetOneParameterImpl(op, rewriter, false); +} + /** * @brief Merge two compatible two-target, one-parameter operations where the * second operation consumes the outputs with swapped targets. @@ -223,27 +463,44 @@ template LogicalResult mergeTwoTargetOneParameterWithSwappedTargets(OpType op, PatternRewriter& rewriter) { - // Check if the successor is the same operation - auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); - if (!nextOp) { - return failure(); - } - - // Confirm operations act on the same qubits but with swapped targets - if (op.getOutputQubit(0) != nextOp.getInputQubit(1) || - op.getOutputQubit(1) != nextOp.getInputQubit(0)) { - return failure(); - } - - // Compute and set the new parameter on the first operation - auto newParameter = arith::AddFOp::create( - rewriter, op.getLoc(), op.getOperand(2), nextOp.getOperand(2)); - op->setOperand(2, newParameter.getResult()); + return mergeTwoTargetOneParameterImpl(op, rewriter, true); +} - // nextOp results correspond to swapped operands, so swap replacements too - rewriter.replaceOp(nextOp, {op.getOutputQubit(1), op.getOutputQubit(0)}); +/** + * @brief Merge two compatible two-target, two-parameter operations + * + * @details + * Requires matching secondary parameters (e.g., `beta` for `XXPlusYYOp` and + * `XXMinusYYOp`). The primary parameter (`theta`) is summed. + * + * @tparam OpType The type of the operation to be merged. + * @param op The operation instance. + * @param rewriter The pattern rewriter. + * @return LogicalResult Success or failure of the merge. + */ +template +LogicalResult mergeTwoTargetTwoParameter(OpType op, PatternRewriter& rewriter) { + return mergeTwoTargetTwoParameterImpl(op, rewriter, false); +} - return success(); +/** + * @brief Merge two compatible two-target, two-parameter operations + * + * @details + * Requires matching secondary parameters (e.g., `beta` for `XXPlusYYOp` and + * `XXMinusYYOp`). The primary parameter (`theta`) is summed. The second + * operation may consume swapped targets. + * + * @tparam OpType The type of the operation to be merged. + * @param op The operation instance. + * @param rewriter The pattern rewriter. + * @return LogicalResult Success or failure of the merge. + */ +template +LogicalResult +mergeTwoTargetTwoParameterWithSwappedTargets(OpType op, + PatternRewriter& rewriter) { + return mergeTwoTargetTwoParameterImpl(op, rewriter, true); } } // namespace mlir::qco diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp index fa7c5c8e22..c7ff9db1a3 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp @@ -43,6 +43,19 @@ struct MergeSubsequentP final : OpRewritePattern { } }; +/** + * @brief Merge P operations separated only by `ctrl` hops on a control wire + * by adding their angles. + */ +struct MergePThroughCtrlControlChain final : OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(POp op, + PatternRewriter& rewriter) const override { + return mergeOneTargetOneParameterThroughCtrlControlChain(op, rewriter); + } +}; + } // namespace void POp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, @@ -62,7 +75,7 @@ OpFoldResult POp::fold(FoldAdaptor /*adaptor*/) { void POp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional POp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp index 1eca6542b0..5b50925dcb 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp @@ -9,6 +9,7 @@ */ #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" @@ -64,6 +65,18 @@ struct ReplaceRWithRY final : OpRewritePattern { } }; +/** + * @brief Merge subsequent R operations on the same qubit with matching `phi`. + */ +struct MergeSubsequentR final : OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(ROp op, + PatternRewriter& rewriter) const override { + return mergeOneTargetTwoParameter(op, rewriter); + } +}; + } // namespace void ROp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, @@ -77,7 +90,7 @@ void ROp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, void ROp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional ROp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp index 288e9f0d0f..e62362d0b8 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp @@ -43,6 +43,19 @@ struct MergeSubsequentRZ final : OpRewritePattern { } }; +/** + * @brief Merge RZ operations separated only by `ctrl` hops on a control wire + * by adding their angles. + */ +struct MergeRZThroughCtrlControlChain final : OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(RZOp op, + PatternRewriter& rewriter) const override { + return mergeOneTargetOneParameterThroughCtrlControlChain(op, rewriter); + } +}; + } // namespace void RZOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, @@ -62,7 +75,7 @@ OpFoldResult RZOp::fold(FoldAdaptor /*adaptor*/) { void RZOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional RZOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp index 768bddfcc0..5b8854dea2 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp @@ -48,11 +48,26 @@ struct MergeSubsequentS final : OpRewritePattern { } }; +/** + * @brief Merge S operations separated only by `ctrl` hops on a control wire + * into a Z operation. + */ +struct MergeSThroughCtrlControlChain final : OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(SOp op, + PatternRewriter& rewriter) const override { + return mergeOneTargetZeroParameterThroughCtrlControlChain(op, + rewriter); + } +}; + } // namespace void SOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add( + context); } Matrix2x2 SOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp index 1a9110d312..693f3f6453 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp @@ -48,11 +48,26 @@ struct MergeSubsequentSdg final : OpRewritePattern { } }; +/** + * @brief Merge Sdg operations separated only by `ctrl` hops on a control wire + * into a Z operation. + */ +struct MergeSdgThroughCtrlControlChain final : OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(SdgOp op, + PatternRewriter& rewriter) const override { + return mergeOneTargetZeroParameterThroughCtrlControlChain(op, + rewriter); + } +}; + } // namespace void SdgOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } Matrix2x2 SdgOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp index 8e9a90e582..7937e0fe6f 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp @@ -49,11 +49,26 @@ struct MergeSubsequentT final : OpRewritePattern { } }; +/** + * @brief Merge T operations separated only by `ctrl` hops on a control wire + * into an S operation. + */ +struct MergeTThroughCtrlControlChain final : OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(TOp op, + PatternRewriter& rewriter) const override { + return mergeOneTargetZeroParameterThroughCtrlControlChain(op, + rewriter); + } +}; + } // namespace void TOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add( + context); } Matrix2x2 TOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp index ca093fd81d..567f1a34b4 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp @@ -50,11 +50,26 @@ struct MergeSubsequentTdg final : OpRewritePattern { } }; +/** + * @brief Merge Tdg operations separated only by `ctrl` hops on a control wire + * into an Sdg operation. + */ +struct MergeTdgThroughCtrlControlChain final : OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(TdgOp op, + PatternRewriter& rewriter) const override { + return mergeOneTargetZeroParameterThroughCtrlControlChain(op, + rewriter); + } +}; + } // namespace void TdgOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } Matrix2x2 TdgOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp index 26d5966d11..c1a3278e0e 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp @@ -9,6 +9,7 @@ */ #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" @@ -40,36 +41,20 @@ struct MergeSubsequentXXMinusYY final : OpRewritePattern { LogicalResult matchAndRewrite(XXMinusYYOp op, PatternRewriter& rewriter) const override { - // Check if the successor is the same operation - auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); - if (!nextOp) { - return failure(); - } - - // Confirm operations act on the same qubits - if (op.getOutputQubit(1) != nextOp.getInputQubit(1)) { - return failure(); - } - - // Confirm betas are equal - const auto beta = valueToDouble(op.getBeta()); - const auto nextBeta = valueToDouble(nextOp.getBeta()); - if (beta && nextBeta) { - if (std::abs(*beta - *nextBeta) > TOLERANCE) { - return failure(); - } - } else if (op.getBeta() != nextOp.getBeta()) { - return failure(); - } + return mergeTwoTargetTwoParameter(op, rewriter); + } +}; - // Compute and set new theta, which has index 2 - auto newParameter = arith::AddFOp::create( - rewriter, op.getLoc(), op.getOperand(2), nextOp.getOperand(2)); - op->setOperand(2, newParameter.getResult()); +/** + * @brief Merge XXMinusYY operations with swapped target wires by adding their + * thetas. + */ +struct MergeSwappedTargetsXXMinusYY final : OpRewritePattern { + using OpRewritePattern::OpRewritePattern; - // Replace the second operation with the result of the first operation - rewriter.replaceOp(nextOp, op.getResults()); - return success(); + LogicalResult matchAndRewrite(XXMinusYYOp op, + PatternRewriter& rewriter) const override { + return mergeTwoTargetTwoParameterWithSwappedTargets(op, rewriter); } }; @@ -98,7 +83,7 @@ LogicalResult XXMinusYYOp::fold(FoldAdaptor /*adaptor*/, void XXMinusYYOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional XXMinusYYOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp index 6b4c5f0b9e..9c7d21b498 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp @@ -9,6 +9,7 @@ */ #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" @@ -40,36 +41,20 @@ struct MergeSubsequentXXPlusYY final : OpRewritePattern { LogicalResult matchAndRewrite(XXPlusYYOp op, PatternRewriter& rewriter) const override { - // Check if the successor is the same operation - auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); - if (!nextOp) { - return failure(); - } - - // Confirm operations act on the same qubits - if (op.getOutputQubit(1) != nextOp.getInputQubit(1)) { - return failure(); - } - - // Confirm betas are equal - const auto beta = valueToDouble(op.getBeta()); - const auto nextBeta = valueToDouble(nextOp.getBeta()); - if (beta && nextBeta) { - if (std::abs(*beta - *nextBeta) > TOLERANCE) { - return failure(); - } - } else if (op.getBeta() != nextOp.getBeta()) { - return failure(); - } + return mergeTwoTargetTwoParameter(op, rewriter); + } +}; - // Compute and set new theta, which has index 2 - auto newParameter = arith::AddFOp::create( - rewriter, op.getLoc(), op.getOperand(2), nextOp.getOperand(2)); - op->setOperand(2, newParameter.getResult()); +/** + * @brief Merge XXPlusYY operations with swapped target wires by adding their + * thetas. + */ +struct MergeSwappedTargetsXXPlusYY final : OpRewritePattern { + using OpRewritePattern::OpRewritePattern; - // Replace the second operation with the result of the first operation - rewriter.replaceOp(nextOp, op.getResults()); - return success(); + LogicalResult matchAndRewrite(XXPlusYYOp op, + PatternRewriter& rewriter) const override { + return mergeTwoTargetTwoParameterWithSwappedTargets(op, rewriter); } }; @@ -98,7 +83,7 @@ LogicalResult XXPlusYYOp::fold(FoldAdaptor /*adaptor*/, void XXPlusYYOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional XXPlusYYOp::getUnitaryMatrix() { diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp index 883c7d32d2..0bf3b68ec7 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp @@ -453,7 +453,10 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(inverseMultipleControlledP), MQT_NAMED_BUILDER(multipleControlledP)}, QCOTestCase{"TwoPOppositePhase", MQT_NAMED_BUILDER(twoPOppositePhase), - MQT_NAMED_BUILDER(emptyQCO)})); + MQT_NAMED_BUILDER(emptyQCO)}, + QCOTestCase{"TwoPThroughCtrlControlChain", + MQT_NAMED_BUILDER(twoPThroughCtrlControlChain), + MQT_NAMED_BUILDER(twoPThroughCtrlControlChainMerged)})); /// @} /// \name QCO/Operations/StandardGates/ROp.cpp @@ -479,7 +482,8 @@ INSTANTIATE_TEST_SUITE_P( QCOTestCase{"CanonicalizeRToRx", MQT_NAMED_BUILDER(canonicalizeRToRx), MQT_NAMED_BUILDER(rx)}, QCOTestCase{"CanonicalizeRToRy", MQT_NAMED_BUILDER(canonicalizeRToRy), - MQT_NAMED_BUILDER(ry)})); + MQT_NAMED_BUILDER(ry)}, + QCOTestCase{"TwoR", MQT_NAMED_BUILDER(twoR), MQT_NAMED_BUILDER(r)})); /// @} /// \name QCO/Operations/StandardGates/RxOp.cpp @@ -626,7 +630,17 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(inverseMultipleControlledRz), MQT_NAMED_BUILDER(multipleControlledRz)}, QCOTestCase{"TwoRZOppositePhase", MQT_NAMED_BUILDER(twoRzOppositePhase), - MQT_NAMED_BUILDER(emptyQCO)})); + MQT_NAMED_BUILDER(emptyQCO)}, + QCOTestCase{"TwoRZThroughCtrlControlChain", + MQT_NAMED_BUILDER(twoRzThroughCtrlControlChain), + MQT_NAMED_BUILDER(twoRzThroughCtrlControlChainMerged)}, + QCOTestCase{ + "TwoRZThroughNestedCtrlControlChain", + MQT_NAMED_BUILDER(twoRzThroughNestedCtrlControlChain), + MQT_NAMED_BUILDER(twoRzThroughNestedCtrlControlChainMerged)}, + QCOTestCase{"TwoRZSplitAcrossCtrlTargetWires", + MQT_NAMED_BUILDER(twoRzSplitAcrossCtrlTargetWires), + MQT_NAMED_BUILDER(twoRzSplitAcrossCtrlTargetWires)})); /// @} /// \name QCO/Operations/StandardGates/RzxOp.cpp @@ -715,36 +729,41 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(multipleControlledSdg)}, QCOTestCase{"SThenSdg", MQT_NAMED_BUILDER(sThenSdg), MQT_NAMED_BUILDER(emptyQCO)}, - QCOTestCase{"TwoS", MQT_NAMED_BUILDER(twoS), MQT_NAMED_BUILDER(z)})); + QCOTestCase{"TwoS", MQT_NAMED_BUILDER(twoS), MQT_NAMED_BUILDER(z)}, + QCOTestCase{"TwoSThroughCtrlControlChain", + MQT_NAMED_BUILDER(twoSThroughCtrlControlChain), + MQT_NAMED_BUILDER(twoSThroughCtrlControlChainMerged)})); /// @} /// \name QCO/Operations/StandardGates/SdgOp.cpp /// @{ INSTANTIATE_TEST_SUITE_P( QCOSdgOpTest, QCOTest, - testing::Values(QCOTestCase{"Sdg", MQT_NAMED_BUILDER(sdg), - MQT_NAMED_BUILDER(sdg)}, - QCOTestCase{"SingleControlledSdg", - MQT_NAMED_BUILDER(singleControlledSdg), - MQT_NAMED_BUILDER(singleControlledSdg)}, - QCOTestCase{"MultipleControlledSdg", - MQT_NAMED_BUILDER(multipleControlledSdg), - MQT_NAMED_BUILDER(multipleControlledSdg)}, - QCOTestCase{"NestedControlledSdg", - MQT_NAMED_BUILDER(nestedControlledSdg), - MQT_NAMED_BUILDER(multipleControlledSdg)}, - QCOTestCase{"TrivialControlledSdg", - MQT_NAMED_BUILDER(trivialControlledSdg), - MQT_NAMED_BUILDER(sdg)}, - QCOTestCase{"InverseSdg", MQT_NAMED_BUILDER(inverseSdg), - MQT_NAMED_BUILDER(s)}, - QCOTestCase{"InverseMultipleControlledSdg", - MQT_NAMED_BUILDER(inverseMultipleControlledSdg), - MQT_NAMED_BUILDER(multipleControlledS)}, - QCOTestCase{"SdgThenS", MQT_NAMED_BUILDER(sdgThenS), - MQT_NAMED_BUILDER(emptyQCO)}, - QCOTestCase{"TwoSdg", MQT_NAMED_BUILDER(twoSdg), - MQT_NAMED_BUILDER(z)})); + testing::Values( + QCOTestCase{"Sdg", MQT_NAMED_BUILDER(sdg), MQT_NAMED_BUILDER(sdg)}, + QCOTestCase{"SingleControlledSdg", + MQT_NAMED_BUILDER(singleControlledSdg), + MQT_NAMED_BUILDER(singleControlledSdg)}, + QCOTestCase{"MultipleControlledSdg", + MQT_NAMED_BUILDER(multipleControlledSdg), + MQT_NAMED_BUILDER(multipleControlledSdg)}, + QCOTestCase{"NestedControlledSdg", + MQT_NAMED_BUILDER(nestedControlledSdg), + MQT_NAMED_BUILDER(multipleControlledSdg)}, + QCOTestCase{"TrivialControlledSdg", + MQT_NAMED_BUILDER(trivialControlledSdg), + MQT_NAMED_BUILDER(sdg)}, + QCOTestCase{"InverseSdg", MQT_NAMED_BUILDER(inverseSdg), + MQT_NAMED_BUILDER(s)}, + QCOTestCase{"InverseMultipleControlledSdg", + MQT_NAMED_BUILDER(inverseMultipleControlledSdg), + MQT_NAMED_BUILDER(multipleControlledS)}, + QCOTestCase{"SdgThenS", MQT_NAMED_BUILDER(sdgThenS), + MQT_NAMED_BUILDER(emptyQCO)}, + QCOTestCase{"TwoSdg", MQT_NAMED_BUILDER(twoSdg), MQT_NAMED_BUILDER(z)}, + QCOTestCase{"TwoSdgThroughCtrlControlChain", + MQT_NAMED_BUILDER(twoSdgThroughCtrlControlChain), + MQT_NAMED_BUILDER(twoSdgThroughCtrlControlChainMerged)})); /// @} /// \name QCO/Operations/StandardGates/SwapOp.cpp @@ -854,36 +873,42 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(multipleControlledTdg)}, QCOTestCase{"TThenTdg", MQT_NAMED_BUILDER(tThenTdg), MQT_NAMED_BUILDER(emptyQCO)}, - QCOTestCase{"TwoT", MQT_NAMED_BUILDER(twoT), MQT_NAMED_BUILDER(s)})); + QCOTestCase{"TwoT", MQT_NAMED_BUILDER(twoT), MQT_NAMED_BUILDER(s)}, + QCOTestCase{"TwoTThroughCtrlControlChain", + MQT_NAMED_BUILDER(twoTThroughCtrlControlChain), + MQT_NAMED_BUILDER(twoTThroughCtrlControlChainMerged)})); /// @} /// \name QCO/Operations/StandardGates/TdgOp.cpp /// @{ INSTANTIATE_TEST_SUITE_P( QCOTdgOpTest, QCOTest, - testing::Values(QCOTestCase{"Tdg", MQT_NAMED_BUILDER(tdg), - MQT_NAMED_BUILDER(tdg)}, - QCOTestCase{"SingleControlledTdg", - MQT_NAMED_BUILDER(singleControlledTdg), - MQT_NAMED_BUILDER(singleControlledTdg)}, - QCOTestCase{"MultipleControlledTdg", - MQT_NAMED_BUILDER(multipleControlledTdg), - MQT_NAMED_BUILDER(multipleControlledTdg)}, - QCOTestCase{"NestedControlledTdg", - MQT_NAMED_BUILDER(nestedControlledTdg), - MQT_NAMED_BUILDER(multipleControlledTdg)}, - QCOTestCase{"TrivialControlledTdg", - MQT_NAMED_BUILDER(trivialControlledTdg), - MQT_NAMED_BUILDER(tdg)}, - QCOTestCase{"InverseTdg", MQT_NAMED_BUILDER(inverseTdg), - MQT_NAMED_BUILDER(t_)}, - QCOTestCase{"InverseMultipleControlledTdg", - MQT_NAMED_BUILDER(inverseMultipleControlledTdg), - MQT_NAMED_BUILDER(multipleControlledT)}, - QCOTestCase{"TdgThenS", MQT_NAMED_BUILDER(tdgThenT), - MQT_NAMED_BUILDER(emptyQCO)}, - QCOTestCase{"TwoTdg", MQT_NAMED_BUILDER(twoTdg), - MQT_NAMED_BUILDER(sdg)})); + testing::Values( + QCOTestCase{"Tdg", MQT_NAMED_BUILDER(tdg), MQT_NAMED_BUILDER(tdg)}, + QCOTestCase{"SingleControlledTdg", + MQT_NAMED_BUILDER(singleControlledTdg), + MQT_NAMED_BUILDER(singleControlledTdg)}, + QCOTestCase{"MultipleControlledTdg", + MQT_NAMED_BUILDER(multipleControlledTdg), + MQT_NAMED_BUILDER(multipleControlledTdg)}, + QCOTestCase{"NestedControlledTdg", + MQT_NAMED_BUILDER(nestedControlledTdg), + MQT_NAMED_BUILDER(multipleControlledTdg)}, + QCOTestCase{"TrivialControlledTdg", + MQT_NAMED_BUILDER(trivialControlledTdg), + MQT_NAMED_BUILDER(tdg)}, + QCOTestCase{"InverseTdg", MQT_NAMED_BUILDER(inverseTdg), + MQT_NAMED_BUILDER(t_)}, + QCOTestCase{"InverseMultipleControlledTdg", + MQT_NAMED_BUILDER(inverseMultipleControlledTdg), + MQT_NAMED_BUILDER(multipleControlledT)}, + QCOTestCase{"TdgThenS", MQT_NAMED_BUILDER(tdgThenT), + MQT_NAMED_BUILDER(emptyQCO)}, + QCOTestCase{"TwoTdg", MQT_NAMED_BUILDER(twoTdg), + MQT_NAMED_BUILDER(sdg)}, + QCOTestCase{"TwoTdgThroughCtrlControlChain", + MQT_NAMED_BUILDER(twoTdgThroughCtrlControlChain), + MQT_NAMED_BUILDER(twoTdgThroughCtrlControlChainMerged)})); /// @} /// \name QCO/Operations/StandardGates/U2Op.cpp @@ -999,7 +1024,10 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(multipleControlledXxMinusYY)}, QCOTestCase{"TwoXXMinusYYOppositePhase", MQT_NAMED_BUILDER(twoXxMinusYYOppositePhase), - MQT_NAMED_BUILDER(emptyQCO)})); + MQT_NAMED_BUILDER(emptyQCO)}, + QCOTestCase{"TwoXXMinusYYSwappedTargets", + MQT_NAMED_BUILDER(twoXxMinusYYSwappedTargets), + MQT_NAMED_BUILDER(xxMinusYY)})); /// @} /// \name QCO/Operations/StandardGates/XxPlusYyOp.cpp @@ -1028,7 +1056,10 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(multipleControlledXxPlusYY)}, QCOTestCase{"TwoXXPlusYYOppositePhase", MQT_NAMED_BUILDER(twoXxPlusYYOppositePhase), - MQT_NAMED_BUILDER(emptyQCO)})); + MQT_NAMED_BUILDER(emptyQCO)}, + QCOTestCase{"TwoXXPlusYYSwappedTargets", + MQT_NAMED_BUILDER(twoXxPlusYYSwappedTargets), + MQT_NAMED_BUILDER(xxPlusYY)})); /// @} /// \name QCO/Operations/StandardGates/YOp.cpp diff --git a/mlir/unittests/programs/qco_programs.cpp b/mlir/unittests/programs/qco_programs.cpp index 523f071f8a..b443764a51 100644 --- a/mlir/unittests/programs/qco_programs.cpp +++ b/mlir/unittests/programs/qco_programs.cpp @@ -542,6 +542,19 @@ void twoS(QCOProgramBuilder& b) { q[0] = b.s(q[0]); } +void twoSThroughCtrlControlChain(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.s(q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); + q[0] = b.s(q[0]); +} + +void twoSThroughCtrlControlChainMerged(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.z(q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); +} + void sdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.sdg(q[0]); @@ -602,6 +615,19 @@ void twoSdg(QCOProgramBuilder& b) { q[0] = b.sdg(q[0]); } +void twoSdgThroughCtrlControlChain(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.sdg(q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); + q[0] = b.sdg(q[0]); +} + +void twoSdgThroughCtrlControlChainMerged(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.z(q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); +} + void t_(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.t(q[0]); @@ -661,6 +687,19 @@ void twoT(QCOProgramBuilder& b) { q[0] = b.t(q[0]); } +void twoTThroughCtrlControlChain(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.t(q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); + q[0] = b.t(q[0]); +} + +void twoTThroughCtrlControlChainMerged(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.s(q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); +} + void tdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.tdg(q[0]); @@ -721,6 +760,19 @@ void twoTdg(QCOProgramBuilder& b) { q[0] = b.tdg(q[0]); } +void twoTdgThroughCtrlControlChain(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.tdg(q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); + q[0] = b.tdg(q[0]); +} + +void twoTdgThroughCtrlControlChainMerged(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.sdg(q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); +} + void sx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.sx(q[0]); @@ -1015,6 +1067,41 @@ void twoRzOppositePhase(QCOProgramBuilder& b) { q[0] = b.rz(-0.789, q[0]); } +void twoRzThroughCtrlControlChain(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.rz(0.1, q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); + q[0] = b.rz(0.2, q[0]); +} + +void twoRzThroughCtrlControlChainMerged(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.rz(0.3, q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); +} + +void twoRzThroughNestedCtrlControlChain(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(3); + q[0] = b.rz(0.1, q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); + std::tie(q[0], q[2]) = b.cx(q[0], q[2]); + q[0] = b.rz(0.2, q[0]); +} + +void twoRzThroughNestedCtrlControlChainMerged(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(3); + q[0] = b.rz(0.3, q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); + std::tie(q[0], q[2]) = b.cx(q[0], q[2]); +} + +void twoRzSplitAcrossCtrlTargetWires(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.rz(0.1, q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); + q[1] = b.rz(0.2, q[1]); +} + void p(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.p(0.123, q[0]); @@ -1069,6 +1156,19 @@ void twoPOppositePhase(QCOProgramBuilder& b) { q[0] = b.p(-0.123, q[0]); } +void twoPThroughCtrlControlChain(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.p(0.1, q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); + q[0] = b.p(0.2, q[0]); +} + +void twoPThroughCtrlControlChainMerged(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.p(0.3, q[0]); + std::tie(q[0], q[1]) = b.cx(q[0], q[1]); +} + void r(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.r(0.123, 0.456, q[0]); @@ -1128,6 +1228,12 @@ void canonicalizeRToRy(QCOProgramBuilder& b) { q[0] = b.r(0.456, std::numbers::pi / 2, q[0]); } +void twoR(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(1); + q[0] = b.r(0.045, 0.456, q[0]); + q[0] = b.r(0.078, 0.456, q[0]); +} + void u2(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.u2(0.234, 0.567, q[0]); @@ -1851,6 +1957,12 @@ void twoXxPlusYYOppositePhase(QCOProgramBuilder& b) { std::tie(q[0], q[1]) = b.xx_plus_yy(-0.123, 0.456, q[0], q[1]); } +void twoXxPlusYYSwappedTargets(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + std::tie(q[0], q[1]) = b.xx_plus_yy(0.045, 0.456, q[0], q[1]); + std::tie(q[1], q[0]) = b.xx_plus_yy(0.078, 0.456, q[1], q[0]); +} + void xxMinusYY(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); b.xx_minus_yy(0.123, 0.456, q[0], q[1]); @@ -1909,6 +2021,12 @@ void twoXxMinusYYOppositePhase(QCOProgramBuilder& b) { std::tie(q[0], q[1]) = b.xx_minus_yy(-0.123, 0.456, q[0], q[1]); } +void twoXxMinusYYSwappedTargets(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + std::tie(q[0], q[1]) = b.xx_minus_yy(0.045, 0.456, q[0], q[1]); + std::tie(q[1], q[0]) = b.xx_minus_yy(0.078, 0.456, q[1], q[0]); +} + void barrier(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.barrier(q[0]); diff --git a/mlir/unittests/programs/qco_programs.h b/mlir/unittests/programs/qco_programs.h index f562cfff8a..aee96d205c 100644 --- a/mlir/unittests/programs/qco_programs.h +++ b/mlir/unittests/programs/qco_programs.h @@ -287,6 +287,13 @@ void sThenSdg(QCOProgramBuilder& b); /// Creates a circuit with two S gates in a row. void twoS(QCOProgramBuilder& b); +/// Creates a circuit with two S gates on a control wire separated by a `ctrl` +/// hop. +void twoSThroughCtrlControlChain(QCOProgramBuilder& b); + +/// Creates the merged Z form of @ref twoSThroughCtrlControlChain. +void twoSThroughCtrlControlChainMerged(QCOProgramBuilder& b); + // --- SdgOp ---------------------------------------------------------------- // /// Creates a circuit with just an Sdg gate. @@ -316,6 +323,13 @@ void sdgThenS(QCOProgramBuilder& b); /// Creates a circuit with two Sdg gates in a row. void twoSdg(QCOProgramBuilder& b); +/// Creates a circuit with two Sdg gates on a control wire separated by a `ctrl` +/// hop. +void twoSdgThroughCtrlControlChain(QCOProgramBuilder& b); + +/// Creates the merged Z form of @ref twoSdgThroughCtrlControlChain. +void twoSdgThroughCtrlControlChainMerged(QCOProgramBuilder& b); + // --- TOp ------------------------------------------------------------------ // /// Creates a circuit with just a T gate. @@ -345,6 +359,13 @@ void tThenTdg(QCOProgramBuilder& b); /// Creates a circuit with two T gates in a row. void twoT(QCOProgramBuilder& b); +/// Creates a circuit with two T gates on a control wire separated by a `ctrl` +/// hop. +void twoTThroughCtrlControlChain(QCOProgramBuilder& b); + +/// Creates the merged S form of @ref twoTThroughCtrlControlChain. +void twoTThroughCtrlControlChainMerged(QCOProgramBuilder& b); + // --- TdgOp ---------------------------------------------------------------- // /// Creates a circuit with just a Tdg gate. @@ -374,6 +395,13 @@ void tdgThenT(QCOProgramBuilder& b); /// Creates a circuit with two Tdg gates in a row. void twoTdg(QCOProgramBuilder& b); +/// Creates a circuit with two Tdg gates on a control wire separated by a `ctrl` +/// hop. +void twoTdgThroughCtrlControlChain(QCOProgramBuilder& b); + +/// Creates the merged Sdg form of @ref twoTdgThroughCtrlControlChain. +void twoTdgThroughCtrlControlChainMerged(QCOProgramBuilder& b); + // --- SXOp ----------------------------------------------------------------- // /// Creates a circuit with just an SX gate. @@ -517,6 +545,24 @@ void inverseMultipleControlledRz(QCOProgramBuilder& b); /// Creates a circuit with two RZ gates in a row with opposite phases. void twoRzOppositePhase(QCOProgramBuilder& b); +/// Creates a circuit with two RZ gates on a control wire separated by a `ctrl` +/// hop. +void twoRzThroughCtrlControlChain(QCOProgramBuilder& b); + +/// Creates the merged single-RZ form of @ref twoRzThroughCtrlControlChain. +void twoRzThroughCtrlControlChainMerged(QCOProgramBuilder& b); + +/// Creates a circuit with two RZ gates on a control wire separated by nested +/// `ctrl` hops. +void twoRzThroughNestedCtrlControlChain(QCOProgramBuilder& b); + +/// Creates the merged single-RZ form of @ref +/// twoRzThroughNestedCtrlControlChain. +void twoRzThroughNestedCtrlControlChainMerged(QCOProgramBuilder& b); + +/// Creates a circuit with RZ gates on control and target wires around `ctrl`. +void twoRzSplitAcrossCtrlTargetWires(QCOProgramBuilder& b); + // --- POp ------------------------------------------------------------------ // /// Creates a circuit with just a P gate. @@ -543,6 +589,13 @@ void inverseMultipleControlledP(QCOProgramBuilder& b); /// Creates a circuit with two P gates in a row with opposite phases. void twoPOppositePhase(QCOProgramBuilder& b); +/// Creates a circuit with two P gates on a control wire separated by a `ctrl` +/// hop. +void twoPThroughCtrlControlChain(QCOProgramBuilder& b); + +/// Creates the merged single-P form of @ref twoPThroughCtrlControlChain. +void twoPThroughCtrlControlChainMerged(QCOProgramBuilder& b); + // --- ROp ------------------------------------------------------------------ // /// Creates a circuit with just an R gate. @@ -572,6 +625,9 @@ void canonicalizeRToRx(QCOProgramBuilder& b); /// Creates a circuit with an R gate that can be canonicalized to an RY gate. void canonicalizeRToRy(QCOProgramBuilder& b); +/// Creates a circuit with two R gates in a row with the same `phi`. +void twoR(QCOProgramBuilder& b); + // --- U2Op ----------------------------------------------------------------- // /// Creates a circuit with just a U2 gate. @@ -915,6 +971,9 @@ void inverseMultipleControlledXxPlusYY(QCOProgramBuilder& b); /// Creates a circuit with two XXPlusYY gates in a row with opposite phases. void twoXxPlusYYOppositePhase(QCOProgramBuilder& b); +/// Creates a circuit with two XXPlusYY gates in a row with swapped targets. +void twoXxPlusYYSwappedTargets(QCOProgramBuilder& b); + // --- XXMinusYYOp ---------------------------------------------------------- // /// Creates a circuit with just an XXMinusYY gate. @@ -942,6 +1001,9 @@ void inverseMultipleControlledXxMinusYY(QCOProgramBuilder& b); /// Creates a circuit with two XXMinusYY gates in a row with opposite phases. void twoXxMinusYYOppositePhase(QCOProgramBuilder& b); +/// Creates a circuit with two XXMinusYY gates in a row with swapped targets. +void twoXxMinusYYSwappedTargets(QCOProgramBuilder& b); + // --- BarrierOp ------------------------------------------------------------ // /// Creates a circuit with a barrier. From 696940872df26a362a9abddc6a37818c7f2ea670 Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Fri, 12 Jun 2026 13:00:51 +0200 Subject: [PATCH 02/18] =?UTF-8?q?=F0=9F=8E=A8=20Align=20function=20orderin?= =?UTF-8?q?g?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Dialect/QCO/QCOUtils.h | 217 ++++++++++++----------- 1 file changed, 109 insertions(+), 108 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index eacab73f8c..3f1013fe43 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -99,99 +99,6 @@ static FailureOr findPartnerThroughCtrlControlChain(OpType op) { return failure(); } -/** - * @brief Shared implementation for merging two-target, one-parameter - * operations. - * - * @details - * When @p swappedTargets is false, the successor must consume the same target - * wires in the same order. When true, the successor may consume swapped - * targets. The parameter at operand index 2 is summed. - * - * @tparam OpType The type of the operation to be merged. - * @param op The first operation instance. - * @param rewriter The pattern rewriter. - * @param swappedTargets Whether the successor consumes swapped target wires. - * @return LogicalResult Success or failure of the merge. - */ -template -static LogicalResult mergeTwoTargetOneParameterImpl(OpType op, - PatternRewriter& rewriter, - bool swappedTargets) { - auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); - if (!nextOp) { - return failure(); - } - - if (swappedTargets) { - if (op.getOutputQubit(0) != nextOp.getInputQubit(1) || - op.getOutputQubit(1) != nextOp.getInputQubit(0)) { - return failure(); - } - } else if (op.getOutputQubit(1) != nextOp.getInputQubit(1)) { - return failure(); - } - - auto newParameter = addFloatParameters( - rewriter, op.getLoc(), op.getOperand(2), nextOp.getOperand(2)); - op->setOperand(2, newParameter); - if (swappedTargets) { - rewriter.replaceOp(nextOp, {op.getOutputQubit(1), op.getOutputQubit(0)}); - } else { - rewriter.replaceOp(nextOp, op.getResults()); - } - return success(); -} - -/** - * @brief Shared implementation for merging two-target, two-parameter - * operations. - * - * @details - * Requires matching secondary parameters (e.g., `beta` for `XXPlusYYOp` and - * `XXMinusYYOp`). The primary parameter (`theta`) at operand index 2 is summed. - * Wire-order requirements are the same as - * @ref mergeTwoTargetOneParameterImpl. - * - * @tparam OpType The type of the operation to be merged. - * @param op The first operation instance. - * @param rewriter The pattern rewriter. - * @param swappedTargets Whether the successor consumes swapped target wires. - * @return LogicalResult Success or failure of the merge. - */ -template -static LogicalResult mergeTwoTargetTwoParameterImpl(OpType op, - PatternRewriter& rewriter, - bool swappedTargets) { - auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); - if (!nextOp) { - return failure(); - } - - if (swappedTargets) { - if (op.getOutputQubit(0) != nextOp.getInputQubit(1) || - op.getOutputQubit(1) != nextOp.getInputQubit(0)) { - return failure(); - } - } else if (op.getOutputQubit(1) != nextOp.getInputQubit(1)) { - return failure(); - } - - if (!valuesMatchWithinTolerance(op.getBeta(), nextOp.getBeta())) { - return failure(); - } - - auto newTheta = addFloatParameters(rewriter, op.getLoc(), op.getOperand(2), - nextOp.getOperand(2)); - op->setOperand(2, newTheta); - if (swappedTargets) { - rewriter.replaceOp(nextOp, {op.getOutputQubit(1), op.getOutputQubit(0)}); - } else { - rewriter.replaceOp(nextOp, op.getResults()); - } - return success(); -} - /** * @brief Remove a pair of inverse one-target, zero-parameter operations * @@ -329,6 +236,7 @@ LogicalResult mergeOneTargetZeroParameter(OpType op, */ template LogicalResult mergeOneTargetOneParameter(OpType op, PatternRewriter& rewriter) { + // Check if the successor is the same operation auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); @@ -374,6 +282,34 @@ LogicalResult mergeOneTargetTwoParameter(OpType op, PatternRewriter& rewriter) { return success(); } +/** + * @brief Merge Z-diagonal one-target, zero-parameter gates through a chain of + * `ctrl` hops on control wires. + * + * @details + * Replaces `op; …; op` on a control wire with `square; …` (e.g. `s; ctrl; s` → + * `z; ctrl`). + * + * @tparam SquareOpType Result of squaring the gate (e.g. `ZOp` for `SOp`). + * @tparam OpType The Z-diagonal operation to be merged. + * @param op The operation instance. + * @param rewriter The pattern rewriter. + * @return LogicalResult Success or failure of the merge. + */ +template +LogicalResult +mergeOneTargetZeroParameterThroughCtrlControlChain(OpType op, + PatternRewriter& rewriter) { + auto partner = findPartnerThroughCtrlControlChain(op); + if (failed(partner)) { + return failure(); + } + + rewriter.replaceOpWithNewOp(op, op.getInputQubit(0)); + rewriter.replaceOp(*partner, partner->getInputQubit(0)); + return success(); +} + /** * @brief Merge Z-diagonal one-target, one-parameter gate angles through `ctrl` * control wires. @@ -402,30 +338,46 @@ mergeOneTargetOneParameterThroughCtrlControlChain(OpType op, } /** - * @brief Merge Z-diagonal one-target, zero-parameter gates through a chain of - * `ctrl` hops on control wires. + * @brief Shared implementation for merging two-target, one-parameter + * operations. * * @details - * Replaces `op; …; op` on a control wire with `square; …` (e.g. `s; ctrl; s` → - * `z; ctrl`). + * When @p swappedTargets is false, the successor must consume the same target + * wires in the same order. When true, the successor may consume swapped + * targets. The parameter at operand index 2 is summed. * - * @tparam SquareOpType Result of squaring the gate (e.g. `ZOp` for `SOp`). - * @tparam OpType The Z-diagonal operation to be merged. - * @param op The operation instance. + * @tparam OpType The type of the operation to be merged. + * @param op The first operation instance. * @param rewriter The pattern rewriter. + * @param swappedTargets Whether the successor consumes swapped target wires. * @return LogicalResult Success or failure of the merge. */ -template -LogicalResult -mergeOneTargetZeroParameterThroughCtrlControlChain(OpType op, - PatternRewriter& rewriter) { - auto partner = findPartnerThroughCtrlControlChain(op); - if (failed(partner)) { +template +static LogicalResult mergeTwoTargetOneParameterImpl(OpType op, + PatternRewriter& rewriter, + bool swappedTargets) { + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); + if (!nextOp) { return failure(); } - rewriter.replaceOpWithNewOp(op, op.getInputQubit(0)); - rewriter.replaceOp(*partner, partner->getInputQubit(0)); + if (swappedTargets) { + if (op.getOutputQubit(0) != nextOp.getInputQubit(1) || + op.getOutputQubit(1) != nextOp.getInputQubit(0)) { + return failure(); + } + } else if (op.getOutputQubit(1) != nextOp.getInputQubit(1)) { + return failure(); + } + + auto newParameter = addFloatParameters( + rewriter, op.getLoc(), op.getOperand(2), nextOp.getOperand(2)); + op->setOperand(2, newParameter); + if (swappedTargets) { + rewriter.replaceOp(nextOp, {op.getOutputQubit(1), op.getOutputQubit(0)}); + } else { + rewriter.replaceOp(nextOp, op.getResults()); + } return success(); } @@ -466,6 +418,55 @@ mergeTwoTargetOneParameterWithSwappedTargets(OpType op, return mergeTwoTargetOneParameterImpl(op, rewriter, true); } +/** + * @brief Shared implementation for merging two-target, two-parameter + * operations. + * + * @details + * Requires matching secondary parameters (e.g., `beta` for `XXPlusYYOp` and + * `XXMinusYYOp`). The primary parameter (`theta`) at operand index 2 is summed. + * Wire-order requirements are the same as + * @ref mergeTwoTargetOneParameterImpl. + * + * @tparam OpType The type of the operation to be merged. + * @param op The first operation instance. + * @param rewriter The pattern rewriter. + * @param swappedTargets Whether the successor consumes swapped target wires. + * @return LogicalResult Success or failure of the merge. + */ +template +static LogicalResult mergeTwoTargetTwoParameterImpl(OpType op, + PatternRewriter& rewriter, + bool swappedTargets) { + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); + if (!nextOp) { + return failure(); + } + + if (swappedTargets) { + if (op.getOutputQubit(0) != nextOp.getInputQubit(1) || + op.getOutputQubit(1) != nextOp.getInputQubit(0)) { + return failure(); + } + } else if (op.getOutputQubit(1) != nextOp.getInputQubit(1)) { + return failure(); + } + + if (!valuesMatchWithinTolerance(op.getBeta(), nextOp.getBeta())) { + return failure(); + } + + auto newTheta = addFloatParameters(rewriter, op.getLoc(), op.getOperand(2), + nextOp.getOperand(2)); + op->setOperand(2, newTheta); + if (swappedTargets) { + rewriter.replaceOp(nextOp, {op.getOutputQubit(1), op.getOutputQubit(0)}); + } else { + rewriter.replaceOp(nextOp, op.getResults()); + } + return success(); +} + /** * @brief Merge two compatible two-target, two-parameter operations * From 23380dfce51ecea336c24bb23691b87bf0849427 Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Fri, 12 Jun 2026 13:18:47 +0200 Subject: [PATCH 03/18] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20linter=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp | 1 - mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp index c1a3278e0e..f15686b097 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp @@ -13,7 +13,6 @@ #include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp index 9c7d21b498..846d3e66c4 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp @@ -13,7 +13,6 @@ #include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include #include #include From 9331b1eaf16b90c887cbc251b273f8365e1c4579 Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Fri, 12 Jun 2026 13:47:29 +0200 Subject: [PATCH 04/18] =?UTF-8?q?=E2=98=82=EF=B8=8F=20Increase=20coverage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp | 11 +++- mlir/unittests/programs/qco_programs.cpp | 50 +++++++++++++++++++ mlir/unittests/programs/qco_programs.h | 15 ++++++ 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp index 0bf3b68ec7..e551a7f34f 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp @@ -483,7 +483,9 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(rx)}, QCOTestCase{"CanonicalizeRToRy", MQT_NAMED_BUILDER(canonicalizeRToRy), MQT_NAMED_BUILDER(ry)}, - QCOTestCase{"TwoR", MQT_NAMED_BUILDER(twoR), MQT_NAMED_BUILDER(r)})); + QCOTestCase{"TwoR", MQT_NAMED_BUILDER(twoR), MQT_NAMED_BUILDER(r)}, + QCOTestCase{"TwoRDynamicPhi", MQT_NAMED_BUILDER(twoRDynamicPhi), + MQT_NAMED_BUILDER(twoRDynamicPhiMerged)})); /// @} /// \name QCO/Operations/StandardGates/RxOp.cpp @@ -634,6 +636,8 @@ INSTANTIATE_TEST_SUITE_P( QCOTestCase{"TwoRZThroughCtrlControlChain", MQT_NAMED_BUILDER(twoRzThroughCtrlControlChain), MQT_NAMED_BUILDER(twoRzThroughCtrlControlChainMerged)}, + QCOTestCase{"TwoRZDynamicAngles", MQT_NAMED_BUILDER(twoRzDynamicAngles), + MQT_NAMED_BUILDER(twoRzDynamicAnglesMerged)}, QCOTestCase{ "TwoRZThroughNestedCtrlControlChain", MQT_NAMED_BUILDER(twoRzThroughNestedCtrlControlChain), @@ -1027,7 +1031,10 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(emptyQCO)}, QCOTestCase{"TwoXXMinusYYSwappedTargets", MQT_NAMED_BUILDER(twoXxMinusYYSwappedTargets), - MQT_NAMED_BUILDER(xxMinusYY)})); + MQT_NAMED_BUILDER(xxMinusYY)}, + QCOTestCase{"TwoXXMinusYYMismatchedBeta", + MQT_NAMED_BUILDER(twoXxMinusYYMismatchedBeta), + MQT_NAMED_BUILDER(twoXxMinusYYMismatchedBeta)})); /// @} /// \name QCO/Operations/StandardGates/XxPlusYyOp.cpp diff --git a/mlir/unittests/programs/qco_programs.cpp b/mlir/unittests/programs/qco_programs.cpp index b443764a51..bd048fdf59 100644 --- a/mlir/unittests/programs/qco_programs.cpp +++ b/mlir/unittests/programs/qco_programs.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -1080,6 +1081,28 @@ void twoRzThroughCtrlControlChainMerged(QCOProgramBuilder& b) { std::tie(q[0], q[1]) = b.cx(q[0], q[1]); } +void twoRzDynamicAngles(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(1); + const auto thetaA = + arith::ConstantOp::create(b, b.getF64FloatAttr(0.1)).getResult(); + const auto thetaB = + arith::ConstantOp::create(b, b.getF64FloatAttr(0.2)).getResult(); + const auto thetaSum = arith::AddFOp::create(b, thetaA, thetaB).getResult(); + q[0] = b.rz(thetaSum, q[0]); + q[0] = b.rz(thetaB, q[0]); +} + +void twoRzDynamicAnglesMerged(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(1); + const auto thetaA = + arith::ConstantOp::create(b, b.getF64FloatAttr(0.1)).getResult(); + const auto thetaB = + arith::ConstantOp::create(b, b.getF64FloatAttr(0.2)).getResult(); + const auto thetaSum = arith::AddFOp::create(b, thetaA, thetaB).getResult(); + const auto theta = arith::AddFOp::create(b, thetaSum, thetaB).getResult(); + b.rz(theta, q[0]); +} + void twoRzThroughNestedCtrlControlChain(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); q[0] = b.rz(0.1, q[0]); @@ -1234,6 +1257,27 @@ void twoR(QCOProgramBuilder& b) { q[0] = b.r(0.078, 0.456, q[0]); } +void twoRDynamicPhi(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(1); + const auto phiBase = + arith::ConstantOp::create(b, b.getF64FloatAttr(0.1)).getResult(); + const auto phiOffset = + arith::ConstantOp::create(b, b.getF64FloatAttr(0.05)).getResult(); + const auto phi = arith::AddFOp::create(b, phiBase, phiOffset).getResult(); + q[0] = b.r(0.045, phi, q[0]); + q[0] = b.r(0.078, phi, q[0]); +} + +void twoRDynamicPhiMerged(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(1); + const auto phiBase = + arith::ConstantOp::create(b, b.getF64FloatAttr(0.1)).getResult(); + const auto phiOffset = + arith::ConstantOp::create(b, b.getF64FloatAttr(0.05)).getResult(); + const auto phi = arith::AddFOp::create(b, phiBase, phiOffset).getResult(); + b.r(0.123, phi, q[0]); +} + void u2(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.u2(0.234, 0.567, q[0]); @@ -2027,6 +2071,12 @@ void twoXxMinusYYSwappedTargets(QCOProgramBuilder& b) { std::tie(q[1], q[0]) = b.xx_minus_yy(0.078, 0.456, q[1], q[0]); } +void twoXxMinusYYMismatchedBeta(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + std::tie(q[0], q[1]) = b.xx_minus_yy(0.1, 0.456, q[0], q[1]); + std::tie(q[0], q[1]) = b.xx_minus_yy(0.2, 0.789, q[0], q[1]); +} + void barrier(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.barrier(q[0]); diff --git a/mlir/unittests/programs/qco_programs.h b/mlir/unittests/programs/qco_programs.h index aee96d205c..392cbb1acb 100644 --- a/mlir/unittests/programs/qco_programs.h +++ b/mlir/unittests/programs/qco_programs.h @@ -552,6 +552,12 @@ void twoRzThroughCtrlControlChain(QCOProgramBuilder& b); /// Creates the merged single-RZ form of @ref twoRzThroughCtrlControlChain. void twoRzThroughCtrlControlChainMerged(QCOProgramBuilder& b); +/// Creates a circuit with two RZ gates using a non-constant summed angle. +void twoRzDynamicAngles(QCOProgramBuilder& b); + +/// Creates the merged single-RZ form of @ref twoRzDynamicAngles. +void twoRzDynamicAnglesMerged(QCOProgramBuilder& b); + /// Creates a circuit with two RZ gates on a control wire separated by nested /// `ctrl` hops. void twoRzThroughNestedCtrlControlChain(QCOProgramBuilder& b); @@ -628,6 +634,12 @@ void canonicalizeRToRy(QCOProgramBuilder& b); /// Creates a circuit with two R gates in a row with the same `phi`. void twoR(QCOProgramBuilder& b); +/// Creates a circuit with two R gates sharing a symbolic `phi` value. +void twoRDynamicPhi(QCOProgramBuilder& b); + +/// Creates the merged single-R form of @ref twoRDynamicPhi. +void twoRDynamicPhiMerged(QCOProgramBuilder& b); + // --- U2Op ----------------------------------------------------------------- // /// Creates a circuit with just a U2 gate. @@ -1004,6 +1016,9 @@ void twoXxMinusYYOppositePhase(QCOProgramBuilder& b); /// Creates a circuit with two XXMinusYY gates in a row with swapped targets. void twoXxMinusYYSwappedTargets(QCOProgramBuilder& b); +/// Creates a circuit with two XXMinusYY gates that differ in `beta`. +void twoXxMinusYYMismatchedBeta(QCOProgramBuilder& b); + // --- BarrierOp ------------------------------------------------------------ // /// Creates a circuit with a barrier. From 9a526f589c59a5daca1302c5eacbfbfbdbff2f69 Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Fri, 12 Jun 2026 14:05:07 +0200 Subject: [PATCH 05/18] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unnecessary=20tes?= =?UTF-8?q?ts=20again?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp | 6 +-- mlir/unittests/programs/qco_programs.cpp | 44 ------------------- mlir/unittests/programs/qco_programs.h | 12 ----- 3 files changed, 1 insertion(+), 61 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp index e551a7f34f..3da727440c 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp @@ -483,9 +483,7 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(rx)}, QCOTestCase{"CanonicalizeRToRy", MQT_NAMED_BUILDER(canonicalizeRToRy), MQT_NAMED_BUILDER(ry)}, - QCOTestCase{"TwoR", MQT_NAMED_BUILDER(twoR), MQT_NAMED_BUILDER(r)}, - QCOTestCase{"TwoRDynamicPhi", MQT_NAMED_BUILDER(twoRDynamicPhi), - MQT_NAMED_BUILDER(twoRDynamicPhiMerged)})); + QCOTestCase{"TwoR", MQT_NAMED_BUILDER(twoR), MQT_NAMED_BUILDER(r)})); /// @} /// \name QCO/Operations/StandardGates/RxOp.cpp @@ -636,8 +634,6 @@ INSTANTIATE_TEST_SUITE_P( QCOTestCase{"TwoRZThroughCtrlControlChain", MQT_NAMED_BUILDER(twoRzThroughCtrlControlChain), MQT_NAMED_BUILDER(twoRzThroughCtrlControlChainMerged)}, - QCOTestCase{"TwoRZDynamicAngles", MQT_NAMED_BUILDER(twoRzDynamicAngles), - MQT_NAMED_BUILDER(twoRzDynamicAnglesMerged)}, QCOTestCase{ "TwoRZThroughNestedCtrlControlChain", MQT_NAMED_BUILDER(twoRzThroughNestedCtrlControlChain), diff --git a/mlir/unittests/programs/qco_programs.cpp b/mlir/unittests/programs/qco_programs.cpp index fe1b149363..0e745cd34c 100644 --- a/mlir/unittests/programs/qco_programs.cpp +++ b/mlir/unittests/programs/qco_programs.cpp @@ -14,7 +14,6 @@ #include #include -#include #include #include @@ -1081,28 +1080,6 @@ void twoRzThroughCtrlControlChainMerged(QCOProgramBuilder& b) { std::tie(q[0], q[1]) = b.cx(q[0], q[1]); } -void twoRzDynamicAngles(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(1); - const auto thetaA = - arith::ConstantOp::create(b, b.getF64FloatAttr(0.1)).getResult(); - const auto thetaB = - arith::ConstantOp::create(b, b.getF64FloatAttr(0.2)).getResult(); - const auto thetaSum = arith::AddFOp::create(b, thetaA, thetaB).getResult(); - q[0] = b.rz(thetaSum, q[0]); - q[0] = b.rz(thetaB, q[0]); -} - -void twoRzDynamicAnglesMerged(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(1); - const auto thetaA = - arith::ConstantOp::create(b, b.getF64FloatAttr(0.1)).getResult(); - const auto thetaB = - arith::ConstantOp::create(b, b.getF64FloatAttr(0.2)).getResult(); - const auto thetaSum = arith::AddFOp::create(b, thetaA, thetaB).getResult(); - const auto theta = arith::AddFOp::create(b, thetaSum, thetaB).getResult(); - b.rz(theta, q[0]); -} - void twoRzThroughNestedCtrlControlChain(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); q[0] = b.rz(0.1, q[0]); @@ -1257,27 +1234,6 @@ void twoR(QCOProgramBuilder& b) { q[0] = b.r(0.078, 0.456, q[0]); } -void twoRDynamicPhi(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(1); - const auto phiBase = - arith::ConstantOp::create(b, b.getF64FloatAttr(0.1)).getResult(); - const auto phiOffset = - arith::ConstantOp::create(b, b.getF64FloatAttr(0.05)).getResult(); - const auto phi = arith::AddFOp::create(b, phiBase, phiOffset).getResult(); - q[0] = b.r(0.045, phi, q[0]); - q[0] = b.r(0.078, phi, q[0]); -} - -void twoRDynamicPhiMerged(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(1); - const auto phiBase = - arith::ConstantOp::create(b, b.getF64FloatAttr(0.1)).getResult(); - const auto phiOffset = - arith::ConstantOp::create(b, b.getF64FloatAttr(0.05)).getResult(); - const auto phi = arith::AddFOp::create(b, phiBase, phiOffset).getResult(); - b.r(0.123, phi, q[0]); -} - void u2(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.u2(0.234, 0.567, q[0]); diff --git a/mlir/unittests/programs/qco_programs.h b/mlir/unittests/programs/qco_programs.h index 877c3379a7..7a3d32660d 100644 --- a/mlir/unittests/programs/qco_programs.h +++ b/mlir/unittests/programs/qco_programs.h @@ -552,12 +552,6 @@ void twoRzThroughCtrlControlChain(QCOProgramBuilder& b); /// Creates the merged single-RZ form of @ref twoRzThroughCtrlControlChain. void twoRzThroughCtrlControlChainMerged(QCOProgramBuilder& b); -/// Creates a circuit with two RZ gates using a non-constant summed angle. -void twoRzDynamicAngles(QCOProgramBuilder& b); - -/// Creates the merged single-RZ form of @ref twoRzDynamicAngles. -void twoRzDynamicAnglesMerged(QCOProgramBuilder& b); - /// Creates a circuit with two RZ gates on a control wire separated by nested /// `ctrl` hops. void twoRzThroughNestedCtrlControlChain(QCOProgramBuilder& b); @@ -634,12 +628,6 @@ void canonicalizeRToRy(QCOProgramBuilder& b); /// Creates a circuit with two R gates in a row with the same `phi`. void twoR(QCOProgramBuilder& b); -/// Creates a circuit with two R gates sharing a symbolic `phi` value. -void twoRDynamicPhi(QCOProgramBuilder& b); - -/// Creates the merged single-R form of @ref twoRDynamicPhi. -void twoRDynamicPhiMerged(QCOProgramBuilder& b); - // --- U2Op ----------------------------------------------------------------- // /// Creates a circuit with just a U2 gate. From 40762ba6cf6af7362247c7789b64b5df79635d6c Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Fri, 12 Jun 2026 14:39:29 +0200 Subject: [PATCH 06/18] =?UTF-8?q?=F0=9F=94=A5=20Remove=20unnecessary=20tes?= =?UTF-8?q?ts=20again?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp | 10 ++-------- mlir/unittests/programs/qco_programs.cpp | 13 ------------- mlir/unittests/programs/qco_programs.h | 6 ------ 3 files changed, 2 insertions(+), 27 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp index 3da727440c..ca1dd92537 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp @@ -637,10 +637,7 @@ INSTANTIATE_TEST_SUITE_P( QCOTestCase{ "TwoRZThroughNestedCtrlControlChain", MQT_NAMED_BUILDER(twoRzThroughNestedCtrlControlChain), - MQT_NAMED_BUILDER(twoRzThroughNestedCtrlControlChainMerged)}, - QCOTestCase{"TwoRZSplitAcrossCtrlTargetWires", - MQT_NAMED_BUILDER(twoRzSplitAcrossCtrlTargetWires), - MQT_NAMED_BUILDER(twoRzSplitAcrossCtrlTargetWires)})); + MQT_NAMED_BUILDER(twoRzThroughNestedCtrlControlChainMerged)})); /// @} /// \name QCO/Operations/StandardGates/RzxOp.cpp @@ -1027,10 +1024,7 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(emptyQCO)}, QCOTestCase{"TwoXXMinusYYSwappedTargets", MQT_NAMED_BUILDER(twoXxMinusYYSwappedTargets), - MQT_NAMED_BUILDER(xxMinusYY)}, - QCOTestCase{"TwoXXMinusYYMismatchedBeta", - MQT_NAMED_BUILDER(twoXxMinusYYMismatchedBeta), - MQT_NAMED_BUILDER(twoXxMinusYYMismatchedBeta)})); + MQT_NAMED_BUILDER(xxMinusYY)})); /// @} /// \name QCO/Operations/StandardGates/XxPlusYyOp.cpp diff --git a/mlir/unittests/programs/qco_programs.cpp b/mlir/unittests/programs/qco_programs.cpp index 0e745cd34c..ba73615ab9 100644 --- a/mlir/unittests/programs/qco_programs.cpp +++ b/mlir/unittests/programs/qco_programs.cpp @@ -1095,13 +1095,6 @@ void twoRzThroughNestedCtrlControlChainMerged(QCOProgramBuilder& b) { std::tie(q[0], q[2]) = b.cx(q[0], q[2]); } -void twoRzSplitAcrossCtrlTargetWires(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.rz(0.1, q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); - q[1] = b.rz(0.2, q[1]); -} - void p(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.p(0.123, q[0]); @@ -2027,12 +2020,6 @@ void twoXxMinusYYSwappedTargets(QCOProgramBuilder& b) { std::tie(q[1], q[0]) = b.xx_minus_yy(0.078, 0.456, q[1], q[0]); } -void twoXxMinusYYMismatchedBeta(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - std::tie(q[0], q[1]) = b.xx_minus_yy(0.1, 0.456, q[0], q[1]); - std::tie(q[0], q[1]) = b.xx_minus_yy(0.2, 0.789, q[0], q[1]); -} - void barrier(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.barrier(q[0]); diff --git a/mlir/unittests/programs/qco_programs.h b/mlir/unittests/programs/qco_programs.h index 7a3d32660d..5e1ff661d8 100644 --- a/mlir/unittests/programs/qco_programs.h +++ b/mlir/unittests/programs/qco_programs.h @@ -560,9 +560,6 @@ void twoRzThroughNestedCtrlControlChain(QCOProgramBuilder& b); /// twoRzThroughNestedCtrlControlChain. void twoRzThroughNestedCtrlControlChainMerged(QCOProgramBuilder& b); -/// Creates a circuit with RZ gates on control and target wires around `ctrl`. -void twoRzSplitAcrossCtrlTargetWires(QCOProgramBuilder& b); - // --- POp ------------------------------------------------------------------ // /// Creates a circuit with just a P gate. @@ -1004,9 +1001,6 @@ void twoXxMinusYYOppositePhase(QCOProgramBuilder& b); /// Creates a circuit with two XXMinusYY gates in a row with swapped targets. void twoXxMinusYYSwappedTargets(QCOProgramBuilder& b); -/// Creates a circuit with two XXMinusYY gates that differ in `beta`. -void twoXxMinusYYMismatchedBeta(QCOProgramBuilder& b); - // --- BarrierOp ------------------------------------------------------------ // /// Creates a circuit with a barrier. From c275cf8e7eaa940c8ca8a312268d36e18847b585 Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Fri, 12 Jun 2026 14:40:13 +0200 Subject: [PATCH 07/18] =?UTF-8?q?=F0=9F=94=A5=20Revert=20back=20to=20arith?= =?UTF-8?q?::AddFOp=20for=20summing=20parameters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Dialect/QCO/QCOUtils.h | 48 +++++++----------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index 3f1013fe43..c5a54b05d3 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -40,26 +40,6 @@ static inline bool valuesMatchWithinTolerance(Value lhs, Value rhs) { return lhs == rhs; } -/** - * @brief Sum two floating-point parameter values, folding constants when - * possible. - * - * @param rewriter The pattern rewriter. - * @param loc The location for newly created operations. - * @param lhs The first summand. - * @param rhs The second summand. - * @return Value The sum, as a constant or `arith.addf` result. - */ -static inline Value addFloatParameters(PatternRewriter& rewriter, Location loc, - Value lhs, Value rhs) { - const auto lhsVal = utils::valueToDouble(lhs); - const auto rhsVal = utils::valueToDouble(rhs); - if (lhsVal.has_value() && rhsVal.has_value()) { - return utils::constantFromScalar(rewriter, loc, *lhsVal + *rhsVal); - } - return arith::AddFOp::create(rewriter, loc, lhs, rhs).getResult(); -} - /** * @brief Find a same-type partner operation reachable through `ctrl` hops on a * control wire. @@ -243,9 +223,9 @@ LogicalResult mergeOneTargetOneParameter(OpType op, PatternRewriter& rewriter) { } // Compute and set the new parameter - auto newParameter = addFloatParameters( + auto newParameter = arith::AddFOp::create( rewriter, op.getLoc(), op.getOperand(1), nextOp.getOperand(1)); - op->setOperand(1, newParameter); + op->setOperand(1, newParameter.getResult()); // Replace the second operation with the result of the first operation rewriter.replaceOp(nextOp, op.getResult()); @@ -275,9 +255,9 @@ LogicalResult mergeOneTargetTwoParameter(OpType op, PatternRewriter& rewriter) { return failure(); } - auto newTheta = addFloatParameters(rewriter, op.getLoc(), op.getOperand(1), - nextOp.getOperand(1)); - op->setOperand(1, newTheta); + auto newTheta = arith::AddFOp::create(rewriter, op.getLoc(), op.getOperand(1), + nextOp.getOperand(1)); + op->setOperand(1, newTheta.getResult()); rewriter.replaceOp(nextOp, op.getResult()); return success(); } @@ -328,11 +308,11 @@ mergeOneTargetOneParameterThroughCtrlControlChain(OpType op, return failure(); } - const Location loc = op.getLoc(); rewriter.setInsertionPoint(op); - const Value newTheta = - addFloatParameters(rewriter, loc, op.getTheta(), partner->getTheta()); - rewriter.modifyOpInPlace(op, [&] { op.getThetaMutable().assign(newTheta); }); + auto newTheta = arith::AddFOp::create(rewriter, op.getLoc(), op.getTheta(), + partner->getTheta()); + rewriter.modifyOpInPlace( + op, [&] { op.getThetaMutable().assign(newTheta.getResult()); }); rewriter.replaceOp(*partner, partner->getOperand(0)); return success(); } @@ -370,9 +350,9 @@ static LogicalResult mergeTwoTargetOneParameterImpl(OpType op, return failure(); } - auto newParameter = addFloatParameters( + auto newParameter = arith::AddFOp::create( rewriter, op.getLoc(), op.getOperand(2), nextOp.getOperand(2)); - op->setOperand(2, newParameter); + op->setOperand(2, newParameter.getResult()); if (swappedTargets) { rewriter.replaceOp(nextOp, {op.getOutputQubit(1), op.getOutputQubit(0)}); } else { @@ -456,9 +436,9 @@ static LogicalResult mergeTwoTargetTwoParameterImpl(OpType op, return failure(); } - auto newTheta = addFloatParameters(rewriter, op.getLoc(), op.getOperand(2), - nextOp.getOperand(2)); - op->setOperand(2, newTheta); + auto newTheta = arith::AddFOp::create(rewriter, op.getLoc(), op.getOperand(2), + nextOp.getOperand(2)); + op->setOperand(2, newTheta.getResult()); if (swappedTargets) { rewriter.replaceOp(nextOp, {op.getOutputQubit(1), op.getOutputQubit(0)}); } else { From 57dffa93dc53d83c615153150b7ef41677c83ee0 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Mon, 15 Jun 2026 11:57:19 +0200 Subject: [PATCH 08/18] Remove namespace qaulifiers and use auto --- mlir/include/mlir/Dialect/QCO/QCOUtils.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index c5a54b05d3..e077b12c3a 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -55,21 +55,20 @@ static inline bool valuesMatchWithinTolerance(Value lhs, Value rhs) { template static FailureOr findPartnerThroughCtrlControlChain(OpType op) { Value v = op->getResult(0); - if (!llvm::isa(v.getType())) { + if (!isa(v.getType())) { return failure(); } unsigned hops = 0; while (v.hasOneUse()) { - Operation* user = *v.getUsers().begin(); - if (auto next = llvm::dyn_cast(user); - next && next->getOperand(0) == v) { + auto* user = *v.getUsers().begin(); + if (auto next = dyn_cast(user); next && next->getOperand(0) == v) { if (hops == 0) { return failure(); } return next; } - auto ctrl = llvm::dyn_cast(user); + auto ctrl = dyn_cast(user); if (!ctrl || !llvm::is_contained(ctrl.getControlsIn(), v)) { return failure(); } @@ -267,8 +266,8 @@ LogicalResult mergeOneTargetTwoParameter(OpType op, PatternRewriter& rewriter) { * `ctrl` hops on control wires. * * @details - * Replaces `op; …; op` on a control wire with `square; …` (e.g. `s; ctrl; s` → - * `z; ctrl`). + * Replaces `op; ...; op` on a control wire with `square; ...` (e.g., `s; ctrl; + * s` → `z; ctrl`). * * @tparam SquareOpType Result of squaring the gate (e.g. `ZOp` for `SOp`). * @tparam OpType The Z-diagonal operation to be merged. From 8aed9f05a88d6721da22c14e9dba51447821d591 Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Tue, 16 Jun 2026 10:10:55 +0200 Subject: [PATCH 09/18] =?UTF-8?q?=E2=9C=A8=20Refactor=20QCO=20operations?= =?UTF-8?q?=20to=20improve=20control=20wire=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Dialect/QCO/QCOUtils.h | 285 ++++++------------ .../QCO/IR/Operations/StandardGates/DCXOp.cpp | 3 +- .../QCO/IR/Operations/StandardGates/POp.cpp | 6 +- .../QCO/IR/Operations/StandardGates/ROp.cpp | 15 +- .../QCO/IR/Operations/StandardGates/RXXOp.cpp | 2 +- .../QCO/IR/Operations/StandardGates/RYYOp.cpp | 2 +- .../QCO/IR/Operations/StandardGates/RZOp.cpp | 6 +- .../QCO/IR/Operations/StandardGates/RZZOp.cpp | 2 +- .../QCO/IR/Operations/StandardGates/SOp.cpp | 8 +- .../IR/Operations/StandardGates/SWAPOp.cpp | 3 +- .../QCO/IR/Operations/StandardGates/SdgOp.cpp | 9 +- .../QCO/IR/Operations/StandardGates/TOp.cpp | 8 +- .../QCO/IR/Operations/StandardGates/TdgOp.cpp | 9 +- .../Operations/StandardGates/XXMinusYYOp.cpp | 4 +- .../Operations/StandardGates/XXPlusYYOp.cpp | 4 +- mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp | 85 +++--- mlir/unittests/programs/qco_programs.cpp | 28 +- mlir/unittests/programs/qco_programs.h | 42 +-- 18 files changed, 206 insertions(+), 315 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index e077b12c3a..0f4ecbcf37 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -24,27 +24,48 @@ namespace mlir::qco { /** - * @brief Check whether two parameter values are equal, using tolerance for - * constants. + * @brief Check whether two two-target operations consume the same target wires. + * + * @tparam OpType The type of the operation to be checked. + * @param op The first operation instance. + * @param nextOp The successor operation instance. + * @param swappedTargets Whether @p nextOp consumes swapped target wires. + * @return true if the wire order matches. + */ +template +static bool twoTargetWiresMatch(OpType op, OpType nextOp, bool swappedTargets) { + if (swappedTargets) { + return op.getOutputQubit(0) == nextOp.getInputQubit(1) && + op.getOutputQubit(1) == nextOp.getInputQubit(0); + } + return op.getOutputQubit(1) == nextOp.getInputQubit(1); +} + +/** + * @brief Check whether two parameter values match. + * + * @details + * Identical SSA values always match. Otherwise, if both are constants, they + * are compared with @ref utils::TOLERANCE. * * @param lhs The first parameter value. * @param rhs The second parameter value. * @return true if the values match. */ static inline bool valuesMatchWithinTolerance(Value lhs, Value rhs) { + if (lhs == rhs) { + return true; + } const auto lhsVal = utils::valueToDouble(lhs); const auto rhsVal = utils::valueToDouble(rhs); - if (lhsVal.has_value() && rhsVal.has_value()) { - return std::abs(*lhsVal - *rhsVal) <= utils::TOLERANCE; - } - return lhs == rhs; + return lhsVal && rhsVal && std::abs(*lhsVal - *rhsVal) <= utils::TOLERANCE; } /** - * @brief Find a same-type partner operation reachable through `ctrl` hops on a - * control wire. + * @brief Find a same-type partner operation on a control wire. * * @details + * Walks `ctrl` hops on the control wire to find a matching operation. * Returns failure when no partner exists or when the partner is directly * adjacent on the same wire (zero `ctrl` hops). * @@ -53,7 +74,7 @@ static inline bool valuesMatchWithinTolerance(Value lhs, Value rhs) { * @return FailureOr The partner operation, or failure. */ template -static FailureOr findPartnerThroughCtrlControlChain(OpType op) { +static FailureOr findPartnerOnControlWire(OpType op) { Value v = op->getResult(0); if (!isa(v.getType())) { return failure(); @@ -105,65 +126,43 @@ removeInversePairOneTargetZeroParameter(OpType op, PatternRewriter& rewriter) { } /** - * @brief Remove a pair of inverse two-target, zero-parameter operations + * @brief Remove a pair of inverse two-target, zero-parameter operations. + * + * @details + * When @p swappedTargets is false, the successor must consume the same target + * wires in the same order. When true, the successor may consume swapped + * targets. * * @tparam InverseOpType The type of the inverse operation. * @tparam OpType The type of the operation to be checked. * @param op The operation instance. * @param rewriter The pattern rewriter. + * @param swappedTargets Whether the successor consumes swapped target wires. * @return LogicalResult Success or failure of the removal. */ template LogicalResult -removeInversePairTwoTargetZeroParameter(OpType op, PatternRewriter& rewriter) { +removeInversePairTwoTargetZeroParameter(OpType op, PatternRewriter& rewriter, + bool swappedTargets = false) { // Check if the successor is the inverse operation auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } - // Confirm operations act on the same qubits - if (op.getOutputQubit(1) != nextOp.getInputQubit(1)) { + if (!twoTargetWiresMatch(op, nextOp, swappedTargets)) { return failure(); } - // Erase both operations - rewriter.replaceAllUsesWith(nextOp->getResults(), - {op.getInputQubit(0), op.getInputQubit(1)}); - rewriter.eraseOp(nextOp); - rewriter.eraseOp(op); - - return success(); -} - -/** - * @brief Remove a pair of two-target, zero-parameter operations where - * the second operation is the same gate with swapped targets. - * - * @tparam OpType The type of the (self-inverse) operation. - * @param op The operation instance. - * @param rewriter The pattern rewriter. - * @return LogicalResult Success or failure of the removal. - */ -template -LogicalResult -removeTwoTargetZeroParameterPairWithSwappedTargets(OpType op, - PatternRewriter& rewriter) { - // Check if the successor is the same operation - auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); - if (!nextOp) { - return failure(); - } - - // Confirm operations act on the same qubits but with swapped targets - if (op.getOutputQubit(0) != nextOp.getInputQubit(1) || - op.getOutputQubit(1) != nextOp.getInputQubit(0)) { - return failure(); + if (swappedTargets) { + rewriter.replaceAllUsesWith(nextOp->getResults(), + {op.getInputQubit(1), op.getInputQubit(0)}); + } else { + rewriter.replaceAllUsesWith(nextOp->getResults(), + {op.getInputQubit(0), op.getInputQubit(1)}); } // Erase both operations - rewriter.replaceAllUsesWith(nextOp->getResults(), - {op.getInputQubit(1), op.getInputQubit(0)}); rewriter.eraseOp(nextOp); rewriter.eraseOp(op); @@ -232,38 +231,7 @@ LogicalResult mergeOneTargetOneParameter(OpType op, PatternRewriter& rewriter) { } /** - * @brief Merge two compatible one-target, two-parameter operations - * - * @details - * Requires matching secondary parameters (e.g., `phi` for `ROp`). The primary - * parameter (`theta`) is summed. - * - * @tparam OpType The type of the operation to be merged. - * @param op The operation instance. - * @param rewriter The pattern rewriter. - * @return LogicalResult Success or failure of the merge. - */ -template -LogicalResult mergeOneTargetTwoParameter(OpType op, PatternRewriter& rewriter) { - auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); - if (!nextOp) { - return failure(); - } - - if (!valuesMatchWithinTolerance(op.getPhi(), nextOp.getPhi())) { - return failure(); - } - - auto newTheta = arith::AddFOp::create(rewriter, op.getLoc(), op.getOperand(1), - nextOp.getOperand(1)); - op->setOperand(1, newTheta.getResult()); - rewriter.replaceOp(nextOp, op.getResult()); - return success(); -} - -/** - * @brief Merge Z-diagonal one-target, zero-parameter gates through a chain of - * `ctrl` hops on control wires. + * @brief Merge Z-diagonal one-target, zero-parameter gates on a control wire. * * @details * Replaces `op; ...; op` on a control wire with `square; ...` (e.g., `s; ctrl; @@ -277,9 +245,8 @@ LogicalResult mergeOneTargetTwoParameter(OpType op, PatternRewriter& rewriter) { */ template LogicalResult -mergeOneTargetZeroParameterThroughCtrlControlChain(OpType op, - PatternRewriter& rewriter) { - auto partner = findPartnerThroughCtrlControlChain(op); +mergeOneTargetZeroParameterOnControlWire(OpType op, PatternRewriter& rewriter) { + auto partner = findPartnerOnControlWire(op); if (failed(partner)) { return failure(); } @@ -290,8 +257,8 @@ mergeOneTargetZeroParameterThroughCtrlControlChain(OpType op, } /** - * @brief Merge Z-diagonal one-target, one-parameter gate angles through `ctrl` - * control wires. + * @brief Merge Z-diagonal one-target, one-parameter gate angles on a control + * wire. * * @tparam OpType The type of the Z-diagonal operation to be merged. * @param op The operation instance. @@ -300,18 +267,18 @@ mergeOneTargetZeroParameterThroughCtrlControlChain(OpType op, */ template LogicalResult -mergeOneTargetOneParameterThroughCtrlControlChain(OpType op, - PatternRewriter& rewriter) { - auto partner = findPartnerThroughCtrlControlChain(op); +mergeOneTargetOneParameterOnControlWire(OpType op, PatternRewriter& rewriter) { + auto partner = findPartnerOnControlWire(op); if (failed(partner)) { return failure(); } - rewriter.setInsertionPoint(op); - auto newTheta = arith::AddFOp::create(rewriter, op.getLoc(), op.getTheta(), - partner->getTheta()); - rewriter.modifyOpInPlace( - op, [&] { op.getThetaMutable().assign(newTheta.getResult()); }); + // Compute and set the new parameter + auto newParameter = arith::AddFOp::create( + rewriter, op.getLoc(), op.getOperand(1), partner->getOperand(1)); + op->setOperand(1, newParameter.getResult()); + + // Replace the partner operation with the input of the first operation rewriter.replaceOp(*partner, partner->getOperand(0)); return success(); } @@ -321,166 +288,90 @@ mergeOneTargetOneParameterThroughCtrlControlChain(OpType op, * operations. * * @details - * When @p swappedTargets is false, the successor must consume the same target - * wires in the same order. When true, the successor may consume swapped - * targets. The parameter at operand index 2 is summed. + * Wire-order requirements are enforced via @ref twoTargetWiresMatch. The + * parameter at operand index 2 is summed. * * @tparam OpType The type of the operation to be merged. * @param op The first operation instance. + * @param nextOp The successor operation instance. * @param rewriter The pattern rewriter. * @param swappedTargets Whether the successor consumes swapped target wires. * @return LogicalResult Success or failure of the merge. */ template -static LogicalResult mergeTwoTargetOneParameterImpl(OpType op, +static LogicalResult mergeTwoTargetOneParameterImpl(OpType op, OpType nextOp, PatternRewriter& rewriter, bool swappedTargets) { - auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); - if (!nextOp) { - return failure(); - } - - if (swappedTargets) { - if (op.getOutputQubit(0) != nextOp.getInputQubit(1) || - op.getOutputQubit(1) != nextOp.getInputQubit(0)) { - return failure(); - } - } else if (op.getOutputQubit(1) != nextOp.getInputQubit(1)) { + if (!twoTargetWiresMatch(op, nextOp, swappedTargets)) { return failure(); } + // Compute and set the new parameter auto newParameter = arith::AddFOp::create( rewriter, op.getLoc(), op.getOperand(2), nextOp.getOperand(2)); op->setOperand(2, newParameter.getResult()); if (swappedTargets) { + // nextOp results correspond to swapped operands, so swap replacements too rewriter.replaceOp(nextOp, {op.getOutputQubit(1), op.getOutputQubit(0)}); } else { + // Replace the second operation with the result of the first operation rewriter.replaceOp(nextOp, op.getResults()); } return success(); } /** - * @brief Merge two compatible two-target, one-parameter operations + * @brief Merge two compatible two-target, one-parameter operations. * * @details - * The new parameter is computed as the sum of the two original parameters. + * When @p swappedTargets is false, the successor must consume the same target + * wires in the same order. When true, the successor may consume swapped + * targets. The parameter at operand index 2 is summed. * * @tparam OpType The type of the operation to be merged. * @param op The operation instance. * @param rewriter The pattern rewriter. + * @param swappedTargets Whether the successor consumes swapped target wires. * @return LogicalResult Success or failure of the merge. */ template -LogicalResult mergeTwoTargetOneParameter(OpType op, PatternRewriter& rewriter) { - return mergeTwoTargetOneParameterImpl(op, rewriter, false); +LogicalResult mergeTwoTargetOneParameter(OpType op, PatternRewriter& rewriter, + bool swappedTargets = false) { + // Check if the successor is the same operation + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); + if (!nextOp) { + return failure(); + } + return mergeTwoTargetOneParameterImpl(op, nextOp, rewriter, swappedTargets); } /** - * @brief Merge two compatible two-target, one-parameter operations where the - * second operation consumes the outputs with swapped targets. + * @brief Merge consecutive XXPlusYY or XXMinusYY operations. * * @details - * This is analogous to mergeTwoTargetOneParameter, but it additionally handles - * the case where the second operation swaps its target qubits. The new - * parameter is computed as the sum of the two original parameters. + * Sums `theta` when `beta` matches within tolerance. Wire-order requirements + * are the same as @ref mergeTwoTargetOneParameter. * * @tparam OpType The type of the operation to be merged. * @param op The operation instance. * @param rewriter The pattern rewriter. - * @return LogicalResult Success or failure of the merge. - */ -template -LogicalResult -mergeTwoTargetOneParameterWithSwappedTargets(OpType op, - PatternRewriter& rewriter) { - return mergeTwoTargetOneParameterImpl(op, rewriter, true); -} - -/** - * @brief Shared implementation for merging two-target, two-parameter - * operations. - * - * @details - * Requires matching secondary parameters (e.g., `beta` for `XXPlusYYOp` and - * `XXMinusYYOp`). The primary parameter (`theta`) at operand index 2 is summed. - * Wire-order requirements are the same as - * @ref mergeTwoTargetOneParameterImpl. - * - * @tparam OpType The type of the operation to be merged. - * @param op The first operation instance. - * @param rewriter The pattern rewriter. * @param swappedTargets Whether the successor consumes swapped target wires. * @return LogicalResult Success or failure of the merge. */ template -static LogicalResult mergeTwoTargetTwoParameterImpl(OpType op, - PatternRewriter& rewriter, - bool swappedTargets) { +LogicalResult mergeXXPlusMinusYY(OpType op, PatternRewriter& rewriter, + bool swappedTargets = false) { + // Check if the successor is the same operation auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } - if (swappedTargets) { - if (op.getOutputQubit(0) != nextOp.getInputQubit(1) || - op.getOutputQubit(1) != nextOp.getInputQubit(0)) { - return failure(); - } - } else if (op.getOutputQubit(1) != nextOp.getInputQubit(1)) { - return failure(); - } - + // Confirm matching beta before summing theta if (!valuesMatchWithinTolerance(op.getBeta(), nextOp.getBeta())) { return failure(); } - - auto newTheta = arith::AddFOp::create(rewriter, op.getLoc(), op.getOperand(2), - nextOp.getOperand(2)); - op->setOperand(2, newTheta.getResult()); - if (swappedTargets) { - rewriter.replaceOp(nextOp, {op.getOutputQubit(1), op.getOutputQubit(0)}); - } else { - rewriter.replaceOp(nextOp, op.getResults()); - } - return success(); -} - -/** - * @brief Merge two compatible two-target, two-parameter operations - * - * @details - * Requires matching secondary parameters (e.g., `beta` for `XXPlusYYOp` and - * `XXMinusYYOp`). The primary parameter (`theta`) is summed. - * - * @tparam OpType The type of the operation to be merged. - * @param op The operation instance. - * @param rewriter The pattern rewriter. - * @return LogicalResult Success or failure of the merge. - */ -template -LogicalResult mergeTwoTargetTwoParameter(OpType op, PatternRewriter& rewriter) { - return mergeTwoTargetTwoParameterImpl(op, rewriter, false); -} - -/** - * @brief Merge two compatible two-target, two-parameter operations - * - * @details - * Requires matching secondary parameters (e.g., `beta` for `XXPlusYYOp` and - * `XXMinusYYOp`). The primary parameter (`theta`) is summed. The second - * operation may consume swapped targets. - * - * @tparam OpType The type of the operation to be merged. - * @param op The operation instance. - * @param rewriter The pattern rewriter. - * @return LogicalResult Success or failure of the merge. - */ -template -LogicalResult -mergeTwoTargetTwoParameterWithSwappedTargets(OpType op, - PatternRewriter& rewriter) { - return mergeTwoTargetTwoParameterImpl(op, rewriter, true); + return mergeTwoTargetOneParameterImpl(op, nextOp, rewriter, swappedTargets); } } // namespace mlir::qco diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp index 60cb98c196..ba58bc747d 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp @@ -31,8 +31,7 @@ struct RemoveInversePairDCX final : OpRewritePattern { LogicalResult matchAndRewrite(DCXOp op, PatternRewriter& rewriter) const override { - return removeTwoTargetZeroParameterPairWithSwappedTargets(op, - rewriter); + return removeInversePairTwoTargetZeroParameter(op, rewriter, true); } }; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp index c7ff9db1a3..7b255e5d51 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp @@ -47,12 +47,12 @@ struct MergeSubsequentP final : OpRewritePattern { * @brief Merge P operations separated only by `ctrl` hops on a control wire * by adding their angles. */ -struct MergePThroughCtrlControlChain final : OpRewritePattern { +struct MergePOnControlWire final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(POp op, PatternRewriter& rewriter) const override { - return mergeOneTargetOneParameterThroughCtrlControlChain(op, rewriter); + return mergeOneTargetOneParameterOnControlWire(op, rewriter); } }; @@ -75,7 +75,7 @@ OpFoldResult POp::fold(FoldAdaptor /*adaptor*/) { void POp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional POp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp index 5b50925dcb..7eaf6db9a4 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp @@ -73,7 +73,20 @@ struct MergeSubsequentR final : OpRewritePattern { LogicalResult matchAndRewrite(ROp op, PatternRewriter& rewriter) const override { - return mergeOneTargetTwoParameter(op, rewriter); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); + if (!nextOp) { + return failure(); + } + + if (!valuesMatchWithinTolerance(op.getPhi(), nextOp.getPhi())) { + return failure(); + } + + auto newParameter = arith::AddFOp::create(rewriter, op.getLoc(), + op.getTheta(), nextOp.getTheta()); + op->setOperand(1, newParameter.getResult()); + rewriter.replaceOp(nextOp, op.getResult()); + return success(); } }; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp index c55edc7f8a..1460e191b0 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp @@ -52,7 +52,7 @@ struct MergeSwappedTargetsRXX final : OpRewritePattern { LogicalResult matchAndRewrite(RXXOp op, PatternRewriter& rewriter) const override { - return mergeTwoTargetOneParameterWithSwappedTargets(op, rewriter); + return mergeTwoTargetOneParameter(op, rewriter, true); } }; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp index 9f59ca471e..5be4041f78 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp @@ -52,7 +52,7 @@ struct MergeSwappedTargetsRYY final : OpRewritePattern { LogicalResult matchAndRewrite(RYYOp op, PatternRewriter& rewriter) const override { - return mergeTwoTargetOneParameterWithSwappedTargets(op, rewriter); + return mergeTwoTargetOneParameter(op, rewriter, true); } }; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp index e62362d0b8..4eb5cd5994 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp @@ -47,12 +47,12 @@ struct MergeSubsequentRZ final : OpRewritePattern { * @brief Merge RZ operations separated only by `ctrl` hops on a control wire * by adding their angles. */ -struct MergeRZThroughCtrlControlChain final : OpRewritePattern { +struct MergeRZOnControlWire final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(RZOp op, PatternRewriter& rewriter) const override { - return mergeOneTargetOneParameterThroughCtrlControlChain(op, rewriter); + return mergeOneTargetOneParameterOnControlWire(op, rewriter); } }; @@ -75,7 +75,7 @@ OpFoldResult RZOp::fold(FoldAdaptor /*adaptor*/) { void RZOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional RZOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp index c290dc69b0..f5bd29fbd8 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp @@ -52,7 +52,7 @@ struct MergeSwappedTargetsRZZ final : OpRewritePattern { LogicalResult matchAndRewrite(RZZOp op, PatternRewriter& rewriter) const override { - return mergeTwoTargetOneParameterWithSwappedTargets(op, rewriter); + return mergeTwoTargetOneParameter(op, rewriter, true); } }; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp index 5b8854dea2..63b244a182 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp @@ -52,13 +52,12 @@ struct MergeSubsequentS final : OpRewritePattern { * @brief Merge S operations separated only by `ctrl` hops on a control wire * into a Z operation. */ -struct MergeSThroughCtrlControlChain final : OpRewritePattern { +struct MergeSOnControlWire final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(SOp op, PatternRewriter& rewriter) const override { - return mergeOneTargetZeroParameterThroughCtrlControlChain(op, - rewriter); + return mergeOneTargetZeroParameterOnControlWire(op, rewriter); } }; @@ -66,8 +65,7 @@ struct MergeSThroughCtrlControlChain final : OpRewritePattern { void SOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add( - context); + results.add(context); } Matrix2x2 SOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp index 673ffffdee..a033931f0f 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp @@ -43,8 +43,7 @@ struct RemoveSwappedTargetsSWAP final : OpRewritePattern { LogicalResult matchAndRewrite(SWAPOp op, PatternRewriter& rewriter) const override { - return removeTwoTargetZeroParameterPairWithSwappedTargets(op, - rewriter); + return removeInversePairTwoTargetZeroParameter(op, rewriter, true); } }; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp index 693f3f6453..4b08d1c279 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp @@ -52,13 +52,12 @@ struct MergeSubsequentSdg final : OpRewritePattern { * @brief Merge Sdg operations separated only by `ctrl` hops on a control wire * into a Z operation. */ -struct MergeSdgThroughCtrlControlChain final : OpRewritePattern { +struct MergeSdgOnControlWire final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(SdgOp op, PatternRewriter& rewriter) const override { - return mergeOneTargetZeroParameterThroughCtrlControlChain(op, - rewriter); + return mergeOneTargetZeroParameterOnControlWire(op, rewriter); } }; @@ -66,8 +65,8 @@ struct MergeSdgThroughCtrlControlChain final : OpRewritePattern { void SdgOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add( + context); } Matrix2x2 SdgOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp index 7937e0fe6f..61cf722e32 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp @@ -53,13 +53,12 @@ struct MergeSubsequentT final : OpRewritePattern { * @brief Merge T operations separated only by `ctrl` hops on a control wire * into an S operation. */ -struct MergeTThroughCtrlControlChain final : OpRewritePattern { +struct MergeTOnControlWire final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(TOp op, PatternRewriter& rewriter) const override { - return mergeOneTargetZeroParameterThroughCtrlControlChain(op, - rewriter); + return mergeOneTargetZeroParameterOnControlWire(op, rewriter); } }; @@ -67,8 +66,7 @@ struct MergeTThroughCtrlControlChain final : OpRewritePattern { void TOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add( - context); + results.add(context); } Matrix2x2 TOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp index 567f1a34b4..237529df3b 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp @@ -54,13 +54,12 @@ struct MergeSubsequentTdg final : OpRewritePattern { * @brief Merge Tdg operations separated only by `ctrl` hops on a control wire * into an Sdg operation. */ -struct MergeTdgThroughCtrlControlChain final : OpRewritePattern { +struct MergeTdgOnControlWire final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(TdgOp op, PatternRewriter& rewriter) const override { - return mergeOneTargetZeroParameterThroughCtrlControlChain(op, - rewriter); + return mergeOneTargetZeroParameterOnControlWire(op, rewriter); } }; @@ -68,8 +67,8 @@ struct MergeTdgThroughCtrlControlChain final : OpRewritePattern { void TdgOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add( + context); } Matrix2x2 TdgOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp index f15686b097..0e6ee555d3 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp @@ -40,7 +40,7 @@ struct MergeSubsequentXXMinusYY final : OpRewritePattern { LogicalResult matchAndRewrite(XXMinusYYOp op, PatternRewriter& rewriter) const override { - return mergeTwoTargetTwoParameter(op, rewriter); + return mergeXXPlusMinusYY(op, rewriter); } }; @@ -53,7 +53,7 @@ struct MergeSwappedTargetsXXMinusYY final : OpRewritePattern { LogicalResult matchAndRewrite(XXMinusYYOp op, PatternRewriter& rewriter) const override { - return mergeTwoTargetTwoParameterWithSwappedTargets(op, rewriter); + return mergeXXPlusMinusYY(op, rewriter, true); } }; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp index 846d3e66c4..948cb1a11f 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp @@ -40,7 +40,7 @@ struct MergeSubsequentXXPlusYY final : OpRewritePattern { LogicalResult matchAndRewrite(XXPlusYYOp op, PatternRewriter& rewriter) const override { - return mergeTwoTargetTwoParameter(op, rewriter); + return mergeXXPlusMinusYY(op, rewriter); } }; @@ -53,7 +53,7 @@ struct MergeSwappedTargetsXXPlusYY final : OpRewritePattern { LogicalResult matchAndRewrite(XXPlusYYOp op, PatternRewriter& rewriter) const override { - return mergeTwoTargetTwoParameterWithSwappedTargets(op, rewriter); + return mergeXXPlusMinusYY(op, rewriter, true); } }; diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp index ca1dd92537..015d5ab168 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp @@ -454,9 +454,8 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(multipleControlledP)}, QCOTestCase{"TwoPOppositePhase", MQT_NAMED_BUILDER(twoPOppositePhase), MQT_NAMED_BUILDER(emptyQCO)}, - QCOTestCase{"TwoPThroughCtrlControlChain", - MQT_NAMED_BUILDER(twoPThroughCtrlControlChain), - MQT_NAMED_BUILDER(twoPThroughCtrlControlChainMerged)})); + QCOTestCase{"TwoPOnControlWire", MQT_NAMED_BUILDER(twoPOnControlWire), + MQT_NAMED_BUILDER(twoPOnControlWireMerged)})); /// @} /// \name QCO/Operations/StandardGates/ROp.cpp @@ -631,13 +630,11 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(multipleControlledRz)}, QCOTestCase{"TwoRZOppositePhase", MQT_NAMED_BUILDER(twoRzOppositePhase), MQT_NAMED_BUILDER(emptyQCO)}, - QCOTestCase{"TwoRZThroughCtrlControlChain", - MQT_NAMED_BUILDER(twoRzThroughCtrlControlChain), - MQT_NAMED_BUILDER(twoRzThroughCtrlControlChainMerged)}, - QCOTestCase{ - "TwoRZThroughNestedCtrlControlChain", - MQT_NAMED_BUILDER(twoRzThroughNestedCtrlControlChain), - MQT_NAMED_BUILDER(twoRzThroughNestedCtrlControlChainMerged)})); + QCOTestCase{"TwoRZOnControlWire", MQT_NAMED_BUILDER(twoRzOnControlWire), + MQT_NAMED_BUILDER(twoRzOnControlWireMerged)}, + QCOTestCase{"TwoRZOnNestedControlWire", + MQT_NAMED_BUILDER(twoRzOnNestedControlWire), + MQT_NAMED_BUILDER(twoRzOnNestedControlWireMerged)})); /// @} /// \name QCO/Operations/StandardGates/RzxOp.cpp @@ -727,9 +724,8 @@ INSTANTIATE_TEST_SUITE_P( QCOTestCase{"SThenSdg", MQT_NAMED_BUILDER(sThenSdg), MQT_NAMED_BUILDER(emptyQCO)}, QCOTestCase{"TwoS", MQT_NAMED_BUILDER(twoS), MQT_NAMED_BUILDER(z)}, - QCOTestCase{"TwoSThroughCtrlControlChain", - MQT_NAMED_BUILDER(twoSThroughCtrlControlChain), - MQT_NAMED_BUILDER(twoSThroughCtrlControlChainMerged)})); + QCOTestCase{"TwoSOnControlWire", MQT_NAMED_BUILDER(twoSOnControlWire), + MQT_NAMED_BUILDER(twoSOnControlWireMerged)})); /// @} /// \name QCO/Operations/StandardGates/SdgOp.cpp @@ -758,9 +754,9 @@ INSTANTIATE_TEST_SUITE_P( QCOTestCase{"SdgThenS", MQT_NAMED_BUILDER(sdgThenS), MQT_NAMED_BUILDER(emptyQCO)}, QCOTestCase{"TwoSdg", MQT_NAMED_BUILDER(twoSdg), MQT_NAMED_BUILDER(z)}, - QCOTestCase{"TwoSdgThroughCtrlControlChain", - MQT_NAMED_BUILDER(twoSdgThroughCtrlControlChain), - MQT_NAMED_BUILDER(twoSdgThroughCtrlControlChainMerged)})); + QCOTestCase{"TwoSdgOnControlWire", + MQT_NAMED_BUILDER(twoSdgOnControlWire), + MQT_NAMED_BUILDER(twoSdgOnControlWireMerged)})); /// @} /// \name QCO/Operations/StandardGates/SwapOp.cpp @@ -871,41 +867,40 @@ INSTANTIATE_TEST_SUITE_P( QCOTestCase{"TThenTdg", MQT_NAMED_BUILDER(tThenTdg), MQT_NAMED_BUILDER(emptyQCO)}, QCOTestCase{"TwoT", MQT_NAMED_BUILDER(twoT), MQT_NAMED_BUILDER(s)}, - QCOTestCase{"TwoTThroughCtrlControlChain", - MQT_NAMED_BUILDER(twoTThroughCtrlControlChain), - MQT_NAMED_BUILDER(twoTThroughCtrlControlChainMerged)})); + QCOTestCase{"TwoTOnControlWire", MQT_NAMED_BUILDER(twoTOnControlWire), + MQT_NAMED_BUILDER(twoTOnControlWireMerged)})); /// @} /// \name QCO/Operations/StandardGates/TdgOp.cpp /// @{ INSTANTIATE_TEST_SUITE_P( QCOTdgOpTest, QCOTest, - testing::Values( - QCOTestCase{"Tdg", MQT_NAMED_BUILDER(tdg), MQT_NAMED_BUILDER(tdg)}, - QCOTestCase{"SingleControlledTdg", - MQT_NAMED_BUILDER(singleControlledTdg), - MQT_NAMED_BUILDER(singleControlledTdg)}, - QCOTestCase{"MultipleControlledTdg", - MQT_NAMED_BUILDER(multipleControlledTdg), - MQT_NAMED_BUILDER(multipleControlledTdg)}, - QCOTestCase{"NestedControlledTdg", - MQT_NAMED_BUILDER(nestedControlledTdg), - MQT_NAMED_BUILDER(multipleControlledTdg)}, - QCOTestCase{"TrivialControlledTdg", - MQT_NAMED_BUILDER(trivialControlledTdg), - MQT_NAMED_BUILDER(tdg)}, - QCOTestCase{"InverseTdg", MQT_NAMED_BUILDER(inverseTdg), - MQT_NAMED_BUILDER(t_)}, - QCOTestCase{"InverseMultipleControlledTdg", - MQT_NAMED_BUILDER(inverseMultipleControlledTdg), - MQT_NAMED_BUILDER(multipleControlledT)}, - QCOTestCase{"TdgThenS", MQT_NAMED_BUILDER(tdgThenT), - MQT_NAMED_BUILDER(emptyQCO)}, - QCOTestCase{"TwoTdg", MQT_NAMED_BUILDER(twoTdg), - MQT_NAMED_BUILDER(sdg)}, - QCOTestCase{"TwoTdgThroughCtrlControlChain", - MQT_NAMED_BUILDER(twoTdgThroughCtrlControlChain), - MQT_NAMED_BUILDER(twoTdgThroughCtrlControlChainMerged)})); + testing::Values(QCOTestCase{"Tdg", MQT_NAMED_BUILDER(tdg), + MQT_NAMED_BUILDER(tdg)}, + QCOTestCase{"SingleControlledTdg", + MQT_NAMED_BUILDER(singleControlledTdg), + MQT_NAMED_BUILDER(singleControlledTdg)}, + QCOTestCase{"MultipleControlledTdg", + MQT_NAMED_BUILDER(multipleControlledTdg), + MQT_NAMED_BUILDER(multipleControlledTdg)}, + QCOTestCase{"NestedControlledTdg", + MQT_NAMED_BUILDER(nestedControlledTdg), + MQT_NAMED_BUILDER(multipleControlledTdg)}, + QCOTestCase{"TrivialControlledTdg", + MQT_NAMED_BUILDER(trivialControlledTdg), + MQT_NAMED_BUILDER(tdg)}, + QCOTestCase{"InverseTdg", MQT_NAMED_BUILDER(inverseTdg), + MQT_NAMED_BUILDER(t_)}, + QCOTestCase{"InverseMultipleControlledTdg", + MQT_NAMED_BUILDER(inverseMultipleControlledTdg), + MQT_NAMED_BUILDER(multipleControlledT)}, + QCOTestCase{"TdgThenS", MQT_NAMED_BUILDER(tdgThenT), + MQT_NAMED_BUILDER(emptyQCO)}, + QCOTestCase{"TwoTdg", MQT_NAMED_BUILDER(twoTdg), + MQT_NAMED_BUILDER(sdg)}, + QCOTestCase{"TwoTdgOnControlWire", + MQT_NAMED_BUILDER(twoTdgOnControlWire), + MQT_NAMED_BUILDER(twoTdgOnControlWireMerged)})); /// @} /// \name QCO/Operations/StandardGates/U2Op.cpp diff --git a/mlir/unittests/programs/qco_programs.cpp b/mlir/unittests/programs/qco_programs.cpp index ba73615ab9..7686b102b1 100644 --- a/mlir/unittests/programs/qco_programs.cpp +++ b/mlir/unittests/programs/qco_programs.cpp @@ -542,14 +542,14 @@ void twoS(QCOProgramBuilder& b) { q[0] = b.s(q[0]); } -void twoSThroughCtrlControlChain(QCOProgramBuilder& b) { +void twoSOnControlWire(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); q[0] = b.s(q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); q[0] = b.s(q[0]); } -void twoSThroughCtrlControlChainMerged(QCOProgramBuilder& b) { +void twoSOnControlWireMerged(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); q[0] = b.z(q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); @@ -615,14 +615,14 @@ void twoSdg(QCOProgramBuilder& b) { q[0] = b.sdg(q[0]); } -void twoSdgThroughCtrlControlChain(QCOProgramBuilder& b) { +void twoSdgOnControlWire(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); q[0] = b.sdg(q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); q[0] = b.sdg(q[0]); } -void twoSdgThroughCtrlControlChainMerged(QCOProgramBuilder& b) { +void twoSdgOnControlWireMerged(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); q[0] = b.z(q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); @@ -687,14 +687,14 @@ void twoT(QCOProgramBuilder& b) { q[0] = b.t(q[0]); } -void twoTThroughCtrlControlChain(QCOProgramBuilder& b) { +void twoTOnControlWire(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); q[0] = b.t(q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); q[0] = b.t(q[0]); } -void twoTThroughCtrlControlChainMerged(QCOProgramBuilder& b) { +void twoTOnControlWireMerged(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); q[0] = b.s(q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); @@ -760,14 +760,14 @@ void twoTdg(QCOProgramBuilder& b) { q[0] = b.tdg(q[0]); } -void twoTdgThroughCtrlControlChain(QCOProgramBuilder& b) { +void twoTdgOnControlWire(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); q[0] = b.tdg(q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); q[0] = b.tdg(q[0]); } -void twoTdgThroughCtrlControlChainMerged(QCOProgramBuilder& b) { +void twoTdgOnControlWireMerged(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); q[0] = b.sdg(q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); @@ -1067,20 +1067,20 @@ void twoRzOppositePhase(QCOProgramBuilder& b) { q[0] = b.rz(-0.789, q[0]); } -void twoRzThroughCtrlControlChain(QCOProgramBuilder& b) { +void twoRzOnControlWire(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); q[0] = b.rz(0.1, q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); q[0] = b.rz(0.2, q[0]); } -void twoRzThroughCtrlControlChainMerged(QCOProgramBuilder& b) { +void twoRzOnControlWireMerged(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); q[0] = b.rz(0.3, q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); } -void twoRzThroughNestedCtrlControlChain(QCOProgramBuilder& b) { +void twoRzOnNestedControlWire(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); q[0] = b.rz(0.1, q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); @@ -1088,7 +1088,7 @@ void twoRzThroughNestedCtrlControlChain(QCOProgramBuilder& b) { q[0] = b.rz(0.2, q[0]); } -void twoRzThroughNestedCtrlControlChainMerged(QCOProgramBuilder& b) { +void twoRzOnNestedControlWireMerged(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); q[0] = b.rz(0.3, q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); @@ -1149,14 +1149,14 @@ void twoPOppositePhase(QCOProgramBuilder& b) { q[0] = b.p(-0.123, q[0]); } -void twoPThroughCtrlControlChain(QCOProgramBuilder& b) { +void twoPOnControlWire(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); q[0] = b.p(0.1, q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); q[0] = b.p(0.2, q[0]); } -void twoPThroughCtrlControlChainMerged(QCOProgramBuilder& b) { +void twoPOnControlWireMerged(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); q[0] = b.p(0.3, q[0]); std::tie(q[0], q[1]) = b.cx(q[0], q[1]); diff --git a/mlir/unittests/programs/qco_programs.h b/mlir/unittests/programs/qco_programs.h index 5e1ff661d8..8f5062b3f6 100644 --- a/mlir/unittests/programs/qco_programs.h +++ b/mlir/unittests/programs/qco_programs.h @@ -289,10 +289,10 @@ void twoS(QCOProgramBuilder& b); /// Creates a circuit with two S gates on a control wire separated by a `ctrl` /// hop. -void twoSThroughCtrlControlChain(QCOProgramBuilder& b); +void twoSOnControlWire(QCOProgramBuilder& b); -/// Creates the merged Z form of @ref twoSThroughCtrlControlChain. -void twoSThroughCtrlControlChainMerged(QCOProgramBuilder& b); +/// Creates the merged Z form of @ref twoSOnControlWire. +void twoSOnControlWireMerged(QCOProgramBuilder& b); // --- SdgOp ---------------------------------------------------------------- // @@ -325,10 +325,10 @@ void twoSdg(QCOProgramBuilder& b); /// Creates a circuit with two Sdg gates on a control wire separated by a `ctrl` /// hop. -void twoSdgThroughCtrlControlChain(QCOProgramBuilder& b); +void twoSdgOnControlWire(QCOProgramBuilder& b); -/// Creates the merged Z form of @ref twoSdgThroughCtrlControlChain. -void twoSdgThroughCtrlControlChainMerged(QCOProgramBuilder& b); +/// Creates the merged Z form of @ref twoSdgOnControlWire. +void twoSdgOnControlWireMerged(QCOProgramBuilder& b); // --- TOp ------------------------------------------------------------------ // @@ -361,10 +361,10 @@ void twoT(QCOProgramBuilder& b); /// Creates a circuit with two T gates on a control wire separated by a `ctrl` /// hop. -void twoTThroughCtrlControlChain(QCOProgramBuilder& b); +void twoTOnControlWire(QCOProgramBuilder& b); -/// Creates the merged S form of @ref twoTThroughCtrlControlChain. -void twoTThroughCtrlControlChainMerged(QCOProgramBuilder& b); +/// Creates the merged S form of @ref twoTOnControlWire. +void twoTOnControlWireMerged(QCOProgramBuilder& b); // --- TdgOp ---------------------------------------------------------------- // @@ -397,10 +397,10 @@ void twoTdg(QCOProgramBuilder& b); /// Creates a circuit with two Tdg gates on a control wire separated by a `ctrl` /// hop. -void twoTdgThroughCtrlControlChain(QCOProgramBuilder& b); +void twoTdgOnControlWire(QCOProgramBuilder& b); -/// Creates the merged Sdg form of @ref twoTdgThroughCtrlControlChain. -void twoTdgThroughCtrlControlChainMerged(QCOProgramBuilder& b); +/// Creates the merged Sdg form of @ref twoTdgOnControlWire. +void twoTdgOnControlWireMerged(QCOProgramBuilder& b); // --- SXOp ----------------------------------------------------------------- // @@ -547,18 +547,18 @@ void twoRzOppositePhase(QCOProgramBuilder& b); /// Creates a circuit with two RZ gates on a control wire separated by a `ctrl` /// hop. -void twoRzThroughCtrlControlChain(QCOProgramBuilder& b); +void twoRzOnControlWire(QCOProgramBuilder& b); -/// Creates the merged single-RZ form of @ref twoRzThroughCtrlControlChain. -void twoRzThroughCtrlControlChainMerged(QCOProgramBuilder& b); +/// Creates the merged single-RZ form of @ref twoRzOnControlWire. +void twoRzOnControlWireMerged(QCOProgramBuilder& b); /// Creates a circuit with two RZ gates on a control wire separated by nested /// `ctrl` hops. -void twoRzThroughNestedCtrlControlChain(QCOProgramBuilder& b); +void twoRzOnNestedControlWire(QCOProgramBuilder& b); /// Creates the merged single-RZ form of @ref -/// twoRzThroughNestedCtrlControlChain. -void twoRzThroughNestedCtrlControlChainMerged(QCOProgramBuilder& b); +/// twoRzOnNestedControlWire. +void twoRzOnNestedControlWireMerged(QCOProgramBuilder& b); // --- POp ------------------------------------------------------------------ // @@ -588,10 +588,10 @@ void twoPOppositePhase(QCOProgramBuilder& b); /// Creates a circuit with two P gates on a control wire separated by a `ctrl` /// hop. -void twoPThroughCtrlControlChain(QCOProgramBuilder& b); +void twoPOnControlWire(QCOProgramBuilder& b); -/// Creates the merged single-P form of @ref twoPThroughCtrlControlChain. -void twoPThroughCtrlControlChainMerged(QCOProgramBuilder& b); +/// Creates the merged single-P form of @ref twoPOnControlWire. +void twoPOnControlWireMerged(QCOProgramBuilder& b); // --- ROp ------------------------------------------------------------------ // From 93f6251a97d6b035b1de8ea85ac7a233aa35973c Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Tue, 16 Jun 2026 10:23:08 +0200 Subject: [PATCH 10/18] =?UTF-8?q?=F0=9F=9A=A8=20Fix=20linter=20warnings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp index 7eaf6db9a4..1b742170fa 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp @@ -13,10 +13,12 @@ #include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/Utils/Utils.h" +#include #include #include #include #include +#include #include #include From 05f9f044bcb12c129a32e2e906388821cd147f96 Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Tue, 16 Jun 2026 10:34:02 +0200 Subject: [PATCH 11/18] =?UTF-8?q?=F0=9F=93=9D=20Clean=20up=20documentation?= =?UTF-8?q?=20in=20QCOUtils.h=20by=20removing=20redundant=20details=20from?= =?UTF-8?q?=20function=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Dialect/QCO/QCOUtils.h | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index 0f4ecbcf37..80ec1b8198 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -287,10 +287,6 @@ mergeOneTargetOneParameterOnControlWire(OpType op, PatternRewriter& rewriter) { * @brief Shared implementation for merging two-target, one-parameter * operations. * - * @details - * Wire-order requirements are enforced via @ref twoTargetWiresMatch. The - * parameter at operand index 2 is summed. - * * @tparam OpType The type of the operation to be merged. * @param op The first operation instance. * @param nextOp The successor operation instance. @@ -323,11 +319,6 @@ static LogicalResult mergeTwoTargetOneParameterImpl(OpType op, OpType nextOp, /** * @brief Merge two compatible two-target, one-parameter operations. * - * @details - * When @p swappedTargets is false, the successor must consume the same target - * wires in the same order. When true, the successor may consume swapped - * targets. The parameter at operand index 2 is summed. - * * @tparam OpType The type of the operation to be merged. * @param op The operation instance. * @param rewriter The pattern rewriter. @@ -349,8 +340,7 @@ LogicalResult mergeTwoTargetOneParameter(OpType op, PatternRewriter& rewriter, * @brief Merge consecutive XXPlusYY or XXMinusYY operations. * * @details - * Sums `theta` when `beta` matches within tolerance. Wire-order requirements - * are the same as @ref mergeTwoTargetOneParameter. + * Sums `theta` when `beta` matches within tolerance. * * @tparam OpType The type of the operation to be merged. * @param op The operation instance. From 0c4adcc07e54d096dd097755854b5ea4fa8711ff Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Tue, 16 Jun 2026 10:56:26 +0200 Subject: [PATCH 12/18] =?UTF-8?q?=E2=98=82=EF=B8=8F=20Increase=20coverage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp | 3 ++- mlir/unittests/programs/qco_programs.cpp | 11 ++++++++++- mlir/unittests/programs/qco_programs.h | 6 +++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp index 015d5ab168..6127233317 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp @@ -482,7 +482,8 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(rx)}, QCOTestCase{"CanonicalizeRToRy", MQT_NAMED_BUILDER(canonicalizeRToRy), MQT_NAMED_BUILDER(ry)}, - QCOTestCase{"TwoR", MQT_NAMED_BUILDER(twoR), MQT_NAMED_BUILDER(r)})); + QCOTestCase{"TwoR", MQT_NAMED_BUILDER(twoR), + MQT_NAMED_BUILDER(twoRMerged)})); /// @} /// \name QCO/Operations/StandardGates/RxOp.cpp diff --git a/mlir/unittests/programs/qco_programs.cpp b/mlir/unittests/programs/qco_programs.cpp index 4fd3ebdcc9..d4c6b24c1e 100644 --- a/mlir/unittests/programs/qco_programs.cpp +++ b/mlir/unittests/programs/qco_programs.cpp @@ -1232,9 +1232,18 @@ void canonicalizeRToRy(QCOProgramBuilder& b) { } void twoR(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(1); + auto q = b.allocQubitRegister(2); q[0] = b.r(0.045, 0.456, q[0]); q[0] = b.r(0.078, 0.456, q[0]); + q[1] = b.r(0.123, 0.789, q[1]); + q[1] = b.r(0.456, -0.789, q[1]); +} + +void twoRMerged(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(2); + q[0] = b.r(0.123, 0.456, q[0]); + q[1] = b.r(0.123, 0.789, q[1]); + q[1] = b.r(0.456, -0.789, q[1]); } void u2(QCOProgramBuilder& b) { diff --git a/mlir/unittests/programs/qco_programs.h b/mlir/unittests/programs/qco_programs.h index 5c3a028984..4e5f105adb 100644 --- a/mlir/unittests/programs/qco_programs.h +++ b/mlir/unittests/programs/qco_programs.h @@ -622,9 +622,13 @@ void canonicalizeRToRx(QCOProgramBuilder& b); /// Creates a circuit with an R gate that can be canonicalized to an RY gate. void canonicalizeRToRy(QCOProgramBuilder& b); -/// Creates a circuit with two R gates in a row with the same `phi`. +/// Creates a circuit with two R gates in a row with the same `phi` on one +/// qubit and opposite `phi` on another. void twoR(QCOProgramBuilder& b); +/// Creates the canonicalized form of @ref twoR. +void twoRMerged(QCOProgramBuilder& b); + // --- U2Op ----------------------------------------------------------------- // /// Creates a circuit with just a U2 gate. From 19dde1a28c274ba6048d1c7480c431ae2661537d Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Tue, 16 Jun 2026 11:14:13 +0200 Subject: [PATCH 13/18] =?UTF-8?q?=F0=9F=8E=A8=20Split=20up=20R=20gate=20me?= =?UTF-8?q?rging=20into=20two=20tests=20again?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp | 5 +++-- mlir/unittests/programs/qco_programs.cpp | 13 +++++-------- mlir/unittests/programs/qco_programs.h | 7 +++---- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp index 6127233317..a1854859e9 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp @@ -482,8 +482,9 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(rx)}, QCOTestCase{"CanonicalizeRToRy", MQT_NAMED_BUILDER(canonicalizeRToRy), MQT_NAMED_BUILDER(ry)}, - QCOTestCase{"TwoR", MQT_NAMED_BUILDER(twoR), - MQT_NAMED_BUILDER(twoRMerged)})); + QCOTestCase{"TwoR", MQT_NAMED_BUILDER(twoR), MQT_NAMED_BUILDER(r)}, + QCOTestCase{"TwoROppositePhase", MQT_NAMED_BUILDER(twoROppositePhase), + MQT_NAMED_BUILDER(twoROppositePhase)})); /// @} /// \name QCO/Operations/StandardGates/RxOp.cpp diff --git a/mlir/unittests/programs/qco_programs.cpp b/mlir/unittests/programs/qco_programs.cpp index d4c6b24c1e..1ed3d27ff6 100644 --- a/mlir/unittests/programs/qco_programs.cpp +++ b/mlir/unittests/programs/qco_programs.cpp @@ -1232,18 +1232,15 @@ void canonicalizeRToRy(QCOProgramBuilder& b) { } void twoR(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); + auto q = b.allocQubitRegister(1); q[0] = b.r(0.045, 0.456, q[0]); q[0] = b.r(0.078, 0.456, q[0]); - q[1] = b.r(0.123, 0.789, q[1]); - q[1] = b.r(0.456, -0.789, q[1]); } -void twoRMerged(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.r(0.123, 0.456, q[0]); - q[1] = b.r(0.123, 0.789, q[1]); - q[1] = b.r(0.456, -0.789, q[1]); +void twoROppositePhase(QCOProgramBuilder& b) { + auto q = b.allocQubitRegister(1); + q[0] = b.r(0.123, 0.789, q[0]); + q[0] = b.r(0.456, -0.789, q[0]); } void u2(QCOProgramBuilder& b) { diff --git a/mlir/unittests/programs/qco_programs.h b/mlir/unittests/programs/qco_programs.h index 4e5f105adb..bbba7f9875 100644 --- a/mlir/unittests/programs/qco_programs.h +++ b/mlir/unittests/programs/qco_programs.h @@ -622,12 +622,11 @@ void canonicalizeRToRx(QCOProgramBuilder& b); /// Creates a circuit with an R gate that can be canonicalized to an RY gate. void canonicalizeRToRy(QCOProgramBuilder& b); -/// Creates a circuit with two R gates in a row with the same `phi` on one -/// qubit and opposite `phi` on another. +/// Creates a circuit with two R gates in a row with the same `phi`. void twoR(QCOProgramBuilder& b); -/// Creates the canonicalized form of @ref twoR. -void twoRMerged(QCOProgramBuilder& b); +/// Creates a circuit with two R gates in a row with opposite `phi` angles. +void twoROppositePhase(QCOProgramBuilder& b); // --- U2Op ----------------------------------------------------------------- // From 5192872a5bb6ffa8da1d581dc8b482351042fdf7 Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Tue, 16 Jun 2026 13:06:00 +0200 Subject: [PATCH 14/18] =?UTF-8?q?=F0=9F=94=A5=20Remove=20`twoROppositePhas?= =?UTF-8?q?e`=20function=20and=20related=20test=20case=20from=20QCO=20unit?= =?UTF-8?q?=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp | 4 +--- mlir/unittests/programs/qco_programs.cpp | 6 ------ mlir/unittests/programs/qco_programs.h | 3 --- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp index a1854859e9..015d5ab168 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp @@ -482,9 +482,7 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(rx)}, QCOTestCase{"CanonicalizeRToRy", MQT_NAMED_BUILDER(canonicalizeRToRy), MQT_NAMED_BUILDER(ry)}, - QCOTestCase{"TwoR", MQT_NAMED_BUILDER(twoR), MQT_NAMED_BUILDER(r)}, - QCOTestCase{"TwoROppositePhase", MQT_NAMED_BUILDER(twoROppositePhase), - MQT_NAMED_BUILDER(twoROppositePhase)})); + QCOTestCase{"TwoR", MQT_NAMED_BUILDER(twoR), MQT_NAMED_BUILDER(r)})); /// @} /// \name QCO/Operations/StandardGates/RxOp.cpp diff --git a/mlir/unittests/programs/qco_programs.cpp b/mlir/unittests/programs/qco_programs.cpp index 1ed3d27ff6..4fd3ebdcc9 100644 --- a/mlir/unittests/programs/qco_programs.cpp +++ b/mlir/unittests/programs/qco_programs.cpp @@ -1237,12 +1237,6 @@ void twoR(QCOProgramBuilder& b) { q[0] = b.r(0.078, 0.456, q[0]); } -void twoROppositePhase(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(1); - q[0] = b.r(0.123, 0.789, q[0]); - q[0] = b.r(0.456, -0.789, q[0]); -} - void u2(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.u2(0.234, 0.567, q[0]); diff --git a/mlir/unittests/programs/qco_programs.h b/mlir/unittests/programs/qco_programs.h index bbba7f9875..5c3a028984 100644 --- a/mlir/unittests/programs/qco_programs.h +++ b/mlir/unittests/programs/qco_programs.h @@ -625,9 +625,6 @@ void canonicalizeRToRy(QCOProgramBuilder& b); /// Creates a circuit with two R gates in a row with the same `phi`. void twoR(QCOProgramBuilder& b); -/// Creates a circuit with two R gates in a row with opposite `phi` angles. -void twoROppositePhase(QCOProgramBuilder& b); - // --- U2Op ----------------------------------------------------------------- // /// Creates a circuit with just a U2 gate. From 30cfb278922bd23fdbbd0dfa0682b306f2155c63 Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Tue, 16 Jun 2026 13:08:19 +0200 Subject: [PATCH 15/18] =?UTF-8?q?=F0=9F=8E=A8=20Fix=20logic=20in=20`twoTar?= =?UTF-8?q?getWiresMatch`=20function=20to=20ensure=20correct=20qubit=20mat?= =?UTF-8?q?ching?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Dialect/QCO/QCOUtils.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index 80ec1b8198..0a4c53827e 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -38,7 +38,8 @@ static bool twoTargetWiresMatch(OpType op, OpType nextOp, bool swappedTargets) { return op.getOutputQubit(0) == nextOp.getInputQubit(1) && op.getOutputQubit(1) == nextOp.getInputQubit(0); } - return op.getOutputQubit(1) == nextOp.getInputQubit(1); + return op.getOutputQubit(0) == nextOp.getInputQubit(0) && + op.getOutputQubit(1) == nextOp.getInputQubit(1); } /** From ded2ca0d9ac54124d624bcec0843d970199eb648 Mon Sep 17 00:00:00 2001 From: Simon Hofmann Date: Tue, 16 Jun 2026 13:46:10 +0200 Subject: [PATCH 16/18] =?UTF-8?q?=F0=9F=93=9D=20Update=20CHANGELOG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 941d811227..6a10522165 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel - ✨ Add conversions between `jeff` and QCO ([#1479], [#1548], [#1565], [#1637], [#1676], [#1706], [#1776]) ([**@denialhaag**], [**@burgholzer**]) - ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588], [#1600], [#1664], [#1709], [#1716], [#1748]) ([**@MatthiasReumann**], [**@burgholzer**]) - ✨ Add initial infrastructure for new QC and QCO MLIR dialects - ([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635], [#1638], [#1673], [#1675], [#1700], [#1710], [#1717], [#1728], [#1730], [#1749], [#1751], [#1762], [#1765], [#1774], [#1781]) + ([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635], [#1638], [#1673], [#1675], [#1700], [#1710], [#1717], [#1728], [#1730], [#1749], [#1751], [#1762], [#1765], [#1774], [#1781], [#1782]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**]) ### Changed @@ -402,6 +402,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool +[#1782]: https://github.com/munich-quantum-toolkit/core/pull/1782 [#1781]: https://github.com/munich-quantum-toolkit/core/pull/1781 [#1776]: https://github.com/munich-quantum-toolkit/core/pull/1776 [#1774]: https://github.com/munich-quantum-toolkit/core/pull/1774 From d2a7fbfe0bcb5e029246ebf38797dc0c3a15b39b Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Wed, 17 Jun 2026 21:13:21 +0200 Subject: [PATCH 17/18] =?UTF-8?q?=E2=9A=A1=20Simplify=20canonicalization?= =?UTF-8?q?=20patterns=20for=20gate=20cancellation=20and=20merging?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds direct handling for symmetric gates, which halves the number of patterns for certain operations. Furthermore, optimizes the implementation itself. Signed-off-by: Lukas Burgholzer --- mlir/include/mlir/Dialect/QCO/QCOUtils.h | 104 +++++++----------- .../QCO/IR/Operations/StandardGates/DCXOp.cpp | 3 +- .../QCO/IR/Operations/StandardGates/RXXOp.cpp | 15 +-- .../QCO/IR/Operations/StandardGates/RYYOp.cpp | 15 +-- .../QCO/IR/Operations/StandardGates/RZZOp.cpp | 15 +-- .../IR/Operations/StandardGates/SWAPOp.cpp | 15 +-- .../Operations/StandardGates/XXMinusYYOp.cpp | 15 +-- .../Operations/StandardGates/XXPlusYYOp.cpp | 15 +-- 8 files changed, 48 insertions(+), 149 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index 0a4c53827e..79ea3c7472 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -23,25 +23,6 @@ namespace mlir::qco { -/** - * @brief Check whether two two-target operations consume the same target wires. - * - * @tparam OpType The type of the operation to be checked. - * @param op The first operation instance. - * @param nextOp The successor operation instance. - * @param swappedTargets Whether @p nextOp consumes swapped target wires. - * @return true if the wire order matches. - */ -template -static bool twoTargetWiresMatch(OpType op, OpType nextOp, bool swappedTargets) { - if (swappedTargets) { - return op.getOutputQubit(0) == nextOp.getInputQubit(1) && - op.getOutputQubit(1) == nextOp.getInputQubit(0); - } - return op.getOutputQubit(0) == nextOp.getInputQubit(0) && - op.getOutputQubit(1) == nextOp.getInputQubit(1); -} - /** * @brief Check whether two parameter values match. * @@ -53,7 +34,7 @@ static bool twoTargetWiresMatch(OpType op, OpType nextOp, bool swappedTargets) { * @param rhs The second parameter value. * @return true if the values match. */ -static inline bool valuesMatchWithinTolerance(Value lhs, Value rhs) { +static bool valuesMatchWithinTolerance(Value lhs, Value rhs) { if (lhs == rhs) { return true; } @@ -119,55 +100,49 @@ removeInversePairOneTargetZeroParameter(OpType op, PatternRewriter& rewriter) { } // Erase both operations - rewriter.replaceAllUsesWith(nextOp->getResult(0), op.getInputQubit(0)); - rewriter.eraseOp(nextOp); - rewriter.eraseOp(op); - + rewriter.replaceOp(op, op.getInputQubits()); + rewriter.replaceOp(nextOp, nextOp.getInputQubits()); return success(); } /** * @brief Remove a pair of inverse two-target, zero-parameter operations. * - * @details - * When @p swappedTargets is false, the successor must consume the same target - * wires in the same order. When true, the successor may consume swapped - * targets. - * * @tparam InverseOpType The type of the inverse operation. * @tparam OpType The type of the operation to be checked. * @param op The operation instance. * @param rewriter The pattern rewriter. + * @param symmetric Whether the two-target gate is symmetric (order of the + * qubits does not matter) * @param swappedTargets Whether the successor consumes swapped target wires. * @return LogicalResult Success or failure of the removal. */ template LogicalResult removeInversePairTwoTargetZeroParameter(OpType op, PatternRewriter& rewriter, + bool symmetric = false, bool swappedTargets = false) { + auto output0 = op.getOutputQubit(0); + // Check if the successor is the inverse operation - auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*output0.user_begin()); if (!nextOp) { return failure(); } - if (!twoTargetWiresMatch(op, nextOp, swappedTargets)) { + // Both qubits have to point to the same successor + auto nextOp2 = *op.getOutputQubit(1).user_begin(); + if (nextOp2 != nextOp) { return failure(); } - if (swappedTargets) { - rewriter.replaceAllUsesWith(nextOp->getResults(), - {op.getInputQubit(1), op.getInputQubit(0)}); - } else { - rewriter.replaceAllUsesWith(nextOp->getResults(), - {op.getInputQubit(0), op.getInputQubit(1)}); + if (symmetric || (swappedTargets && output0 == nextOp.getInputQubit(1)) || + (!swappedTargets && output0 == nextOp.getInputQubit(0))) { + rewriter.replaceOp(op, op.getInputQubits()); + rewriter.replaceOp(nextOp, nextOp.getInputQubits()); + return success(); } - - // Erase both operations - rewriter.eraseOp(nextOp); - rewriter.eraseOp(op); - - return success(); + return failure(); } /** @@ -292,29 +267,31 @@ mergeOneTargetOneParameterOnControlWire(OpType op, PatternRewriter& rewriter) { * @param op The first operation instance. * @param nextOp The successor operation instance. * @param rewriter The pattern rewriter. - * @param swappedTargets Whether the successor consumes swapped target wires. + * @param symmetric Whether the two-target gate is symmetric (order of the + * qubits does not matter) * @return LogicalResult Success or failure of the merge. */ template static LogicalResult mergeTwoTargetOneParameterImpl(OpType op, OpType nextOp, PatternRewriter& rewriter, - bool swappedTargets) { - if (!twoTargetWiresMatch(op, nextOp, swappedTargets)) { + bool symmetric = false) { + + // Both qubits have to point to the same successor + auto nextOp2 = *op.getOutputQubit(1).user_begin(); + if (nextOp2 != nextOp) { return failure(); } - // Compute and set the new parameter - auto newParameter = arith::AddFOp::create( - rewriter, op.getLoc(), op.getOperand(2), nextOp.getOperand(2)); - op->setOperand(2, newParameter.getResult()); - if (swappedTargets) { - // nextOp results correspond to swapped operands, so swap replacements too - rewriter.replaceOp(nextOp, {op.getOutputQubit(1), op.getOutputQubit(0)}); - } else { - // Replace the second operation with the result of the first operation - rewriter.replaceOp(nextOp, op.getResults()); + auto output0 = op.getOutputQubit(0); + if (symmetric || output0 == nextOp.getInputQubit(0)) { + // Compute and set the new parameter + auto newParameter = arith::AddFOp::create( + rewriter, op.getLoc(), op.getOperand(2), nextOp.getOperand(2)); + op->setOperand(2, newParameter.getResult()); + rewriter.replaceOp(nextOp, nextOp.getInputQubits()); + return success(); } - return success(); + return failure(); } /** @@ -323,18 +300,19 @@ static LogicalResult mergeTwoTargetOneParameterImpl(OpType op, OpType nextOp, * @tparam OpType The type of the operation to be merged. * @param op The operation instance. * @param rewriter The pattern rewriter. - * @param swappedTargets Whether the successor consumes swapped target wires. + * @param symmetric Whether the two-target gate is symmetric (order of the + * qubits does not matter) * @return LogicalResult Success or failure of the merge. */ template LogicalResult mergeTwoTargetOneParameter(OpType op, PatternRewriter& rewriter, - bool swappedTargets = false) { + bool symmetric = false) { // Check if the successor is the same operation auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } - return mergeTwoTargetOneParameterImpl(op, nextOp, rewriter, swappedTargets); + return mergeTwoTargetOneParameterImpl(op, nextOp, rewriter, symmetric); } /** @@ -346,12 +324,10 @@ LogicalResult mergeTwoTargetOneParameter(OpType op, PatternRewriter& rewriter, * @tparam OpType The type of the operation to be merged. * @param op The operation instance. * @param rewriter The pattern rewriter. - * @param swappedTargets Whether the successor consumes swapped target wires. * @return LogicalResult Success or failure of the merge. */ template -LogicalResult mergeXXPlusMinusYY(OpType op, PatternRewriter& rewriter, - bool swappedTargets = false) { +LogicalResult mergeXXPlusMinusYY(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { @@ -362,7 +338,7 @@ LogicalResult mergeXXPlusMinusYY(OpType op, PatternRewriter& rewriter, if (!valuesMatchWithinTolerance(op.getBeta(), nextOp.getBeta())) { return failure(); } - return mergeTwoTargetOneParameterImpl(op, nextOp, rewriter, swappedTargets); + return mergeTwoTargetOneParameterImpl(op, nextOp, rewriter, true); } } // namespace mlir::qco diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp index ba58bc747d..eaf442a5ad 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp @@ -31,7 +31,8 @@ struct RemoveInversePairDCX final : OpRewritePattern { LogicalResult matchAndRewrite(DCXOp op, PatternRewriter& rewriter) const override { - return removeInversePairTwoTargetZeroParameter(op, rewriter, true); + return removeInversePairTwoTargetZeroParameter(op, rewriter, false, + true); } }; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp index 1460e191b0..3bd350bf27 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp @@ -37,19 +37,6 @@ namespace { struct MergeSubsequentRXX final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; - LogicalResult matchAndRewrite(RXXOp op, - PatternRewriter& rewriter) const override { - return mergeTwoTargetOneParameter(op, rewriter); - } -}; - -/** - * @brief Merge subsequent RXX operations with swapped targets by adding their - * angles. - */ -struct MergeSwappedTargetsRXX final : OpRewritePattern { - using OpRewritePattern::OpRewritePattern; - LogicalResult matchAndRewrite(RXXOp op, PatternRewriter& rewriter) const override { return mergeTwoTargetOneParameter(op, rewriter, true); @@ -79,7 +66,7 @@ LogicalResult RXXOp::fold(FoldAdaptor /*adaptor*/, void RXXOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional RXXOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp index 5be4041f78..fb99642f64 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp @@ -37,19 +37,6 @@ namespace { struct MergeSubsequentRYY final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; - LogicalResult matchAndRewrite(RYYOp op, - PatternRewriter& rewriter) const override { - return mergeTwoTargetOneParameter(op, rewriter); - } -}; - -/** - * @brief Merge subsequent RYY operations with swapped targets by adding their - * angles. - */ -struct MergeSwappedTargetsRYY final : OpRewritePattern { - using OpRewritePattern::OpRewritePattern; - LogicalResult matchAndRewrite(RYYOp op, PatternRewriter& rewriter) const override { return mergeTwoTargetOneParameter(op, rewriter, true); @@ -79,7 +66,7 @@ LogicalResult RYYOp::fold(FoldAdaptor /*adaptor*/, void RYYOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional RYYOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp index f5bd29fbd8..a10bd15c0c 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp @@ -37,19 +37,6 @@ namespace { struct MergeSubsequentRZZ final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; - LogicalResult matchAndRewrite(RZZOp op, - PatternRewriter& rewriter) const override { - return mergeTwoTargetOneParameter(op, rewriter); - } -}; - -/** - * @brief Merge subsequent RZZ operations with swapped targets by adding their - * angles. - */ -struct MergeSwappedTargetsRZZ final : OpRewritePattern { - using OpRewritePattern::OpRewritePattern; - LogicalResult matchAndRewrite(RZZOp op, PatternRewriter& rewriter) const override { return mergeTwoTargetOneParameter(op, rewriter, true); @@ -79,7 +66,7 @@ LogicalResult RZZOp::fold(FoldAdaptor /*adaptor*/, void RZZOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional RZZOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp index a033931f0f..676be346de 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp @@ -28,19 +28,6 @@ namespace { struct RemoveSubsequentSWAP final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; - LogicalResult matchAndRewrite(SWAPOp op, - PatternRewriter& rewriter) const override { - return removeInversePairTwoTargetZeroParameter(op, rewriter); - } -}; - -/** - * @brief Remove a SWAP operation followed by a SWAP operation with swapped - * targets. - */ -struct RemoveSwappedTargetsSWAP final : OpRewritePattern { - using OpRewritePattern::OpRewritePattern; - LogicalResult matchAndRewrite(SWAPOp op, PatternRewriter& rewriter) const override { return removeInversePairTwoTargetZeroParameter(op, rewriter, true); @@ -51,7 +38,7 @@ struct RemoveSwappedTargetsSWAP final : OpRewritePattern { void SWAPOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } Matrix4x4 SWAPOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp index 0e6ee555d3..c75c45f26e 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp @@ -44,19 +44,6 @@ struct MergeSubsequentXXMinusYY final : OpRewritePattern { } }; -/** - * @brief Merge XXMinusYY operations with swapped target wires by adding their - * thetas. - */ -struct MergeSwappedTargetsXXMinusYY final : OpRewritePattern { - using OpRewritePattern::OpRewritePattern; - - LogicalResult matchAndRewrite(XXMinusYYOp op, - PatternRewriter& rewriter) const override { - return mergeXXPlusMinusYY(op, rewriter, true); - } -}; - } // namespace void XXMinusYYOp::build(OpBuilder& odsBuilder, OperationState& odsState, @@ -82,7 +69,7 @@ LogicalResult XXMinusYYOp::fold(FoldAdaptor /*adaptor*/, void XXMinusYYOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional XXMinusYYOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp index 948cb1a11f..6511b2344b 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp @@ -44,19 +44,6 @@ struct MergeSubsequentXXPlusYY final : OpRewritePattern { } }; -/** - * @brief Merge XXPlusYY operations with swapped target wires by adding their - * thetas. - */ -struct MergeSwappedTargetsXXPlusYY final : OpRewritePattern { - using OpRewritePattern::OpRewritePattern; - - LogicalResult matchAndRewrite(XXPlusYYOp op, - PatternRewriter& rewriter) const override { - return mergeXXPlusMinusYY(op, rewriter, true); - } -}; - } // namespace void XXPlusYYOp::build(OpBuilder& odsBuilder, OperationState& odsState, @@ -82,7 +69,7 @@ LogicalResult XXPlusYYOp::fold(FoldAdaptor /*adaptor*/, void XXPlusYYOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional XXPlusYYOp::getUnitaryMatrix() { From a3a39408b5cc9f950fb5ee205d3df7dd0b2ee7f7 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Wed, 17 Jun 2026 21:34:58 +0200 Subject: [PATCH 18/18] =?UTF-8?q?=E2=8F=AA=20Revert=20changes=20for=20cont?= =?UTF-8?q?rol=20wire=20canonicalizations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lukas Burgholzer --- mlir/include/mlir/Dialect/QCO/QCOUtils.h | 94 ------------------- .../QCO/IR/Operations/StandardGates/POp.cpp | 15 +-- .../QCO/IR/Operations/StandardGates/RZOp.cpp | 15 +-- .../QCO/IR/Operations/StandardGates/SOp.cpp | 15 +-- .../QCO/IR/Operations/StandardGates/SdgOp.cpp | 16 +--- .../QCO/IR/Operations/StandardGates/TOp.cpp | 15 +-- .../QCO/IR/Operations/StandardGates/TdgOp.cpp | 16 +--- mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp | 72 ++++++-------- mlir/unittests/programs/qco_programs.cpp | 93 ------------------ mlir/unittests/programs/qco_programs.h | 50 ---------- 10 files changed, 34 insertions(+), 367 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index 79ea3c7472..4b944d81d6 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -12,15 +12,12 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" -#include #include #include #include #include #include -#include - namespace mlir::qco { /** @@ -43,44 +40,6 @@ static bool valuesMatchWithinTolerance(Value lhs, Value rhs) { return lhsVal && rhsVal && std::abs(*lhsVal - *rhsVal) <= utils::TOLERANCE; } -/** - * @brief Find a same-type partner operation on a control wire. - * - * @details - * Walks `ctrl` hops on the control wire to find a matching operation. - * Returns failure when no partner exists or when the partner is directly - * adjacent on the same wire (zero `ctrl` hops). - * - * @tparam OpType The type of the operation to be merged. - * @param op The first operation instance. - * @return FailureOr The partner operation, or failure. - */ -template -static FailureOr findPartnerOnControlWire(OpType op) { - Value v = op->getResult(0); - if (!isa(v.getType())) { - return failure(); - } - - unsigned hops = 0; - while (v.hasOneUse()) { - auto* user = *v.getUsers().begin(); - if (auto next = dyn_cast(user); next && next->getOperand(0) == v) { - if (hops == 0) { - return failure(); - } - return next; - } - auto ctrl = dyn_cast(user); - if (!ctrl || !llvm::is_contained(ctrl.getControlsIn(), v)) { - return failure(); - } - v = ctrl.getOutputForInput(v); - ++hops; - } - return failure(); -} - /** * @brief Remove a pair of inverse one-target, zero-parameter operations * @@ -206,59 +165,6 @@ LogicalResult mergeOneTargetOneParameter(OpType op, PatternRewriter& rewriter) { return success(); } -/** - * @brief Merge Z-diagonal one-target, zero-parameter gates on a control wire. - * - * @details - * Replaces `op; ...; op` on a control wire with `square; ...` (e.g., `s; ctrl; - * s` → `z; ctrl`). - * - * @tparam SquareOpType Result of squaring the gate (e.g. `ZOp` for `SOp`). - * @tparam OpType The Z-diagonal operation to be merged. - * @param op The operation instance. - * @param rewriter The pattern rewriter. - * @return LogicalResult Success or failure of the merge. - */ -template -LogicalResult -mergeOneTargetZeroParameterOnControlWire(OpType op, PatternRewriter& rewriter) { - auto partner = findPartnerOnControlWire(op); - if (failed(partner)) { - return failure(); - } - - rewriter.replaceOpWithNewOp(op, op.getInputQubit(0)); - rewriter.replaceOp(*partner, partner->getInputQubit(0)); - return success(); -} - -/** - * @brief Merge Z-diagonal one-target, one-parameter gate angles on a control - * wire. - * - * @tparam OpType The type of the Z-diagonal operation to be merged. - * @param op The operation instance. - * @param rewriter The pattern rewriter. - * @return LogicalResult Success or failure of the merge. - */ -template -LogicalResult -mergeOneTargetOneParameterOnControlWire(OpType op, PatternRewriter& rewriter) { - auto partner = findPartnerOnControlWire(op); - if (failed(partner)) { - return failure(); - } - - // Compute and set the new parameter - auto newParameter = arith::AddFOp::create( - rewriter, op.getLoc(), op.getOperand(1), partner->getOperand(1)); - op->setOperand(1, newParameter.getResult()); - - // Replace the partner operation with the input of the first operation - rewriter.replaceOp(*partner, partner->getOperand(0)); - return success(); -} - /** * @brief Shared implementation for merging two-target, one-parameter * operations. diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp index 7b255e5d51..fa7c5c8e22 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp @@ -43,19 +43,6 @@ struct MergeSubsequentP final : OpRewritePattern { } }; -/** - * @brief Merge P operations separated only by `ctrl` hops on a control wire - * by adding their angles. - */ -struct MergePOnControlWire final : OpRewritePattern { - using OpRewritePattern::OpRewritePattern; - - LogicalResult matchAndRewrite(POp op, - PatternRewriter& rewriter) const override { - return mergeOneTargetOneParameterOnControlWire(op, rewriter); - } -}; - } // namespace void POp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, @@ -75,7 +62,7 @@ OpFoldResult POp::fold(FoldAdaptor /*adaptor*/) { void POp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional POp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp index 4eb5cd5994..288e9f0d0f 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp @@ -43,19 +43,6 @@ struct MergeSubsequentRZ final : OpRewritePattern { } }; -/** - * @brief Merge RZ operations separated only by `ctrl` hops on a control wire - * by adding their angles. - */ -struct MergeRZOnControlWire final : OpRewritePattern { - using OpRewritePattern::OpRewritePattern; - - LogicalResult matchAndRewrite(RZOp op, - PatternRewriter& rewriter) const override { - return mergeOneTargetOneParameterOnControlWire(op, rewriter); - } -}; - } // namespace void RZOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, @@ -75,7 +62,7 @@ OpFoldResult RZOp::fold(FoldAdaptor /*adaptor*/) { void RZOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } std::optional RZOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp index 63b244a182..768bddfcc0 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SOp.cpp @@ -48,24 +48,11 @@ struct MergeSubsequentS final : OpRewritePattern { } }; -/** - * @brief Merge S operations separated only by `ctrl` hops on a control wire - * into a Z operation. - */ -struct MergeSOnControlWire final : OpRewritePattern { - using OpRewritePattern::OpRewritePattern; - - LogicalResult matchAndRewrite(SOp op, - PatternRewriter& rewriter) const override { - return mergeOneTargetZeroParameterOnControlWire(op, rewriter); - } -}; - } // namespace void SOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } Matrix2x2 SOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp index 4b08d1c279..1a9110d312 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SdgOp.cpp @@ -48,25 +48,11 @@ struct MergeSubsequentSdg final : OpRewritePattern { } }; -/** - * @brief Merge Sdg operations separated only by `ctrl` hops on a control wire - * into a Z operation. - */ -struct MergeSdgOnControlWire final : OpRewritePattern { - using OpRewritePattern::OpRewritePattern; - - LogicalResult matchAndRewrite(SdgOp op, - PatternRewriter& rewriter) const override { - return mergeOneTargetZeroParameterOnControlWire(op, rewriter); - } -}; - } // namespace void SdgOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add( - context); + results.add(context); } Matrix2x2 SdgOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp index 61cf722e32..8e9a90e582 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TOp.cpp @@ -49,24 +49,11 @@ struct MergeSubsequentT final : OpRewritePattern { } }; -/** - * @brief Merge T operations separated only by `ctrl` hops on a control wire - * into an S operation. - */ -struct MergeTOnControlWire final : OpRewritePattern { - using OpRewritePattern::OpRewritePattern; - - LogicalResult matchAndRewrite(TOp op, - PatternRewriter& rewriter) const override { - return mergeOneTargetZeroParameterOnControlWire(op, rewriter); - } -}; - } // namespace void TOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add(context); + results.add(context); } Matrix2x2 TOp::getUnitaryMatrix() { diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp index 237529df3b..ca093fd81d 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/TdgOp.cpp @@ -50,25 +50,11 @@ struct MergeSubsequentTdg final : OpRewritePattern { } }; -/** - * @brief Merge Tdg operations separated only by `ctrl` hops on a control wire - * into an Sdg operation. - */ -struct MergeTdgOnControlWire final : OpRewritePattern { - using OpRewritePattern::OpRewritePattern; - - LogicalResult matchAndRewrite(TdgOp op, - PatternRewriter& rewriter) const override { - return mergeOneTargetZeroParameterOnControlWire(op, rewriter); - } -}; - } // namespace void TdgOp::getCanonicalizationPatterns(RewritePatternSet& results, MLIRContext* context) { - results.add( - context); + results.add(context); } Matrix2x2 TdgOp::getUnitaryMatrix() { diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp index 015d5ab168..26fca1733f 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp @@ -453,9 +453,7 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(inverseMultipleControlledP), MQT_NAMED_BUILDER(multipleControlledP)}, QCOTestCase{"TwoPOppositePhase", MQT_NAMED_BUILDER(twoPOppositePhase), - MQT_NAMED_BUILDER(emptyQCO)}, - QCOTestCase{"TwoPOnControlWire", MQT_NAMED_BUILDER(twoPOnControlWire), - MQT_NAMED_BUILDER(twoPOnControlWireMerged)})); + MQT_NAMED_BUILDER(emptyQCO)})); /// @} /// \name QCO/Operations/StandardGates/ROp.cpp @@ -629,12 +627,7 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(inverseMultipleControlledRz), MQT_NAMED_BUILDER(multipleControlledRz)}, QCOTestCase{"TwoRZOppositePhase", MQT_NAMED_BUILDER(twoRzOppositePhase), - MQT_NAMED_BUILDER(emptyQCO)}, - QCOTestCase{"TwoRZOnControlWire", MQT_NAMED_BUILDER(twoRzOnControlWire), - MQT_NAMED_BUILDER(twoRzOnControlWireMerged)}, - QCOTestCase{"TwoRZOnNestedControlWire", - MQT_NAMED_BUILDER(twoRzOnNestedControlWire), - MQT_NAMED_BUILDER(twoRzOnNestedControlWireMerged)})); + MQT_NAMED_BUILDER(emptyQCO)})); /// @} /// \name QCO/Operations/StandardGates/RzxOp.cpp @@ -723,40 +716,36 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(multipleControlledSdg)}, QCOTestCase{"SThenSdg", MQT_NAMED_BUILDER(sThenSdg), MQT_NAMED_BUILDER(emptyQCO)}, - QCOTestCase{"TwoS", MQT_NAMED_BUILDER(twoS), MQT_NAMED_BUILDER(z)}, - QCOTestCase{"TwoSOnControlWire", MQT_NAMED_BUILDER(twoSOnControlWire), - MQT_NAMED_BUILDER(twoSOnControlWireMerged)})); + QCOTestCase{"TwoS", MQT_NAMED_BUILDER(twoS), MQT_NAMED_BUILDER(z)})); /// @} /// \name QCO/Operations/StandardGates/SdgOp.cpp /// @{ INSTANTIATE_TEST_SUITE_P( QCOSdgOpTest, QCOTest, - testing::Values( - QCOTestCase{"Sdg", MQT_NAMED_BUILDER(sdg), MQT_NAMED_BUILDER(sdg)}, - QCOTestCase{"SingleControlledSdg", - MQT_NAMED_BUILDER(singleControlledSdg), - MQT_NAMED_BUILDER(singleControlledSdg)}, - QCOTestCase{"MultipleControlledSdg", - MQT_NAMED_BUILDER(multipleControlledSdg), - MQT_NAMED_BUILDER(multipleControlledSdg)}, - QCOTestCase{"NestedControlledSdg", - MQT_NAMED_BUILDER(nestedControlledSdg), - MQT_NAMED_BUILDER(multipleControlledSdg)}, - QCOTestCase{"TrivialControlledSdg", - MQT_NAMED_BUILDER(trivialControlledSdg), - MQT_NAMED_BUILDER(sdg)}, - QCOTestCase{"InverseSdg", MQT_NAMED_BUILDER(inverseSdg), - MQT_NAMED_BUILDER(s)}, - QCOTestCase{"InverseMultipleControlledSdg", - MQT_NAMED_BUILDER(inverseMultipleControlledSdg), - MQT_NAMED_BUILDER(multipleControlledS)}, - QCOTestCase{"SdgThenS", MQT_NAMED_BUILDER(sdgThenS), - MQT_NAMED_BUILDER(emptyQCO)}, - QCOTestCase{"TwoSdg", MQT_NAMED_BUILDER(twoSdg), MQT_NAMED_BUILDER(z)}, - QCOTestCase{"TwoSdgOnControlWire", - MQT_NAMED_BUILDER(twoSdgOnControlWire), - MQT_NAMED_BUILDER(twoSdgOnControlWireMerged)})); + testing::Values(QCOTestCase{"Sdg", MQT_NAMED_BUILDER(sdg), + MQT_NAMED_BUILDER(sdg)}, + QCOTestCase{"SingleControlledSdg", + MQT_NAMED_BUILDER(singleControlledSdg), + MQT_NAMED_BUILDER(singleControlledSdg)}, + QCOTestCase{"MultipleControlledSdg", + MQT_NAMED_BUILDER(multipleControlledSdg), + MQT_NAMED_BUILDER(multipleControlledSdg)}, + QCOTestCase{"NestedControlledSdg", + MQT_NAMED_BUILDER(nestedControlledSdg), + MQT_NAMED_BUILDER(multipleControlledSdg)}, + QCOTestCase{"TrivialControlledSdg", + MQT_NAMED_BUILDER(trivialControlledSdg), + MQT_NAMED_BUILDER(sdg)}, + QCOTestCase{"InverseSdg", MQT_NAMED_BUILDER(inverseSdg), + MQT_NAMED_BUILDER(s)}, + QCOTestCase{"InverseMultipleControlledSdg", + MQT_NAMED_BUILDER(inverseMultipleControlledSdg), + MQT_NAMED_BUILDER(multipleControlledS)}, + QCOTestCase{"SdgThenS", MQT_NAMED_BUILDER(sdgThenS), + MQT_NAMED_BUILDER(emptyQCO)}, + QCOTestCase{"TwoSdg", MQT_NAMED_BUILDER(twoSdg), + MQT_NAMED_BUILDER(z)})); /// @} /// \name QCO/Operations/StandardGates/SwapOp.cpp @@ -866,9 +855,7 @@ INSTANTIATE_TEST_SUITE_P( MQT_NAMED_BUILDER(multipleControlledTdg)}, QCOTestCase{"TThenTdg", MQT_NAMED_BUILDER(tThenTdg), MQT_NAMED_BUILDER(emptyQCO)}, - QCOTestCase{"TwoT", MQT_NAMED_BUILDER(twoT), MQT_NAMED_BUILDER(s)}, - QCOTestCase{"TwoTOnControlWire", MQT_NAMED_BUILDER(twoTOnControlWire), - MQT_NAMED_BUILDER(twoTOnControlWireMerged)})); + QCOTestCase{"TwoT", MQT_NAMED_BUILDER(twoT), MQT_NAMED_BUILDER(s)})); /// @} /// \name QCO/Operations/StandardGates/TdgOp.cpp @@ -897,10 +884,7 @@ INSTANTIATE_TEST_SUITE_P( QCOTestCase{"TdgThenS", MQT_NAMED_BUILDER(tdgThenT), MQT_NAMED_BUILDER(emptyQCO)}, QCOTestCase{"TwoTdg", MQT_NAMED_BUILDER(twoTdg), - MQT_NAMED_BUILDER(sdg)}, - QCOTestCase{"TwoTdgOnControlWire", - MQT_NAMED_BUILDER(twoTdgOnControlWire), - MQT_NAMED_BUILDER(twoTdgOnControlWireMerged)})); + MQT_NAMED_BUILDER(sdg)})); /// @} /// \name QCO/Operations/StandardGates/U2Op.cpp diff --git a/mlir/unittests/programs/qco_programs.cpp b/mlir/unittests/programs/qco_programs.cpp index feac3ff1bf..b384aa20a1 100644 --- a/mlir/unittests/programs/qco_programs.cpp +++ b/mlir/unittests/programs/qco_programs.cpp @@ -576,19 +576,6 @@ void twoS(QCOProgramBuilder& b) { q[0] = b.s(q[0]); } -void twoSOnControlWire(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.s(q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); - q[0] = b.s(q[0]); -} - -void twoSOnControlWireMerged(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.z(q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); -} - void sdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.sdg(q[0]); @@ -649,19 +636,6 @@ void twoSdg(QCOProgramBuilder& b) { q[0] = b.sdg(q[0]); } -void twoSdgOnControlWire(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.sdg(q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); - q[0] = b.sdg(q[0]); -} - -void twoSdgOnControlWireMerged(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.z(q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); -} - void t_(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.t(q[0]); @@ -721,19 +695,6 @@ void twoT(QCOProgramBuilder& b) { q[0] = b.t(q[0]); } -void twoTOnControlWire(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.t(q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); - q[0] = b.t(q[0]); -} - -void twoTOnControlWireMerged(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.s(q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); -} - void tdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.tdg(q[0]); @@ -794,19 +755,6 @@ void twoTdg(QCOProgramBuilder& b) { q[0] = b.tdg(q[0]); } -void twoTdgOnControlWire(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.tdg(q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); - q[0] = b.tdg(q[0]); -} - -void twoTdgOnControlWireMerged(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.sdg(q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); -} - void sx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.sx(q[0]); @@ -1101,34 +1049,6 @@ void twoRzOppositePhase(QCOProgramBuilder& b) { q[0] = b.rz(-0.789, q[0]); } -void twoRzOnControlWire(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.rz(0.1, q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); - q[0] = b.rz(0.2, q[0]); -} - -void twoRzOnControlWireMerged(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.rz(0.3, q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); -} - -void twoRzOnNestedControlWire(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(3); - q[0] = b.rz(0.1, q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); - std::tie(q[0], q[2]) = b.cx(q[0], q[2]); - q[0] = b.rz(0.2, q[0]); -} - -void twoRzOnNestedControlWireMerged(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(3); - q[0] = b.rz(0.3, q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); - std::tie(q[0], q[2]) = b.cx(q[0], q[2]); -} - void p(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.p(0.123, q[0]); @@ -1183,19 +1103,6 @@ void twoPOppositePhase(QCOProgramBuilder& b) { q[0] = b.p(-0.123, q[0]); } -void twoPOnControlWire(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.p(0.1, q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); - q[0] = b.p(0.2, q[0]); -} - -void twoPOnControlWireMerged(QCOProgramBuilder& b) { - auto q = b.allocQubitRegister(2); - q[0] = b.p(0.3, q[0]); - std::tie(q[0], q[1]) = b.cx(q[0], q[1]); -} - void r(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.r(0.123, 0.456, q[0]); diff --git a/mlir/unittests/programs/qco_programs.h b/mlir/unittests/programs/qco_programs.h index 12404c8898..b568b4b02f 100644 --- a/mlir/unittests/programs/qco_programs.h +++ b/mlir/unittests/programs/qco_programs.h @@ -299,13 +299,6 @@ void sThenSdg(QCOProgramBuilder& b); /// Creates a circuit with two S gates in a row. void twoS(QCOProgramBuilder& b); -/// Creates a circuit with two S gates on a control wire separated by a `ctrl` -/// hop. -void twoSOnControlWire(QCOProgramBuilder& b); - -/// Creates the merged Z form of @ref twoSOnControlWire. -void twoSOnControlWireMerged(QCOProgramBuilder& b); - // --- SdgOp ---------------------------------------------------------------- // /// Creates a circuit with just an Sdg gate. @@ -335,13 +328,6 @@ void sdgThenS(QCOProgramBuilder& b); /// Creates a circuit with two Sdg gates in a row. void twoSdg(QCOProgramBuilder& b); -/// Creates a circuit with two Sdg gates on a control wire separated by a `ctrl` -/// hop. -void twoSdgOnControlWire(QCOProgramBuilder& b); - -/// Creates the merged Z form of @ref twoSdgOnControlWire. -void twoSdgOnControlWireMerged(QCOProgramBuilder& b); - // --- TOp ------------------------------------------------------------------ // /// Creates a circuit with just a T gate. @@ -371,13 +357,6 @@ void tThenTdg(QCOProgramBuilder& b); /// Creates a circuit with two T gates in a row. void twoT(QCOProgramBuilder& b); -/// Creates a circuit with two T gates on a control wire separated by a `ctrl` -/// hop. -void twoTOnControlWire(QCOProgramBuilder& b); - -/// Creates the merged S form of @ref twoTOnControlWire. -void twoTOnControlWireMerged(QCOProgramBuilder& b); - // --- TdgOp ---------------------------------------------------------------- // /// Creates a circuit with just a Tdg gate. @@ -407,13 +386,6 @@ void tdgThenT(QCOProgramBuilder& b); /// Creates a circuit with two Tdg gates in a row. void twoTdg(QCOProgramBuilder& b); -/// Creates a circuit with two Tdg gates on a control wire separated by a `ctrl` -/// hop. -void twoTdgOnControlWire(QCOProgramBuilder& b); - -/// Creates the merged Sdg form of @ref twoTdgOnControlWire. -void twoTdgOnControlWireMerged(QCOProgramBuilder& b); - // --- SXOp ----------------------------------------------------------------- // /// Creates a circuit with just an SX gate. @@ -557,21 +529,6 @@ void inverseMultipleControlledRz(QCOProgramBuilder& b); /// Creates a circuit with two RZ gates in a row with opposite phases. void twoRzOppositePhase(QCOProgramBuilder& b); -/// Creates a circuit with two RZ gates on a control wire separated by a `ctrl` -/// hop. -void twoRzOnControlWire(QCOProgramBuilder& b); - -/// Creates the merged single-RZ form of @ref twoRzOnControlWire. -void twoRzOnControlWireMerged(QCOProgramBuilder& b); - -/// Creates a circuit with two RZ gates on a control wire separated by nested -/// `ctrl` hops. -void twoRzOnNestedControlWire(QCOProgramBuilder& b); - -/// Creates the merged single-RZ form of @ref -/// twoRzOnNestedControlWire. -void twoRzOnNestedControlWireMerged(QCOProgramBuilder& b); - // --- POp ------------------------------------------------------------------ // /// Creates a circuit with just a P gate. @@ -598,13 +555,6 @@ void inverseMultipleControlledP(QCOProgramBuilder& b); /// Creates a circuit with two P gates in a row with opposite phases. void twoPOppositePhase(QCOProgramBuilder& b); -/// Creates a circuit with two P gates on a control wire separated by a `ctrl` -/// hop. -void twoPOnControlWire(QCOProgramBuilder& b); - -/// Creates the merged single-P form of @ref twoPOnControlWire. -void twoPOnControlWireMerged(QCOProgramBuilder& b); - // --- ROp ------------------------------------------------------------------ // /// Creates a circuit with just an R gate.