diff --git a/CHANGELOG.md b/CHANGELOG.md index bebe7635f1..3df6a465d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,8 +42,9 @@ with the exception that minor releases may include breaking changes. [#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]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], - [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], + [#1774], [#1781], [#1782]) + ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], + [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**]) ### Changed @@ -597,6 +598,7 @@ changelogs._ +[#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 diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index e2ec9740ba..4b944d81d6 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -10,12 +10,36 @@ #pragma once +#include "mlir/Dialect/QCO/IR/QCOOps.h" + #include +#include #include #include +#include namespace mlir::qco { +/** + * @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 bool valuesMatchWithinTolerance(Value lhs, Value rhs) { + if (lhs == rhs) { + return true; + } + const auto lhsVal = utils::valueToDouble(lhs); + const auto rhsVal = utils::valueToDouble(rhs); + return lhsVal && rhsVal && std::abs(*lhsVal - *rhsVal) <= utils::TOLERANCE; +} + /** * @brief Remove a pair of inverse one-target, zero-parameter operations * @@ -35,77 +59,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 + * @brief Remove a pair of inverse two-target, zero-parameter operations. * * @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) { +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(); } - // Confirm operations act on the same qubits - if (op.getOutputQubit(1) != nextOp.getInputQubit(1)) { - 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) { + // Both qubits have to point to the same successor + auto nextOp2 = *op.getOutputQubit(1).user_begin(); + if (nextOp2 != 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 (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.replaceAllUsesWith(nextOp->getResults(), - {op.getInputQubit(1), op.getInputQubit(0)}); - rewriter.eraseOp(nextOp); - rewriter.eraseOp(op); - - return success(); + return failure(); } /** @@ -166,53 +162,70 @@ LogicalResult mergeOneTargetOneParameter(OpType op, PatternRewriter& rewriter) { // 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 Shared implementation for merging 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 first operation instance. + * @param nextOp The successor operation instance. + * @param rewriter The pattern rewriter. + * @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 symmetric = false) { + + // Both qubits have to point to the same successor + auto nextOp2 = *op.getOutputQubit(1).user_begin(); + if (nextOp2 != nextOp) { + return failure(); + } + + 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 failure(); +} + +/** + * @brief Merge two compatible two-target, one-parameter operations. * * @tparam OpType The type of the operation to be merged. * @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) * @return LogicalResult Success or failure of the merge. */ template -LogicalResult mergeTwoTargetOneParameter(OpType op, PatternRewriter& rewriter) { +LogicalResult mergeTwoTargetOneParameter(OpType op, PatternRewriter& rewriter, + bool symmetric = false) { // 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(); - } - - // 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()); - - // Replace the second operation with the result of the first operation - rewriter.replaceOp(nextOp, op.getResults()); - - return success(); + return mergeTwoTargetOneParameterImpl(op, nextOp, rewriter, symmetric); } /** - * @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. * * @tparam OpType The type of the operation to be merged. * @param op The operation instance. @@ -220,30 +233,18 @@ LogicalResult mergeTwoTargetOneParameter(OpType op, PatternRewriter& rewriter) { * @return LogicalResult Success or failure of the merge. */ template -LogicalResult -mergeTwoTargetOneParameterWithSwappedTargets(OpType op, - PatternRewriter& rewriter) { +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) { 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)) { + // Confirm matching beta before summing theta + if (!valuesMatchWithinTolerance(op.getBeta(), nextOp.getBeta())) { 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()); - - // nextOp results correspond to swapped operands, so swap replacements too - rewriter.replaceOp(nextOp, {op.getOutputQubit(1), op.getOutputQubit(0)}); - - return success(); + 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 60cb98c196..eaf442a5ad 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/DCXOp.cpp @@ -31,8 +31,8 @@ struct RemoveInversePairDCX final : OpRewritePattern { LogicalResult matchAndRewrite(DCXOp op, PatternRewriter& rewriter) const override { - return removeTwoTargetZeroParameterPairWithSwappedTargets(op, - rewriter); + return removeInversePairTwoTargetZeroParameter(op, rewriter, false, + true); } }; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp index 1eca6542b0..1b742170fa 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp @@ -9,13 +9,16 @@ */ #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" +#include #include #include #include #include +#include #include #include @@ -64,6 +67,31 @@ 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 { + 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(); + } +}; + } // namespace void ROp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, @@ -77,7 +105,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/RXXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp index c55edc7f8a..3bd350bf27 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp @@ -39,20 +39,7 @@ struct MergeSubsequentRXX final : 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 mergeTwoTargetOneParameterWithSwappedTargets(op, rewriter); + 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 9f59ca471e..fb99642f64 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp @@ -39,20 +39,7 @@ struct MergeSubsequentRYY final : 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 mergeTwoTargetOneParameterWithSwappedTargets(op, rewriter); + 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 c290dc69b0..a10bd15c0c 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp @@ -39,20 +39,7 @@ struct MergeSubsequentRZZ final : 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 mergeTwoTargetOneParameterWithSwappedTargets(op, rewriter); + 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 673ffffdee..676be346de 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/SWAPOp.cpp @@ -30,21 +30,7 @@ struct RemoveSubsequentSWAP final : 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 removeTwoTargetZeroParameterPairWithSwappedTargets(op, - rewriter); + return removeInversePairTwoTargetZeroParameter(op, rewriter, true); } }; @@ -52,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 26d5966d11..c75c45f26e 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp @@ -9,10 +9,10 @@ */ #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" -#include #include #include #include @@ -40,36 +40,7 @@ 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(); - } - - // 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()); - - // Replace the second operation with the result of the first operation - rewriter.replaceOp(nextOp, op.getResults()); - return success(); + return mergeXXPlusMinusYY(op, rewriter); } }; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp index 6b4c5f0b9e..6511b2344b 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp @@ -9,10 +9,10 @@ */ #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" -#include #include #include #include @@ -40,36 +40,7 @@ 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(); - } - - // 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()); - - // Replace the second operation with the result of the first operation - rewriter.replaceOp(nextOp, op.getResults()); - return success(); + return mergeXXPlusMinusYY(op, rewriter); } }; diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp index 883c7d32d2..26fca1733f 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp @@ -479,7 +479,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 @@ -999,7 +1000,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 +1032,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 d27b4f5bc0..b384aa20a1 100644 --- a/mlir/unittests/programs/qco_programs.cpp +++ b/mlir/unittests/programs/qco_programs.cpp @@ -1162,6 +1162,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]); @@ -1885,6 +1891,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]); @@ -1943,6 +1955,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 149753795a..b568b4b02f 100644 --- a/mlir/unittests/programs/qco_programs.h +++ b/mlir/unittests/programs/qco_programs.h @@ -584,6 +584,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. @@ -927,6 +930,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. @@ -954,6 +960,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.