From 89f8ee3652da39a2d3668c28ae75ad2d57461b4d Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Wed, 13 May 2026 16:25:30 +0200 Subject: [PATCH 01/25] feat(mlir): :sparkles: add pass and patterns for measurement lifting --- .../mlir/Dialect/QCO/Transforms/Passes.td | 34 +++ .../Optimizations/MeasurementLifting.cpp | 236 ++++++++++++++++++ 2 files changed, 270 insertions(+) create mode 100644 mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index 971dc18f71..05a370c344 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -201,4 +201,38 @@ def HadamardLifting : Pass<"hadamard-lifting", "mlir::ModuleOp"> { }]; } +def MeasurementLifting : Pass<"measurement-lifting", "mlir::ModuleOp"> { + let dependentDialects = ["mlir::qco::QCODialect", + "::mlir::arith::ArithDialect", + ]; + let summary = "This pass attempts to move measurements as far up as" + "possible, shiftling them above gates that commute with them." + "This is done to enable qubit reuse and other optimizations."; + let description = [{ + This pass lifts measurements gates away from the measurements in order to apply measurement lifting more effectively. + Measurement lifting is a subroutine of the qubit reuse routine. The goal is to measure qubits earlier in the + circuit to reuse them and to potentially remove some quantum gates. + + Measurement lifting uses the following commutation rules: + ┌──────┐ ┌──────┐ + ──■──┤ Meas │────── ─┤ Meas ├──■─── + │ └──────┘ └──────┘ │ + ┌─┴─┐ => ┌─┴─┐ + ┤ U ├──────────────── ─────────┤ U ├─ + └───┘ └───┘ + (Where U is any (controlled) unitary gate) + + ┌───┐┌──────┐ ┌──────┐┌───┐ + ┤ P ├┤ Meas ├ => ┤ Meas ├┤ P ├ + └───┘└──────┘ └──────┘└───┘ + (Where P is any diagonal gate, e.g., `z`, `s`, ...) + + ┌───┐┌──────┐ ┌───────┐┌───┐ + ┤ X ├┤ Meas ├ => ┤ Meas* ├┤ X ├ + └───┘└──────┘ └───────┘└───┘ + (Where Meas* is a measurement after which the outcome is classically negated) + + }]; +} + #endif // MLIR_DIALECT_QCO_TRANSFORMS_PASSES_TD diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp new file mode 100644 index 0000000000..02589d814d --- /dev/null +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +// +// Created by damian on 5/13/26. +// + +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace mlir::qco { + +#define GEN_PASS_DEF_MEASUREMENTLIFTING +#include "mlir/Dialect/QCO/Transforms/Passes.h.inc" + +namespace { + +/** + * @brief Checks if the given operation is an inverting gate. + * @param op The operation to check. + * @return True if the operation is an inverting gate, false otherwise. + */ +bool isInverting(Operation* op) { return isa(op); } + +/** + * @brief Checks if the given operation is a diagonal gate. + * @param op The operation to check. + * @return True if the operation is a diagonal gate, false otherwise. + */ +bool isDiagonal(Operation* op) { return isa(op); } + +/** + * @brief This method swaps a gate with a measurement. + * @param gate The gate to swap. + * @param measurement The measurement to swap. + * @param rewriter The used rewriter. + */ +void swapGateWithMeasurement(UnitaryOpInterface gate, MeasureOp measurement, + mlir::PatternRewriter& rewriter) { + auto measurementInput = measurement.getQubitIn(); + auto gateInput = gate.getInputForOutput(measurementInput); + rewriter.replaceUsesWithIf(measurementInput, gateInput, + [&](mlir::OpOperand& operand) { + // We only replace the single use by the + // measure op + return operand.getOwner() == measurement; + }); + rewriter.replaceUsesWithIf(gateInput, measurement.getQubitOut(), + [&](mlir::OpOperand& operand) { + // We only replace the single use by the + // predecessor + return operand.getOwner() == gate; + }); + rewriter.replaceUsesWithIf(measurement.getQubitOut(), measurementInput, + [&](mlir::OpOperand& operand) { + // All further uses of the measurement output now + // use the gate output + return operand.getOwner() != gate; + }); + rewriter.moveOpBefore(measurement, gate); +} + +/** + * @brief This pattern is responsible for lifting measurements above any phase + * gates. + */ +struct LiftMeasurementsAbovePhaseGatesPattern final + : mlir::OpRewritePattern { + + explicit LiftMeasurementsAbovePhaseGatesPattern(mlir::MLIRContext* context) + : OpRewritePattern(context) {} + + mlir::LogicalResult + matchAndRewrite(MeasureOp op, + mlir::PatternRewriter& rewriter) const override { + const auto qubitVariable = op.getQubitIn(); + auto* predecessor = qubitVariable.getDefiningOp(); + + auto predecessorUnitary = mlir::dyn_cast(predecessor); + + if (!predecessorUnitary) { + return mlir::failure(); + } + + if (isDiagonal(predecessor)) { + swapGateWithMeasurement(predecessorUnitary, op, rewriter); + return mlir::success(); + } + + return mlir::failure(); + } +}; + +/** + * @brief This pattern is responsible for lifting measurements above any + * non-phase gates. + */ +struct LiftMeasurementsAboveInvertingGatesPattern final + : mlir::OpRewritePattern { + + explicit LiftMeasurementsAboveInvertingGatesPattern( + mlir::MLIRContext* context) + : OpRewritePattern(context) {} + + /** + * @brief Checks if the given qubit is not used anymore. + * @param outQubit The output qubit to check. + * @return True if all users are resets/deallocs, false otherwise. + */ + static bool outputQubitRemainsUnused(mlir::Value outQubit) { + return llvm::all_of(outQubit.getUsers(), [](mlir::Operation* user) { + return mlir::isa(user) || mlir::isa(user); + }); + } + + mlir::LogicalResult + matchAndRewrite(MeasureOp op, + mlir::PatternRewriter& rewriter) const override { + if (!outputQubitRemainsUnused(op.getQubitOut())) { + return mlir::failure(); // if the qubit is still used after the + // measurement, we cannot lift it above the gate. + } + const auto qubitVariable = op.getQubitIn(); + auto* predecessor = qubitVariable.getDefiningOp(); + + auto predecessorUnitary = mlir::dyn_cast(predecessor); + + if (!predecessorUnitary) { + return mlir::failure(); + } + + if (isInverting(predecessor) && + predecessorUnitary.getInputQubits().size() == 1) { + swapGateWithMeasurement(predecessorUnitary, op, rewriter); + rewriter.setInsertionPointAfter(op); + const mlir::Value trueConstant = rewriter.create( + op.getLoc(), rewriter.getBoolAttr(true)); + auto inversion = rewriter.create( + op.getLoc(), op.getResult(), trueConstant); + // We need `replaceUsesWithIf` so that we can replace all uses except for + // the one use that defines the inverted bit. + rewriter.replaceUsesWithIf(op.getResult(), inversion.getResult(), + [&](mlir::OpOperand& operand) { + return operand.getOwner() != inversion; + }); + return mlir::success(); + } + + return mlir::failure(); + } +}; + +/** + * @brief This pattern is responsible for applying the "deferred measurement + * principle", lifting measurements above controls. + */ +struct LiftMeasurementsAboveControlsPattern final + : mlir::OpRewritePattern { + + explicit LiftMeasurementsAboveControlsPattern(mlir::MLIRContext* context) + : OpRewritePattern(context) {} + + mlir::LogicalResult + matchAndRewrite(MeasureOp op, + mlir::PatternRewriter& rewriter) const override { + const auto qubitVariable = op.getQubitIn(); + auto* predecessor = qubitVariable.getDefiningOp(); + auto predecessorUnitary = mlir::dyn_cast(predecessor); + + if (!predecessorUnitary) { + return mlir::failure(); + } + + if (llvm::find(predecessorUnitary.getOutputQubits(), qubitVariable) != + predecessorUnitary.getOutputQubits().end()) { + // The measured qubit is a target, not a control of the gate. + return mlir::failure(); + } + + swapGateWithMeasurement(predecessorUnitary, op, rewriter); + + return mlir::success(); + } +}; + +/** + * @brief Pass raises Measurements above controlled and uncontrolled gates + * gates. + */ +struct MeasurementLifting final + : impl::MeasurementLiftingBase { + using MeasurementLiftingBase::MeasurementLiftingBase; + +protected: + void runOnOperation() override { + const auto op = getOperation(); + auto* ctx = &getContext(); + + // Define the set of patterns to use. + RewritePatternSet patterns(ctx); + patterns.add(patterns.getContext()); + patterns.add( + patterns.getContext()); + patterns.add(patterns.getContext()); + + // Apply patterns in an iterative and greedy manner. + if (failed(applyPatternsGreedily(op, std::move(patterns)))) { + signalPassFailure(); + } + } +}; + +} // namespace + +} // namespace mlir::qco From 6d4332da72da7545cc5a6f82a0549f2aa03e92ee Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Mon, 1 Jun 2026 13:51:43 +0200 Subject: [PATCH 02/25] test(mlir/hybrid-opt): :construction: set up tests for measurement lifting --- .../Dialect/QCO/Builder/QCOProgramBuilder.h | 15 + .../Dialect/QCO/Builder/QCOProgramBuilder.cpp | 6 + .../Transforms/Optimizations/CMakeLists.txt | 4 +- .../test_qco_measurement_lifting.cpp | 428 ++++++++++++++++++ 4 files changed, 452 insertions(+), 1 deletion(-) create mode 100644 mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp diff --git a/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h b/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h index d88e12dfa7..14ea2e85f9 100644 --- a/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h +++ b/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h @@ -138,6 +138,21 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { */ Value floatConstant(double value); + /** + * @brief Create a constant boolean value + * @param value The value to store in the constant + * @return The value produced by the constant operation + * + * @par Example: + * ```c++ + * auto c = builder.boolConstant(true); + * ``` + * ```mlir + * %c = arith.constant 1 : i1 + * ``` + */ + Value boolConstant(bool value); + //===--------------------------------------------------------------------===// // Memory Management //===--------------------------------------------------------------------===// diff --git a/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp b/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp index f766602a24..1a5e2c362a 100644 --- a/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp +++ b/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp @@ -92,6 +92,12 @@ Value QCOProgramBuilder::floatConstant(const double value) { return arith::ConstantOp::create(*this, getF64FloatAttr(value)).getResult(); } +Value QCOProgramBuilder::boolConstant(const bool value) { + checkFinalized(); + return arith::ConstantOp::create(*this, getBoolAttr(value)).getResult(); +} +} + Value& QCOProgramBuilder::QubitRegister::operator[](const size_t index) { if (index >= qubits.size()) { llvm::reportFatalUsageError("Qubit index out of bounds"); diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt index e80e957680..cdd34bdf46 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt @@ -8,7 +8,9 @@ set(target_name mqt-core-mlir-unittest-optimizations) add_executable(${target_name} test_qco_hadamard_lifting.cpp - test_qco_merge_single_qubit_rotation.cpp test_quantum_loop_unroll.cpp) + test_qco_measurement_lifting.cpp + test_qco_merge_single_qubit_rotation.cpp + test_quantum_loop_unroll.cpp) target_link_libraries( ${target_name} diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp new file mode 100644 index 0000000000..f0373ebfd5 --- /dev/null +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +// +// Created by damian on 5/21/26. +// + +#include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" +#include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" +#include "mlir/Support/IRVerification.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { + +using namespace mlir; +using namespace mlir::qco; + +class QCOMeasurementLiftingTest : public testing::Test { + +protected: + MLIRContext context; + QCOProgramBuilder programBuilder; + QCOProgramBuilder referenceBuilder; + OwningOpRef module; + OwningOpRef reference; + + QCOMeasurementLiftingTest() + : programBuilder(&context), referenceBuilder(&context) {} + + void SetUp() override { + // Register all necessary dialects + DialectRegistry registry; + registry.insert(); + context.appendDialectRegistry(registry); + context.loadAllAvailableDialects(); + + programBuilder.initialize(); + referenceBuilder.initialize(); + } + + /** + * @brief Adds the measurementLiftingPass to the current context and runs it. + */ + static LogicalResult runMeasurementLiftingPass(ModuleOp module) { + PassManager pm(module.getContext()); + pm.addPass(createMeasurementLifting()); + pm.addPass(createCanonicalizerPass()); + return pm.run(module); + } + + /** + * @brief Adds the canonicalizerPass to the current context and runs it. + */ + static LogicalResult runCanonicalizerPass(ModuleOp module) { + PassManager pm(module.getContext()); + pm.addPass(createCanonicalizerPass()); + return pm.run(module); + } + + static Value i1ToI64(Value i1Value, ImplicitLocOpBuilder& builder) { + return arith::ExtUIOp::create(builder, builder.getI64Type(), i1Value) + .getResult(); + } +}; + +} // namespace + +TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPositiveControl) { + auto q0_0 = programBuilder.allocQubit(); + auto q1_0 = programBuilder.allocQubit(); + + auto [q1_1, q0_1] = programBuilder.cx(q1_0, q0_0); + auto [q0_2, q1_2] = programBuilder.ch(q0_1, q1_1); + auto [q0_3, q1_3] = programBuilder.cx(q0_2, q1_2); + + auto [q0_4, c0] = programBuilder.measure(q0_3); + auto [q1_4, c1] = programBuilder.measure(q1_3); + + programBuilder.sink(q0_4); + programBuilder.sink(q1_4); + module = programBuilder.finalize(); + + auto r0_0 = referenceBuilder.allocQubit(); + auto r1_0 = referenceBuilder.allocQubit(); + + auto [r1_1, r0_1] = referenceBuilder.cx(r1_0, r0_0); + auto [r0_2, cr0] = referenceBuilder.measure(r0_1); + auto [r0_3, r1_2] = referenceBuilder.ch(r0_2, r1_1); + auto [r0_4, r1_3] = referenceBuilder.cx(r0_3, r1_2); + + auto [r1_4, cr1] = referenceBuilder.measure(r1_3); + + referenceBuilder.sink(r0_4); + referenceBuilder.sink(r1_4); + reference = referenceBuilder.finalize(); + + ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); + ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); + + EXPECT_TRUE( + areModulesEquivalentWithPermutations(module.get(), reference.get())); +} + +TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { + auto q0_0 = programBuilder.allocQubit(); + auto q1_0 = programBuilder.allocQubit(); + auto q2_0 = programBuilder.allocQubit(); + + auto [q12_0, q0_1] = + programBuilder.ctrl({q1_0, q2_0}, {q0_0}, [&](const ValueRange target) { + return SmallVector{programBuilder.x(target[0])}; + }); + auto [q12_1, q0_2] = programBuilder.ctrl( + {q12_0[1], q12_0[0]}, q0_1, [&](const ValueRange target) { + return SmallVector{programBuilder.h(target[0])}; + }); + auto [q12_2, q0_3] = programBuilder.ctrl( + {q12_1[1], q12_1[0]}, q0_2, [&](const ValueRange target) { + return SmallVector{programBuilder.x(target[0])}; + }); + + auto [q1_4, c1] = programBuilder.measure(q12_2[0]); + + auto q0_4 = programBuilder.h(q0_3[0]); + auto q2_4 = programBuilder.h(q12_2[1]); + + auto [q0_5, c0] = programBuilder.measure(q0_4); + auto [q2_5, c2] = programBuilder.measure(q2_4); + + programBuilder.sink(q0_5); + programBuilder.sink(q1_4); + programBuilder.sink(q2_5); + + module = programBuilder.finalize(); + + auto r0_0 = referenceBuilder.allocQubit(); + auto r1_0 = referenceBuilder.allocQubit(); + auto r2_0 = referenceBuilder.allocQubit(); + + auto [r1_1, cr1] = referenceBuilder.measure(r1_0); + + auto [r12_0, r0_1] = + referenceBuilder.ctrl({r1_1, r2_0}, {r0_0}, [&](const ValueRange target) { + return SmallVector{referenceBuilder.x(target[0])}; + }); + auto [r12_1, r0_2] = referenceBuilder.ctrl( + {r12_0[1], r12_0[0]}, r0_1, [&](const ValueRange target) { + return SmallVector{referenceBuilder.h(target[0])}; + }); + auto [r12_2, r0_3] = referenceBuilder.ctrl( + {r12_1[1], r12_1[0]}, r0_2, [&](const ValueRange target) { + return SmallVector{referenceBuilder.x(target[0])}; + }); + + auto r0_4 = referenceBuilder.h(r0_3[0]); + auto r2_4 = referenceBuilder.h(r12_2[1]); + + auto [r0_5, cr0] = referenceBuilder.measure(r0_4); + auto [r2_5, cr2] = referenceBuilder.measure(r2_4); + + referenceBuilder.sink(r0_5); + referenceBuilder.sink(r12_2[0]); + referenceBuilder.sink(r2_5); + + reference = referenceBuilder.finalize(); + + ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); + ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); + + EXPECT_TRUE( + areModulesEquivalentWithPermutations(module.get(), reference.get())); +} + +TEST_F(QCOMeasurementLiftingTest, + liftMeasurementMultipleOverOneControlledGate) { + auto q0_0 = programBuilder.allocQubit(); + auto q1_0 = programBuilder.allocQubit(); + auto q2_0 = programBuilder.allocQubit(); + + auto [q12_0, q0_1] = + programBuilder.ctrl({q1_0, q2_0}, {q0_0}, [&](const ValueRange target) { + return SmallVector{programBuilder.x(target[0])}; + }); + + auto [q1_1, c1] = programBuilder.measure(q12_0[0]); + auto [q2_1, c2] = programBuilder.measure(q12_0[1]); + + programBuilder.sink(q0_1[0]); + programBuilder.sink(q1_1); + programBuilder.sink(q2_1); + module = programBuilder.finalize(); + + auto r0_0 = referenceBuilder.allocQubit(); + auto r1_0 = referenceBuilder.allocQubit(); + auto r2_0 = referenceBuilder.allocQubit(); + + auto [r1_1, cr1] = programBuilder.measure(r1_0); + auto [r2_1, cr2] = programBuilder.measure(r2_0); + + auto [r12_0, r0_1] = + programBuilder.ctrl({r1_1, r2_1}, {r0_0}, [&](const ValueRange target) { + return SmallVector{programBuilder.x(target[0])}; + }); + + referenceBuilder.sink(r0_1[0]); + referenceBuilder.sink(r12_0[0]); + referenceBuilder.sink(r12_0[1]); + module = referenceBuilder.finalize(); + + ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); + ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); + + EXPECT_TRUE( + areModulesEquivalentWithPermutations(module.get(), reference.get())); +} + +TEST_F(QCOMeasurementLiftingTest, + liftMeasurementOverControlledParametrizedGate) { + auto q0_0 = programBuilder.allocQubit(); + auto q1_0 = programBuilder.allocQubit(); + + auto [q0_1, q1_1] = programBuilder.crx(std::numbers::pi / 2, q0_0, q1_0); + + auto [q0_2, c0] = programBuilder.measure(q0_1); + auto [q1_2, c1] = programBuilder.measure(q1_1); + + programBuilder.sink(q0_2); + programBuilder.sink(q1_2); + module = programBuilder.finalize(); + + auto r0_0 = referenceBuilder.allocQubit(); + auto r1_0 = referenceBuilder.allocQubit(); + + auto [r0_1, cr0] = referenceBuilder.measure(r0_0); + + auto [r0_2, r1_1] = referenceBuilder.crx(std::numbers::pi / 2, r0_1, r1_0); + + auto [r1_2, cr1] = referenceBuilder.measure(r1_1); + + referenceBuilder.sink(r0_2); + referenceBuilder.sink(r1_2); + reference = referenceBuilder.finalize(); + + ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); + ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); + + EXPECT_TRUE( + areModulesEquivalentWithPermutations(module.get(), reference.get())); +} + +TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleX) { + auto q_0 = programBuilder.allocQubit(); + auto q_1 = programBuilder.x(q_0); + auto [q_2, c] = programBuilder.measure(q_1); + programBuilder.sink(q_2); + module = programBuilder.finalize(i1ToI64(c, programBuilder)); + + auto r_0 = referenceBuilder.allocQubit(); + auto true_constant = referenceBuilder.boolConstant(true); + auto [r_1, cr] = referenceBuilder.measure(r_0); + + auto xorOp = arith::XOrIOp::create( + referenceBuilder, referenceBuilder.getLoc(), cr, true_constant); + referenceBuilder.sink(r_1); + reference = + referenceBuilder.finalize(i1ToI64(xorOp.getResult(), referenceBuilder)); + + ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); + ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); + + reference.get().dump(); + module.get().dump(); + + EXPECT_TRUE( + areModulesEquivalentWithPermutations(module.get(), reference.get())); +} + +TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleY) { + auto q_0 = programBuilder.allocQubit(); + auto q_1 = programBuilder.y(q_0); + auto [q_2, c] = programBuilder.measure(q_1); + programBuilder.sink(q_2); + module = programBuilder.finalize(); + + auto r_0 = referenceBuilder.allocQubit(); + auto true_constant = referenceBuilder.boolConstant(true); + auto [r_1, cr] = referenceBuilder.measure(r_0); + referenceBuilder.insert(arith::XOrIOp::create( + referenceBuilder, referenceBuilder.getLoc(), cr, true_constant)); + referenceBuilder.sink(r_1); + reference = referenceBuilder.finalize(); + + ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); + ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); + + EXPECT_TRUE( + areModulesEquivalentWithPermutations(module.get(), reference.get())); +} + +TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPhaseGates) { + auto q_0 = programBuilder.allocQubit(); + auto q_1 = programBuilder.id(q_0); + auto q_2 = programBuilder.z(q_1); + auto q_3 = programBuilder.s(q_2); + auto q_4 = programBuilder.sdg(q_3); + auto q_5 = programBuilder.t(q_4); + auto q_6 = programBuilder.tdg(q_5); + auto q_7 = programBuilder.p(std::numbers::pi / 2, q_6); + auto q_8 = programBuilder.rz(std::numbers::pi / 2, q_7); + auto [q_9, c] = programBuilder.measure(q_8); + programBuilder.sink(q_9); + module = programBuilder.finalize(); + + auto r_0 = referenceBuilder.allocQubit(); + auto [r_1, cr] = referenceBuilder.measure(r_0); + referenceBuilder.sink(r_1); + reference = referenceBuilder.finalize(); + + ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); + ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); + + EXPECT_TRUE( + areModulesEquivalentWithPermutations(module.get(), reference.get())); +} + +TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverMultipleXY) { + auto q_0 = programBuilder.allocQubit(); + auto q_1 = programBuilder.x(q_0); + auto q_2 = programBuilder.y(q_1); + auto [q_3, c] = programBuilder.measure(q_2); + programBuilder.sink(q_3); + module = programBuilder.finalize(); + + auto r_0 = referenceBuilder.allocQubit(); + auto [r_1, cr] = referenceBuilder.measure(r_0); + referenceBuilder.sink(r_1); + reference = referenceBuilder.finalize(); + + ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); + ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); + + EXPECT_TRUE( + areModulesEquivalentWithPermutations(module.get(), reference.get())); +} + +TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { + auto q0_0 = programBuilder.allocQubit(); + auto q1_0 = programBuilder.allocQubit(); + + auto [q0_1, q1_1] = programBuilder.cy(q0_0, q1_0); + auto q0_2 = programBuilder.x(q0_1); + auto [q0_3, q1_2] = programBuilder.cy(q0_2, q1_1); + auto q0_4 = programBuilder.x(q0_3); + + auto [q0_5, c0] = programBuilder.measure(q0_4); + + programBuilder.sink(q0_5); + programBuilder.sink(q1_2); + module = programBuilder.finalize(); + + auto r0_0 = referenceBuilder.allocQubit(); + auto r1_0 = referenceBuilder.allocQubit(); + + auto [r0_1, cr0] = referenceBuilder.measure(r0_0); + + auto [r0_2, r1_1] = referenceBuilder.cx(r0_1, r1_0); + auto r0_3 = referenceBuilder.x(r0_2); + auto [r0_4, r1_2] = referenceBuilder.cx(r0_3, r1_1); + + referenceBuilder.sink(r0_4); + referenceBuilder.sink(r1_2); + reference = referenceBuilder.finalize(); + + ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); + ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); + + EXPECT_TRUE( + areModulesEquivalentWithPermutations(module.get(), reference.get())); +} + +TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverDiagonalGateInControl) { + auto q0_0 = programBuilder.allocQubit(); + auto q1_0 = programBuilder.allocQubit(); + + auto [q0_1, q1_1] = programBuilder.cz(q0_0, q1_0); + + auto [q0_2, c0] = programBuilder.measure(q0_1); + + programBuilder.sink(q0_2); + programBuilder.sink(q1_1); + module = programBuilder.finalize(); + + auto r0_0 = referenceBuilder.allocQubit(); + auto r1_0 = referenceBuilder.allocQubit(); + + auto [r0_1, cr0] = referenceBuilder.measure(r0_0); + + referenceBuilder.sink(r0_1); + referenceBuilder.sink(r1_0); + reference = referenceBuilder.finalize(); + + ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); + ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); + + EXPECT_TRUE( + areModulesEquivalentWithPermutations(module.get(), reference.get())); +} From 1a7664c301752761d64cba47cb5acc07d796cb70 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 2 Jun 2026 10:37:47 +0200 Subject: [PATCH 03/25] fix(mlir): :white_check_mark: fix tests --- .../test_qco_measurement_lifting.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp index f0373ebfd5..61b9458c13 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -215,18 +215,18 @@ TEST_F(QCOMeasurementLiftingTest, auto r1_0 = referenceBuilder.allocQubit(); auto r2_0 = referenceBuilder.allocQubit(); - auto [r1_1, cr1] = programBuilder.measure(r1_0); - auto [r2_1, cr2] = programBuilder.measure(r2_0); + auto [r1_1, cr1] = referenceBuilder.measure(r1_0); + auto [r2_1, cr2] = referenceBuilder.measure(r2_0); auto [r12_0, r0_1] = - programBuilder.ctrl({r1_1, r2_1}, {r0_0}, [&](const ValueRange target) { - return SmallVector{programBuilder.x(target[0])}; + referenceBuilder.ctrl({r1_1, r2_1}, {r0_0}, [&](const ValueRange target) { + return SmallVector{referenceBuilder.x(target[0])}; }); referenceBuilder.sink(r0_1[0]); referenceBuilder.sink(r12_0[0]); referenceBuilder.sink(r12_0[1]); - module = referenceBuilder.finalize(); + reference = referenceBuilder.finalize(); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); @@ -289,9 +289,6 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleX) { ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); - reference.get().dump(); - module.get().dump(); - EXPECT_TRUE( areModulesEquivalentWithPermutations(module.get(), reference.get())); } @@ -306,8 +303,8 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleY) { auto r_0 = referenceBuilder.allocQubit(); auto true_constant = referenceBuilder.boolConstant(true); auto [r_1, cr] = referenceBuilder.measure(r_0); - referenceBuilder.insert(arith::XOrIOp::create( - referenceBuilder, referenceBuilder.getLoc(), cr, true_constant)); + auto xorOp = arith::XOrIOp::create( + referenceBuilder, referenceBuilder.getLoc(), cr, true_constant); referenceBuilder.sink(r_1); reference = referenceBuilder.finalize(); From 770e55c3cdcd1c458a93197dff04789945e37cf0 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 2 Jun 2026 10:48:50 +0200 Subject: [PATCH 04/25] style(mlir): :rotating_light: fix linter issues --- .../Optimizations/MeasurementLifting.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp index 02589d814d..5e6baea41f 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp @@ -16,9 +16,7 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" -#include #include -#include #include #include #include @@ -26,7 +24,6 @@ #include #include -#include #include namespace mlir::qco { @@ -34,21 +31,21 @@ namespace mlir::qco { #define GEN_PASS_DEF_MEASUREMENTLIFTING #include "mlir/Dialect/QCO/Transforms/Passes.h.inc" -namespace { - /** * @brief Checks if the given operation is an inverting gate. * @param op The operation to check. * @return True if the operation is an inverting gate, false otherwise. */ -bool isInverting(Operation* op) { return isa(op); } +static bool isInverting(Operation* op) { return isa(op); } /** * @brief Checks if the given operation is a diagonal gate. * @param op The operation to check. * @return True if the operation is a diagonal gate, false otherwise. */ -bool isDiagonal(Operation* op) { return isa(op); } +static bool isDiagonal(Operation* op) { + return isa(op); +} /** * @brief This method swaps a gate with a measurement. @@ -56,8 +53,9 @@ bool isDiagonal(Operation* op) { return isa(op); } * @param measurement The measurement to swap. * @param rewriter The used rewriter. */ -void swapGateWithMeasurement(UnitaryOpInterface gate, MeasureOp measurement, - mlir::PatternRewriter& rewriter) { +static void swapGateWithMeasurement(UnitaryOpInterface gate, + MeasureOp measurement, + mlir::PatternRewriter& rewriter) { auto measurementInput = measurement.getQubitIn(); auto gateInput = gate.getInputForOutput(measurementInput); rewriter.replaceUsesWithIf(measurementInput, gateInput, @@ -81,6 +79,7 @@ void swapGateWithMeasurement(UnitaryOpInterface gate, MeasureOp measurement, rewriter.moveOpBefore(measurement, gate); } +namespace { /** * @brief This pattern is responsible for lifting measurements above any phase * gates. From 64b162cc9cf237d263bbb6d39cec14c08090cc4e Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 2 Jun 2026 13:24:45 +0200 Subject: [PATCH 05/25] style(mlir): :recycle: implement coderabbit suggestions --- mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h | 1 - mlir/include/mlir/Dialect/QCO/Transforms/Passes.td | 6 +++--- .../QCO/Transforms/Optimizations/MeasurementLifting.cpp | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h b/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h index 14ea2e85f9..a029bda7e4 100644 --- a/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h +++ b/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h @@ -103,7 +103,6 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { * @param returnTypes The new return types for the main function */ void retype(TypeRange returnTypes); - //===--------------------------------------------------------------------===// // Constants //===--------------------------------------------------------------------===// diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index 05a370c344..9a95a0642e 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -205,11 +205,11 @@ def MeasurementLifting : Pass<"measurement-lifting", "mlir::ModuleOp"> { let dependentDialects = ["mlir::qco::QCODialect", "::mlir::arith::ArithDialect", ]; - let summary = "This pass attempts to move measurements as far up as" - "possible, shiftling them above gates that commute with them." + let summary = "This pass attempts to move measurements as far up as " + "possible, shifting them above gates that commute with them. " "This is done to enable qubit reuse and other optimizations."; let description = [{ - This pass lifts measurements gates away from the measurements in order to apply measurement lifting more effectively. + This pass applies measurement lifting, moving measurements up the code as far as possible. Measurement lifting is a subroutine of the qubit reuse routine. The goal is to measure qubits earlier in the circuit to reuse them and to potentially remove some quantum gates. diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp index 5e6baea41f..db1b3c95cc 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp @@ -44,7 +44,7 @@ static bool isInverting(Operation* op) { return isa(op); } * @return True if the operation is a diagonal gate, false otherwise. */ static bool isDiagonal(Operation* op) { - return isa(op); + return isa(op); } /** From 137d93cfa2ee6a226676d95fe989ce97a5fd3da5 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 2 Jun 2026 14:15:47 +0200 Subject: [PATCH 06/25] fix(mlir): :bug: fix all measurement lifting tests --- .../Optimizations/MeasurementLifting.cpp | 20 ++--- .../test_qco_measurement_lifting.cpp | 76 +++++++++++++------ 2 files changed, 63 insertions(+), 33 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp index db1b3c95cc..1fb173a348 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp @@ -44,6 +44,12 @@ static bool isInverting(Operation* op) { return isa(op); } * @return True if the operation is a diagonal gate, false otherwise. */ static bool isDiagonal(Operation* op) { + if (auto c = dyn_cast(op)) { + return isDiagonal(c.getBodyUnitary()); + } + if (auto i = dyn_cast(op)) { + return isDiagonal(i.getBodyUnitary()); + } return isa(op); } @@ -136,10 +142,6 @@ struct LiftMeasurementsAboveInvertingGatesPattern final mlir::LogicalResult matchAndRewrite(MeasureOp op, mlir::PatternRewriter& rewriter) const override { - if (!outputQubitRemainsUnused(op.getQubitOut())) { - return mlir::failure(); // if the qubit is still used after the - // measurement, we cannot lift it above the gate. - } const auto qubitVariable = op.getQubitIn(); auto* predecessor = qubitVariable.getDefiningOp(); @@ -185,19 +187,19 @@ struct LiftMeasurementsAboveControlsPattern final mlir::PatternRewriter& rewriter) const override { const auto qubitVariable = op.getQubitIn(); auto* predecessor = qubitVariable.getDefiningOp(); - auto predecessorUnitary = mlir::dyn_cast(predecessor); + auto predecessorCtrl = mlir::dyn_cast(predecessor); - if (!predecessorUnitary) { + if (!predecessorCtrl) { return mlir::failure(); } - if (llvm::find(predecessorUnitary.getOutputQubits(), qubitVariable) != - predecessorUnitary.getOutputQubits().end()) { + if (llvm::find(predecessorCtrl.getControlsOut(), qubitVariable) == + predecessorCtrl.getControlsOut().end()) { // The measured qubit is a target, not a control of the gate. return mlir::failure(); } - swapGateWithMeasurement(predecessorUnitary, op, rewriter); + swapGateWithMeasurement(predecessorCtrl, op, rewriter); return mlir::success(); } diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp index 61b9458c13..eca6c31a16 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -54,9 +54,6 @@ class QCOMeasurementLiftingTest : public testing::Test { registry.insert(); context.appendDialectRegistry(registry); context.loadAllAvailableDialects(); - - programBuilder.initialize(); - referenceBuilder.initialize(); } /** @@ -87,6 +84,8 @@ class QCOMeasurementLiftingTest : public testing::Test { } // namespace TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPositiveControl) { + programBuilder.initialize( + {programBuilder.getI1Type(), programBuilder.getI1Type()}); auto q0_0 = programBuilder.allocQubit(); auto q1_0 = programBuilder.allocQubit(); @@ -99,8 +98,10 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPositiveControl) { programBuilder.sink(q0_4); programBuilder.sink(q1_4); - module = programBuilder.finalize(); + module = programBuilder.finalize({c0, c1}); + referenceBuilder.initialize( + {programBuilder.getI1Type(), programBuilder.getI1Type()}); auto r0_0 = referenceBuilder.allocQubit(); auto r1_0 = referenceBuilder.allocQubit(); @@ -113,7 +114,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPositiveControl) { referenceBuilder.sink(r0_4); referenceBuilder.sink(r1_4); - reference = referenceBuilder.finalize(); + reference = referenceBuilder.finalize({cr0, cr1}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); @@ -123,6 +124,9 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPositiveControl) { } TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { + programBuilder.initialize({programBuilder.getI1Type(), + programBuilder.getI1Type(), + programBuilder.getI1Type()}); auto q0_0 = programBuilder.allocQubit(); auto q1_0 = programBuilder.allocQubit(); auto q2_0 = programBuilder.allocQubit(); @@ -152,8 +156,11 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { programBuilder.sink(q1_4); programBuilder.sink(q2_5); - module = programBuilder.finalize(); + module = programBuilder.finalize({c0, c1, c2}); + referenceBuilder.initialize({programBuilder.getI1Type(), + programBuilder.getI1Type(), + programBuilder.getI1Type()}); auto r0_0 = referenceBuilder.allocQubit(); auto r1_0 = referenceBuilder.allocQubit(); auto r2_0 = referenceBuilder.allocQubit(); @@ -183,7 +190,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { referenceBuilder.sink(r12_2[0]); referenceBuilder.sink(r2_5); - reference = referenceBuilder.finalize(); + reference = referenceBuilder.finalize({cr0, cr1, cr2}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); @@ -194,6 +201,9 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { TEST_F(QCOMeasurementLiftingTest, liftMeasurementMultipleOverOneControlledGate) { + + programBuilder.initialize( + {programBuilder.getI1Type(), programBuilder.getI1Type()}); auto q0_0 = programBuilder.allocQubit(); auto q1_0 = programBuilder.allocQubit(); auto q2_0 = programBuilder.allocQubit(); @@ -209,8 +219,10 @@ TEST_F(QCOMeasurementLiftingTest, programBuilder.sink(q0_1[0]); programBuilder.sink(q1_1); programBuilder.sink(q2_1); - module = programBuilder.finalize(); + module = programBuilder.finalize({c1, c2}); + referenceBuilder.initialize( + {programBuilder.getI1Type(), programBuilder.getI1Type()}); auto r0_0 = referenceBuilder.allocQubit(); auto r1_0 = referenceBuilder.allocQubit(); auto r2_0 = referenceBuilder.allocQubit(); @@ -226,7 +238,7 @@ TEST_F(QCOMeasurementLiftingTest, referenceBuilder.sink(r0_1[0]); referenceBuilder.sink(r12_0[0]); referenceBuilder.sink(r12_0[1]); - reference = referenceBuilder.finalize(); + reference = referenceBuilder.finalize({cr1, cr2}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); @@ -237,6 +249,8 @@ TEST_F(QCOMeasurementLiftingTest, TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverControlledParametrizedGate) { + programBuilder.initialize( + {programBuilder.getI1Type(), programBuilder.getI1Type()}); auto q0_0 = programBuilder.allocQubit(); auto q1_0 = programBuilder.allocQubit(); @@ -247,8 +261,10 @@ TEST_F(QCOMeasurementLiftingTest, programBuilder.sink(q0_2); programBuilder.sink(q1_2); - module = programBuilder.finalize(); + module = programBuilder.finalize({c0, c1}); + referenceBuilder.initialize( + {programBuilder.getI1Type(), programBuilder.getI1Type()}); auto r0_0 = referenceBuilder.allocQubit(); auto r1_0 = referenceBuilder.allocQubit(); @@ -260,7 +276,7 @@ TEST_F(QCOMeasurementLiftingTest, referenceBuilder.sink(r0_2); referenceBuilder.sink(r1_2); - reference = referenceBuilder.finalize(); + reference = referenceBuilder.finalize({cr0, cr1}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); @@ -270,12 +286,15 @@ TEST_F(QCOMeasurementLiftingTest, } TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleX) { + + programBuilder.initialize({programBuilder.getI1Type()}); auto q_0 = programBuilder.allocQubit(); auto q_1 = programBuilder.x(q_0); auto [q_2, c] = programBuilder.measure(q_1); programBuilder.sink(q_2); - module = programBuilder.finalize(i1ToI64(c, programBuilder)); + module = programBuilder.finalize(c); + referenceBuilder.initialize({programBuilder.getI1Type()}); auto r_0 = referenceBuilder.allocQubit(); auto true_constant = referenceBuilder.boolConstant(true); auto [r_1, cr] = referenceBuilder.measure(r_0); @@ -283,8 +302,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleX) { auto xorOp = arith::XOrIOp::create( referenceBuilder, referenceBuilder.getLoc(), cr, true_constant); referenceBuilder.sink(r_1); - reference = - referenceBuilder.finalize(i1ToI64(xorOp.getResult(), referenceBuilder)); + reference = referenceBuilder.finalize(xorOp.getResult()); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); @@ -294,19 +312,21 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleX) { } TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleY) { + programBuilder.initialize({programBuilder.getI1Type()}); auto q_0 = programBuilder.allocQubit(); auto q_1 = programBuilder.y(q_0); auto [q_2, c] = programBuilder.measure(q_1); programBuilder.sink(q_2); - module = programBuilder.finalize(); + module = programBuilder.finalize({c}); + referenceBuilder.initialize({programBuilder.getI1Type()}); auto r_0 = referenceBuilder.allocQubit(); auto true_constant = referenceBuilder.boolConstant(true); auto [r_1, cr] = referenceBuilder.measure(r_0); auto xorOp = arith::XOrIOp::create( referenceBuilder, referenceBuilder.getLoc(), cr, true_constant); referenceBuilder.sink(r_1); - reference = referenceBuilder.finalize(); + reference = referenceBuilder.finalize({xorOp.getResult()}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); @@ -316,6 +336,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleY) { } TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPhaseGates) { + programBuilder.initialize({programBuilder.getI1Type()}); auto q_0 = programBuilder.allocQubit(); auto q_1 = programBuilder.id(q_0); auto q_2 = programBuilder.z(q_1); @@ -327,12 +348,13 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPhaseGates) { auto q_8 = programBuilder.rz(std::numbers::pi / 2, q_7); auto [q_9, c] = programBuilder.measure(q_8); programBuilder.sink(q_9); - module = programBuilder.finalize(); + module = programBuilder.finalize({c}); + referenceBuilder.initialize({programBuilder.getI1Type()}); auto r_0 = referenceBuilder.allocQubit(); auto [r_1, cr] = referenceBuilder.measure(r_0); referenceBuilder.sink(r_1); - reference = referenceBuilder.finalize(); + reference = referenceBuilder.finalize({cr}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); @@ -342,17 +364,19 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPhaseGates) { } TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverMultipleXY) { + programBuilder.initialize({programBuilder.getI1Type()}); auto q_0 = programBuilder.allocQubit(); auto q_1 = programBuilder.x(q_0); auto q_2 = programBuilder.y(q_1); auto [q_3, c] = programBuilder.measure(q_2); programBuilder.sink(q_3); - module = programBuilder.finalize(); + module = programBuilder.finalize({c}); + referenceBuilder.initialize({programBuilder.getI1Type()}); auto r_0 = referenceBuilder.allocQubit(); auto [r_1, cr] = referenceBuilder.measure(r_0); referenceBuilder.sink(r_1); - reference = referenceBuilder.finalize(); + reference = referenceBuilder.finalize({cr}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); @@ -362,6 +386,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverMultipleXY) { } TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { + programBuilder.initialize({programBuilder.getI1Type()}); auto q0_0 = programBuilder.allocQubit(); auto q1_0 = programBuilder.allocQubit(); @@ -374,8 +399,9 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { programBuilder.sink(q0_5); programBuilder.sink(q1_2); - module = programBuilder.finalize(); + module = programBuilder.finalize({c0}); + referenceBuilder.initialize({programBuilder.getI1Type()}); auto r0_0 = referenceBuilder.allocQubit(); auto r1_0 = referenceBuilder.allocQubit(); @@ -387,7 +413,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { referenceBuilder.sink(r0_4); referenceBuilder.sink(r1_2); - reference = referenceBuilder.finalize(); + reference = referenceBuilder.finalize({cr0}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); @@ -397,6 +423,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { } TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverDiagonalGateInControl) { + programBuilder.initialize({programBuilder.getI1Type()}); auto q0_0 = programBuilder.allocQubit(); auto q1_0 = programBuilder.allocQubit(); @@ -406,8 +433,9 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverDiagonalGateInControl) { programBuilder.sink(q0_2); programBuilder.sink(q1_1); - module = programBuilder.finalize(); + module = programBuilder.finalize({c0}); + referenceBuilder.initialize({programBuilder.getI1Type()}); auto r0_0 = referenceBuilder.allocQubit(); auto r1_0 = referenceBuilder.allocQubit(); @@ -415,7 +443,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverDiagonalGateInControl) { referenceBuilder.sink(r0_1); referenceBuilder.sink(r1_0); - reference = referenceBuilder.finalize(); + reference = referenceBuilder.finalize({cr0}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); From 559c9fffd5963d0865a472c57c806be3bbcbff03 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 2 Jun 2026 14:21:20 +0200 Subject: [PATCH 07/25] style(mlir): :rotating_light: fix linter issues --- .../test_qco_measurement_lifting.cpp | 303 +++++++++--------- 1 file changed, 152 insertions(+), 151 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp index eca6c31a16..4847e1b7cf 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -86,34 +87,34 @@ class QCOMeasurementLiftingTest : public testing::Test { TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPositiveControl) { programBuilder.initialize( {programBuilder.getI1Type(), programBuilder.getI1Type()}); - auto q0_0 = programBuilder.allocQubit(); - auto q1_0 = programBuilder.allocQubit(); + auto q0S0 = programBuilder.allocQubit(); + auto q1S0 = programBuilder.allocQubit(); - auto [q1_1, q0_1] = programBuilder.cx(q1_0, q0_0); - auto [q0_2, q1_2] = programBuilder.ch(q0_1, q1_1); - auto [q0_3, q1_3] = programBuilder.cx(q0_2, q1_2); + auto [q1S1, q0S1] = programBuilder.cx(q1S0, q0S0); + auto [q0S2, q1S2] = programBuilder.ch(q0S1, q1S1); + auto [q0S3, q1S3] = programBuilder.cx(q0S2, q1S2); - auto [q0_4, c0] = programBuilder.measure(q0_3); - auto [q1_4, c1] = programBuilder.measure(q1_3); + auto [q0S4, c0] = programBuilder.measure(q0S3); + auto [q1S4, c1] = programBuilder.measure(q1S3); - programBuilder.sink(q0_4); - programBuilder.sink(q1_4); + programBuilder.sink(q0S4); + programBuilder.sink(q1S4); module = programBuilder.finalize({c0, c1}); referenceBuilder.initialize( {programBuilder.getI1Type(), programBuilder.getI1Type()}); - auto r0_0 = referenceBuilder.allocQubit(); - auto r1_0 = referenceBuilder.allocQubit(); + auto r0S0 = referenceBuilder.allocQubit(); + auto r1S0 = referenceBuilder.allocQubit(); - auto [r1_1, r0_1] = referenceBuilder.cx(r1_0, r0_0); - auto [r0_2, cr0] = referenceBuilder.measure(r0_1); - auto [r0_3, r1_2] = referenceBuilder.ch(r0_2, r1_1); - auto [r0_4, r1_3] = referenceBuilder.cx(r0_3, r1_2); + auto [r1S1, r0S1] = referenceBuilder.cx(r1S0, r0S0); + auto [r0S2, cr0] = referenceBuilder.measure(r0S1); + auto [r0S3, r1S2] = referenceBuilder.ch(r0S2, r1S1); + auto [r0S4, r1S3] = referenceBuilder.cx(r0S3, r1S2); - auto [r1_4, cr1] = referenceBuilder.measure(r1_3); + auto [r1S4, cr1] = referenceBuilder.measure(r1S3); - referenceBuilder.sink(r0_4); - referenceBuilder.sink(r1_4); + referenceBuilder.sink(r0S4); + referenceBuilder.sink(r1S4); reference = referenceBuilder.finalize({cr0, cr1}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -127,68 +128,68 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { programBuilder.initialize({programBuilder.getI1Type(), programBuilder.getI1Type(), programBuilder.getI1Type()}); - auto q0_0 = programBuilder.allocQubit(); - auto q1_0 = programBuilder.allocQubit(); - auto q2_0 = programBuilder.allocQubit(); + auto q0S0 = programBuilder.allocQubit(); + auto q1S0 = programBuilder.allocQubit(); + auto q2S0 = programBuilder.allocQubit(); - auto [q12_0, q0_1] = - programBuilder.ctrl({q1_0, q2_0}, {q0_0}, [&](const ValueRange target) { + auto [q12_0, q0S1] = + programBuilder.ctrl({q1S0, q2S0}, {q0S0}, [&](const ValueRange target) { return SmallVector{programBuilder.x(target[0])}; }); - auto [q12_1, q0_2] = programBuilder.ctrl( - {q12_0[1], q12_0[0]}, q0_1, [&](const ValueRange target) { + auto [q12_1, q0S2] = programBuilder.ctrl( + {q12_0[1], q12_0[0]}, q0S1, [&](const ValueRange target) { return SmallVector{programBuilder.h(target[0])}; }); - auto [q12_2, q0_3] = programBuilder.ctrl( - {q12_1[1], q12_1[0]}, q0_2, [&](const ValueRange target) { + auto [q12_2, q0S3] = programBuilder.ctrl( + {q12_1[1], q12_1[0]}, q0S2, [&](const ValueRange target) { return SmallVector{programBuilder.x(target[0])}; }); - auto [q1_4, c1] = programBuilder.measure(q12_2[0]); + auto [q1S4, c1] = programBuilder.measure(q12_2[0]); - auto q0_4 = programBuilder.h(q0_3[0]); - auto q2_4 = programBuilder.h(q12_2[1]); + auto q0S4 = programBuilder.h(q0S3[0]); + auto q2S4 = programBuilder.h(q12_2[1]); - auto [q0_5, c0] = programBuilder.measure(q0_4); - auto [q2_5, c2] = programBuilder.measure(q2_4); + auto [q0S5, c0] = programBuilder.measure(q0S4); + auto [q2S5, c2] = programBuilder.measure(q2S4); - programBuilder.sink(q0_5); - programBuilder.sink(q1_4); - programBuilder.sink(q2_5); + programBuilder.sink(q0S5); + programBuilder.sink(q1S4); + programBuilder.sink(q2S5); module = programBuilder.finalize({c0, c1, c2}); referenceBuilder.initialize({programBuilder.getI1Type(), programBuilder.getI1Type(), programBuilder.getI1Type()}); - auto r0_0 = referenceBuilder.allocQubit(); - auto r1_0 = referenceBuilder.allocQubit(); - auto r2_0 = referenceBuilder.allocQubit(); + auto r0S0 = referenceBuilder.allocQubit(); + auto r1S0 = referenceBuilder.allocQubit(); + auto r2S0 = referenceBuilder.allocQubit(); - auto [r1_1, cr1] = referenceBuilder.measure(r1_0); + auto [r1S1, cr1] = referenceBuilder.measure(r1S0); - auto [r12_0, r0_1] = - referenceBuilder.ctrl({r1_1, r2_0}, {r0_0}, [&](const ValueRange target) { + auto [r12_0, r0S1] = + referenceBuilder.ctrl({r1S1, r2S0}, {r0S0}, [&](const ValueRange target) { return SmallVector{referenceBuilder.x(target[0])}; }); - auto [r12_1, r0_2] = referenceBuilder.ctrl( - {r12_0[1], r12_0[0]}, r0_1, [&](const ValueRange target) { + auto [r12_1, r0S2] = referenceBuilder.ctrl( + {r12_0[1], r12_0[0]}, r0S1, [&](const ValueRange target) { return SmallVector{referenceBuilder.h(target[0])}; }); - auto [r12_2, r0_3] = referenceBuilder.ctrl( - {r12_1[1], r12_1[0]}, r0_2, [&](const ValueRange target) { + auto [r12_2, r0S3] = referenceBuilder.ctrl( + {r12_1[1], r12_1[0]}, r0S2, [&](const ValueRange target) { return SmallVector{referenceBuilder.x(target[0])}; }); - auto r0_4 = referenceBuilder.h(r0_3[0]); - auto r2_4 = referenceBuilder.h(r12_2[1]); + auto r0S4 = referenceBuilder.h(r0S3[0]); + auto r2S4 = referenceBuilder.h(r12_2[1]); - auto [r0_5, cr0] = referenceBuilder.measure(r0_4); - auto [r2_5, cr2] = referenceBuilder.measure(r2_4); + auto [r0S5, cr0] = referenceBuilder.measure(r0S4); + auto [r2S5, cr2] = referenceBuilder.measure(r2S4); - referenceBuilder.sink(r0_5); + referenceBuilder.sink(r0S5); referenceBuilder.sink(r12_2[0]); - referenceBuilder.sink(r2_5); + referenceBuilder.sink(r2S5); reference = referenceBuilder.finalize({cr0, cr1, cr2}); @@ -204,38 +205,38 @@ TEST_F(QCOMeasurementLiftingTest, programBuilder.initialize( {programBuilder.getI1Type(), programBuilder.getI1Type()}); - auto q0_0 = programBuilder.allocQubit(); - auto q1_0 = programBuilder.allocQubit(); - auto q2_0 = programBuilder.allocQubit(); + auto q0S0 = programBuilder.allocQubit(); + auto q1S0 = programBuilder.allocQubit(); + auto q2S0 = programBuilder.allocQubit(); - auto [q12_0, q0_1] = - programBuilder.ctrl({q1_0, q2_0}, {q0_0}, [&](const ValueRange target) { + auto [q12_0, q0S1] = + programBuilder.ctrl({q1S0, q2S0}, {q0S0}, [&](const ValueRange target) { return SmallVector{programBuilder.x(target[0])}; }); - auto [q1_1, c1] = programBuilder.measure(q12_0[0]); - auto [q2_1, c2] = programBuilder.measure(q12_0[1]); + auto [q1S1, c1] = programBuilder.measure(q12_0[0]); + auto [q2S1, c2] = programBuilder.measure(q12_0[1]); - programBuilder.sink(q0_1[0]); - programBuilder.sink(q1_1); - programBuilder.sink(q2_1); + programBuilder.sink(q0S1[0]); + programBuilder.sink(q1S1); + programBuilder.sink(q2S1); module = programBuilder.finalize({c1, c2}); referenceBuilder.initialize( {programBuilder.getI1Type(), programBuilder.getI1Type()}); - auto r0_0 = referenceBuilder.allocQubit(); - auto r1_0 = referenceBuilder.allocQubit(); - auto r2_0 = referenceBuilder.allocQubit(); + auto r0S0 = referenceBuilder.allocQubit(); + auto r1S0 = referenceBuilder.allocQubit(); + auto r2S0 = referenceBuilder.allocQubit(); - auto [r1_1, cr1] = referenceBuilder.measure(r1_0); - auto [r2_1, cr2] = referenceBuilder.measure(r2_0); + auto [r1S1, cr1] = referenceBuilder.measure(r1S0); + auto [r2S1, cr2] = referenceBuilder.measure(r2S0); - auto [r12_0, r0_1] = - referenceBuilder.ctrl({r1_1, r2_1}, {r0_0}, [&](const ValueRange target) { + auto [r12_0, r0S1] = + referenceBuilder.ctrl({r1S1, r2S1}, {r0S0}, [&](const ValueRange target) { return SmallVector{referenceBuilder.x(target[0])}; }); - referenceBuilder.sink(r0_1[0]); + referenceBuilder.sink(r0S1[0]); referenceBuilder.sink(r12_0[0]); referenceBuilder.sink(r12_0[1]); reference = referenceBuilder.finalize({cr1, cr2}); @@ -251,31 +252,31 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverControlledParametrizedGate) { programBuilder.initialize( {programBuilder.getI1Type(), programBuilder.getI1Type()}); - auto q0_0 = programBuilder.allocQubit(); - auto q1_0 = programBuilder.allocQubit(); + auto q0S0 = programBuilder.allocQubit(); + auto q1S0 = programBuilder.allocQubit(); - auto [q0_1, q1_1] = programBuilder.crx(std::numbers::pi / 2, q0_0, q1_0); + auto [q0S1, q1S1] = programBuilder.crx(std::numbers::pi / 2, q0S0, q1S0); - auto [q0_2, c0] = programBuilder.measure(q0_1); - auto [q1_2, c1] = programBuilder.measure(q1_1); + auto [q0S2, c0] = programBuilder.measure(q0S1); + auto [q1S2, c1] = programBuilder.measure(q1S1); - programBuilder.sink(q0_2); - programBuilder.sink(q1_2); + programBuilder.sink(q0S2); + programBuilder.sink(q1S2); module = programBuilder.finalize({c0, c1}); referenceBuilder.initialize( {programBuilder.getI1Type(), programBuilder.getI1Type()}); - auto r0_0 = referenceBuilder.allocQubit(); - auto r1_0 = referenceBuilder.allocQubit(); + auto r0S0 = referenceBuilder.allocQubit(); + auto r1S0 = referenceBuilder.allocQubit(); - auto [r0_1, cr0] = referenceBuilder.measure(r0_0); + auto [r0S1, cr0] = referenceBuilder.measure(r0S0); - auto [r0_2, r1_1] = referenceBuilder.crx(std::numbers::pi / 2, r0_1, r1_0); + auto [r0S2, r1S1] = referenceBuilder.crx(std::numbers::pi / 2, r0S1, r1S0); - auto [r1_2, cr1] = referenceBuilder.measure(r1_1); + auto [r1S2, cr1] = referenceBuilder.measure(r1S1); - referenceBuilder.sink(r0_2); - referenceBuilder.sink(r1_2); + referenceBuilder.sink(r0S2); + referenceBuilder.sink(r1S2); reference = referenceBuilder.finalize({cr0, cr1}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -288,20 +289,20 @@ TEST_F(QCOMeasurementLiftingTest, TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleX) { programBuilder.initialize({programBuilder.getI1Type()}); - auto q_0 = programBuilder.allocQubit(); - auto q_1 = programBuilder.x(q_0); - auto [q_2, c] = programBuilder.measure(q_1); - programBuilder.sink(q_2); + auto q0 = programBuilder.allocQubit(); + auto q1 = programBuilder.x(q0); + auto [q2, c] = programBuilder.measure(q1); + programBuilder.sink(q2); module = programBuilder.finalize(c); referenceBuilder.initialize({programBuilder.getI1Type()}); - auto r_0 = referenceBuilder.allocQubit(); - auto true_constant = referenceBuilder.boolConstant(true); - auto [r_1, cr] = referenceBuilder.measure(r_0); + auto r0 = referenceBuilder.allocQubit(); + auto trueConstant = referenceBuilder.boolConstant(true); + auto [r1, cr] = referenceBuilder.measure(r0); auto xorOp = arith::XOrIOp::create( - referenceBuilder, referenceBuilder.getLoc(), cr, true_constant); - referenceBuilder.sink(r_1); + referenceBuilder, referenceBuilder.getLoc(), cr, trueConstant); + referenceBuilder.sink(r1); reference = referenceBuilder.finalize(xorOp.getResult()); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -313,19 +314,19 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleX) { TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleY) { programBuilder.initialize({programBuilder.getI1Type()}); - auto q_0 = programBuilder.allocQubit(); - auto q_1 = programBuilder.y(q_0); - auto [q_2, c] = programBuilder.measure(q_1); - programBuilder.sink(q_2); + auto q0 = programBuilder.allocQubit(); + auto q1 = programBuilder.y(q0); + auto [q2, c] = programBuilder.measure(q1); + programBuilder.sink(q2); module = programBuilder.finalize({c}); referenceBuilder.initialize({programBuilder.getI1Type()}); - auto r_0 = referenceBuilder.allocQubit(); - auto true_constant = referenceBuilder.boolConstant(true); - auto [r_1, cr] = referenceBuilder.measure(r_0); + auto r0 = referenceBuilder.allocQubit(); + auto trueConstant = referenceBuilder.boolConstant(true); + auto [r1, cr] = referenceBuilder.measure(r0); auto xorOp = arith::XOrIOp::create( - referenceBuilder, referenceBuilder.getLoc(), cr, true_constant); - referenceBuilder.sink(r_1); + referenceBuilder, referenceBuilder.getLoc(), cr, trueConstant); + referenceBuilder.sink(r1); reference = referenceBuilder.finalize({xorOp.getResult()}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -337,23 +338,23 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleY) { TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPhaseGates) { programBuilder.initialize({programBuilder.getI1Type()}); - auto q_0 = programBuilder.allocQubit(); - auto q_1 = programBuilder.id(q_0); - auto q_2 = programBuilder.z(q_1); - auto q_3 = programBuilder.s(q_2); - auto q_4 = programBuilder.sdg(q_3); - auto q_5 = programBuilder.t(q_4); - auto q_6 = programBuilder.tdg(q_5); - auto q_7 = programBuilder.p(std::numbers::pi / 2, q_6); - auto q_8 = programBuilder.rz(std::numbers::pi / 2, q_7); - auto [q_9, c] = programBuilder.measure(q_8); - programBuilder.sink(q_9); + auto q0 = programBuilder.allocQubit(); + auto q1 = programBuilder.id(q0); + auto q2 = programBuilder.z(q1); + auto q3 = programBuilder.s(q2); + auto q4 = programBuilder.sdg(q3); + auto q5 = programBuilder.t(q4); + auto q6 = programBuilder.tdg(q5); + auto q7 = programBuilder.p(std::numbers::pi / 2, q6); + auto q8 = programBuilder.rz(std::numbers::pi / 2, q7); + auto [q9, c] = programBuilder.measure(q8); + programBuilder.sink(q9); module = programBuilder.finalize({c}); referenceBuilder.initialize({programBuilder.getI1Type()}); - auto r_0 = referenceBuilder.allocQubit(); - auto [r_1, cr] = referenceBuilder.measure(r_0); - referenceBuilder.sink(r_1); + auto r0 = referenceBuilder.allocQubit(); + auto [r1, cr] = referenceBuilder.measure(r0); + referenceBuilder.sink(r1); reference = referenceBuilder.finalize({cr}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -365,17 +366,17 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPhaseGates) { TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverMultipleXY) { programBuilder.initialize({programBuilder.getI1Type()}); - auto q_0 = programBuilder.allocQubit(); - auto q_1 = programBuilder.x(q_0); - auto q_2 = programBuilder.y(q_1); - auto [q_3, c] = programBuilder.measure(q_2); - programBuilder.sink(q_3); + auto q0 = programBuilder.allocQubit(); + auto q1 = programBuilder.x(q0); + auto q2 = programBuilder.y(q1); + auto [q3, c] = programBuilder.measure(q2); + programBuilder.sink(q3); module = programBuilder.finalize({c}); referenceBuilder.initialize({programBuilder.getI1Type()}); - auto r_0 = referenceBuilder.allocQubit(); - auto [r_1, cr] = referenceBuilder.measure(r_0); - referenceBuilder.sink(r_1); + auto r0 = referenceBuilder.allocQubit(); + auto [r1, cr] = referenceBuilder.measure(r0); + referenceBuilder.sink(r1); reference = referenceBuilder.finalize({cr}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -387,32 +388,32 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverMultipleXY) { TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { programBuilder.initialize({programBuilder.getI1Type()}); - auto q0_0 = programBuilder.allocQubit(); - auto q1_0 = programBuilder.allocQubit(); + auto q0S0 = programBuilder.allocQubit(); + auto q1S0 = programBuilder.allocQubit(); - auto [q0_1, q1_1] = programBuilder.cy(q0_0, q1_0); - auto q0_2 = programBuilder.x(q0_1); - auto [q0_3, q1_2] = programBuilder.cy(q0_2, q1_1); - auto q0_4 = programBuilder.x(q0_3); + auto [q0S1, q1S1] = programBuilder.cy(q0S0, q1S0); + auto q0S2 = programBuilder.x(q0S1); + auto [q0S3, q1S2] = programBuilder.cy(q0S2, q1S1); + auto q0S4 = programBuilder.x(q0S3); - auto [q0_5, c0] = programBuilder.measure(q0_4); + auto [q0S5, c0] = programBuilder.measure(q0S4); - programBuilder.sink(q0_5); - programBuilder.sink(q1_2); + programBuilder.sink(q0S5); + programBuilder.sink(q1S2); module = programBuilder.finalize({c0}); referenceBuilder.initialize({programBuilder.getI1Type()}); - auto r0_0 = referenceBuilder.allocQubit(); - auto r1_0 = referenceBuilder.allocQubit(); + auto r0S0 = referenceBuilder.allocQubit(); + auto r1S0 = referenceBuilder.allocQubit(); - auto [r0_1, cr0] = referenceBuilder.measure(r0_0); + auto [r0S1, cr0] = referenceBuilder.measure(r0S0); - auto [r0_2, r1_1] = referenceBuilder.cx(r0_1, r1_0); - auto r0_3 = referenceBuilder.x(r0_2); - auto [r0_4, r1_2] = referenceBuilder.cx(r0_3, r1_1); + auto [r0S2, r1S1] = referenceBuilder.cx(r0S1, r1S0); + auto r0S3 = referenceBuilder.x(r0S2); + auto [r0S4, r1S2] = referenceBuilder.cx(r0S3, r1S1); - referenceBuilder.sink(r0_4); - referenceBuilder.sink(r1_2); + referenceBuilder.sink(r0S4); + referenceBuilder.sink(r1S2); reference = referenceBuilder.finalize({cr0}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -424,25 +425,25 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverDiagonalGateInControl) { programBuilder.initialize({programBuilder.getI1Type()}); - auto q0_0 = programBuilder.allocQubit(); - auto q1_0 = programBuilder.allocQubit(); + auto q0S0 = programBuilder.allocQubit(); + auto q1S0 = programBuilder.allocQubit(); - auto [q0_1, q1_1] = programBuilder.cz(q0_0, q1_0); + auto [q0S1, q1S1] = programBuilder.cz(q0S0, q1S0); - auto [q0_2, c0] = programBuilder.measure(q0_1); + auto [q0S2, c0] = programBuilder.measure(q0S1); - programBuilder.sink(q0_2); - programBuilder.sink(q1_1); + programBuilder.sink(q0S2); + programBuilder.sink(q1S1); module = programBuilder.finalize({c0}); referenceBuilder.initialize({programBuilder.getI1Type()}); - auto r0_0 = referenceBuilder.allocQubit(); - auto r1_0 = referenceBuilder.allocQubit(); + auto r0S0 = referenceBuilder.allocQubit(); + auto r1S0 = referenceBuilder.allocQubit(); - auto [r0_1, cr0] = referenceBuilder.measure(r0_0); + auto [r0S1, cr0] = referenceBuilder.measure(r0S0); - referenceBuilder.sink(r0_1); - referenceBuilder.sink(r1_0); + referenceBuilder.sink(r0S1); + referenceBuilder.sink(r1S0); reference = referenceBuilder.finalize({cr0}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); From 679f4547c71c35040319111527f3a64b3b140fe7 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 2 Jun 2026 17:31:33 +0200 Subject: [PATCH 08/25] style(mlir): :recycle: minor code clean-up --- .../test_qco_measurement_lifting.cpp | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp index 4847e1b7cf..6ca087a8c5 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -75,11 +75,6 @@ class QCOMeasurementLiftingTest : public testing::Test { pm.addPass(createCanonicalizerPass()); return pm.run(module); } - - static Value i1ToI64(Value i1Value, ImplicitLocOpBuilder& builder) { - return arith::ExtUIOp::create(builder, builder.getI64Type(), i1Value) - .getResult(); - } }; } // namespace @@ -102,7 +97,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPositiveControl) { module = programBuilder.finalize({c0, c1}); referenceBuilder.initialize( - {programBuilder.getI1Type(), programBuilder.getI1Type()}); + {referenceBuilder.getI1Type(), referenceBuilder.getI1Type()}); auto r0S0 = referenceBuilder.allocQubit(); auto r1S0 = referenceBuilder.allocQubit(); @@ -159,9 +154,9 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { module = programBuilder.finalize({c0, c1, c2}); - referenceBuilder.initialize({programBuilder.getI1Type(), - programBuilder.getI1Type(), - programBuilder.getI1Type()}); + referenceBuilder.initialize({referenceBuilder.getI1Type(), + referenceBuilder.getI1Type(), + referenceBuilder.getI1Type()}); auto r0S0 = referenceBuilder.allocQubit(); auto r1S0 = referenceBuilder.allocQubit(); auto r2S0 = referenceBuilder.allocQubit(); @@ -223,7 +218,7 @@ TEST_F(QCOMeasurementLiftingTest, module = programBuilder.finalize({c1, c2}); referenceBuilder.initialize( - {programBuilder.getI1Type(), programBuilder.getI1Type()}); + {referenceBuilder.getI1Type(), referenceBuilder.getI1Type()}); auto r0S0 = referenceBuilder.allocQubit(); auto r1S0 = referenceBuilder.allocQubit(); auto r2S0 = referenceBuilder.allocQubit(); @@ -265,7 +260,7 @@ TEST_F(QCOMeasurementLiftingTest, module = programBuilder.finalize({c0, c1}); referenceBuilder.initialize( - {programBuilder.getI1Type(), programBuilder.getI1Type()}); + {referenceBuilder.getI1Type(), referenceBuilder.getI1Type()}); auto r0S0 = referenceBuilder.allocQubit(); auto r1S0 = referenceBuilder.allocQubit(); @@ -295,7 +290,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleX) { programBuilder.sink(q2); module = programBuilder.finalize(c); - referenceBuilder.initialize({programBuilder.getI1Type()}); + referenceBuilder.initialize({referenceBuilder.getI1Type()}); auto r0 = referenceBuilder.allocQubit(); auto trueConstant = referenceBuilder.boolConstant(true); auto [r1, cr] = referenceBuilder.measure(r0); @@ -320,7 +315,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleY) { programBuilder.sink(q2); module = programBuilder.finalize({c}); - referenceBuilder.initialize({programBuilder.getI1Type()}); + referenceBuilder.initialize({referenceBuilder.getI1Type()}); auto r0 = referenceBuilder.allocQubit(); auto trueConstant = referenceBuilder.boolConstant(true); auto [r1, cr] = referenceBuilder.measure(r0); @@ -351,7 +346,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPhaseGates) { programBuilder.sink(q9); module = programBuilder.finalize({c}); - referenceBuilder.initialize({programBuilder.getI1Type()}); + referenceBuilder.initialize({referenceBuilder.getI1Type()}); auto r0 = referenceBuilder.allocQubit(); auto [r1, cr] = referenceBuilder.measure(r0); referenceBuilder.sink(r1); @@ -373,7 +368,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverMultipleXY) { programBuilder.sink(q3); module = programBuilder.finalize({c}); - referenceBuilder.initialize({programBuilder.getI1Type()}); + referenceBuilder.initialize({referenceBuilder.getI1Type()}); auto r0 = referenceBuilder.allocQubit(); auto [r1, cr] = referenceBuilder.measure(r0); referenceBuilder.sink(r1); @@ -402,7 +397,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { programBuilder.sink(q1S2); module = programBuilder.finalize({c0}); - referenceBuilder.initialize({programBuilder.getI1Type()}); + referenceBuilder.initialize({referenceBuilder.getI1Type()}); auto r0S0 = referenceBuilder.allocQubit(); auto r1S0 = referenceBuilder.allocQubit(); @@ -436,7 +431,7 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverDiagonalGateInControl) { programBuilder.sink(q1S1); module = programBuilder.finalize({c0}); - referenceBuilder.initialize({programBuilder.getI1Type()}); + referenceBuilder.initialize({referenceBuilder.getI1Type()}); auto r0S0 = referenceBuilder.allocQubit(); auto r1S0 = referenceBuilder.allocQubit(); From 5510fe8367b9da023e175b7461d5588222381259 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 2 Jun 2026 18:11:58 +0200 Subject: [PATCH 09/25] style(mlir): :rotating_light: fix linter issue regarding unused include --- .../Transforms/Optimizations/test_qco_measurement_lifting.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp index 6ca087a8c5..f197a467f7 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include From 64cffab485160cf74c61c4f5aaca024e9af27831 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 01:18:05 +0000 Subject: [PATCH 10/25] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dialect/QCO/Transforms/Optimizations/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt index cdd34bdf46..bdc78401c8 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt @@ -7,10 +7,8 @@ # Licensed under the MIT License set(target_name mqt-core-mlir-unittest-optimizations) -add_executable(${target_name} test_qco_hadamard_lifting.cpp - test_qco_measurement_lifting.cpp - test_qco_merge_single_qubit_rotation.cpp - test_quantum_loop_unroll.cpp) +add_executable(${target_name} test_qco_hadamard_lifting.cpp test_qco_measurement_lifting.cpp + test_qco_merge_single_qubit_rotation.cpp test_quantum_loop_unroll.cpp) target_link_libraries( ${target_name} From df2301b905ee97b8a76aa81cc733310440eeb752 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 14 Jul 2026 03:20:26 +0200 Subject: [PATCH 11/25] chore(mlir): :pencil2: clean up minor typo --- mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h | 1 + 1 file changed, 1 insertion(+) diff --git a/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h b/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h index a029bda7e4..14ea2e85f9 100644 --- a/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h +++ b/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h @@ -103,6 +103,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { * @param returnTypes The new return types for the main function */ void retype(TypeRange returnTypes); + //===--------------------------------------------------------------------===// // Constants //===--------------------------------------------------------------------===// From 8ca81d65bc726c27c6538523ed401780640edabd Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 14 Jul 2026 03:34:48 +0200 Subject: [PATCH 12/25] fix(mlir): :pencil2: fix some further typos --- mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp | 1 - .../Transforms/Optimizations/MeasurementLifting.cpp | 10 ++++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp b/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp index 1a5e2c362a..5d800fb99e 100644 --- a/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp +++ b/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp @@ -96,7 +96,6 @@ Value QCOProgramBuilder::boolConstant(const bool value) { checkFinalized(); return arith::ConstantOp::create(*this, getBoolAttr(value)).getResult(); } -} Value& QCOProgramBuilder::QubitRegister::operator[](const size_t index) { if (index >= qubits.size()) { diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp index 1fb173a348..8e752eeca7 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp @@ -45,10 +45,16 @@ static bool isInverting(Operation* op) { return isa(op); } */ static bool isDiagonal(Operation* op) { if (auto c = dyn_cast(op)) { - return isDiagonal(c.getBodyUnitary()); + if (c.getNumBodyUnitaries() != 1) { + return false; + } + return isDiagonal(c.getBodyUnitary(0)); } if (auto i = dyn_cast(op)) { - return isDiagonal(i.getBodyUnitary()); + if (i.getNumBodyUnitaries() != 1) { + return false; + } + return isDiagonal(i.getBodyUnitary(0)); } return isa(op); } From 653a8f495b4e0b5b5c1f2bb756b78caf2498d63a Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 14 Jul 2026 04:08:04 +0200 Subject: [PATCH 13/25] test(mlir): :white_check_mark: add further special cases to tests --- .../Optimizations/MeasurementLifting.cpp | 11 ---- .../test_qco_measurement_lifting.cpp | 65 ++++++++++++++++--- 2 files changed, 57 insertions(+), 19 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp index 8e752eeca7..41c8f02011 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp @@ -134,17 +134,6 @@ struct LiftMeasurementsAboveInvertingGatesPattern final mlir::MLIRContext* context) : OpRewritePattern(context) {} - /** - * @brief Checks if the given qubit is not used anymore. - * @param outQubit The output qubit to check. - * @return True if all users are resets/deallocs, false otherwise. - */ - static bool outputQubitRemainsUnused(mlir::Value outQubit) { - return llvm::all_of(outQubit.getUsers(), [](mlir::Operation* user) { - return mlir::isa(user) || mlir::isa(user); - }); - } - mlir::LogicalResult matchAndRewrite(MeasureOp op, mlir::PatternRewriter& rewriter) const override { diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp index f197a467f7..647392bb29 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -286,17 +286,19 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleX) { auto q0 = programBuilder.allocQubit(); auto q1 = programBuilder.x(q0); auto [q2, c] = programBuilder.measure(q1); - programBuilder.sink(q2); + auto q3 = programBuilder.h(q2); + programBuilder.sink(q3); module = programBuilder.finalize(c); referenceBuilder.initialize({referenceBuilder.getI1Type()}); auto r0 = referenceBuilder.allocQubit(); auto trueConstant = referenceBuilder.boolConstant(true); auto [r1, cr] = referenceBuilder.measure(r0); + auto r2 = referenceBuilder.h(r1); auto xorOp = arith::XOrIOp::create( referenceBuilder, referenceBuilder.getLoc(), cr, trueConstant); - referenceBuilder.sink(r1); + referenceBuilder.sink(r2); reference = referenceBuilder.finalize(xorOp.getResult()); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -418,27 +420,74 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { } TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverDiagonalGateInControl) { - programBuilder.initialize({programBuilder.getI1Type()}); + programBuilder.initialize( + {programBuilder.getI1Type(), programBuilder.getI1Type()}); auto q0S0 = programBuilder.allocQubit(); auto q1S0 = programBuilder.allocQubit(); auto [q0S1, q1S1] = programBuilder.cz(q0S0, q1S0); auto [q0S2, c0] = programBuilder.measure(q0S1); + auto [q1S2, c1] = programBuilder.measure(q1S1); programBuilder.sink(q0S2); - programBuilder.sink(q1S1); - module = programBuilder.finalize({c0}); + programBuilder.sink(q1S2); + module = programBuilder.finalize({c0, c1}); - referenceBuilder.initialize({referenceBuilder.getI1Type()}); + referenceBuilder.initialize( + {referenceBuilder.getI1Type(), programBuilder.getI1Type()}); auto r0S0 = referenceBuilder.allocQubit(); auto r1S0 = referenceBuilder.allocQubit(); auto [r0S1, cr0] = referenceBuilder.measure(r0S0); + auto [r1S1, cr1] = referenceBuilder.measure(r1S0); referenceBuilder.sink(r0S1); - referenceBuilder.sink(r1S0); - reference = referenceBuilder.finalize({cr0}); + referenceBuilder.sink(r1S1); + reference = referenceBuilder.finalize({cr0, cr1}); + + ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); + ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); + + EXPECT_TRUE( + areModulesEquivalentWithPermutations(module.get(), reference.get())); +} + +TEST_F(QCOMeasurementLiftingTest, dontLiftMeasurementMultipleGatesInControl) { + programBuilder.initialize( + {programBuilder.getI1Type(), referenceBuilder.getI1Type()}); + auto q0S0 = programBuilder.allocQubit(); + auto q1S0 = programBuilder.allocQubit(); + + auto [q0S1, q1S1] = programBuilder.ctrl({q0S0}, q1S0, [&](Value target) { + auto t = programBuilder.z(target); + return programBuilder.x(t); + }); + + auto [q0S2, c0] = programBuilder.measure(q0S1); + auto [q1S2, c1] = programBuilder.measure(q1S1); + + programBuilder.sink(q0S2); + programBuilder.sink(q1S2); + module = programBuilder.finalize({c0, c1}); + + referenceBuilder.initialize( + {referenceBuilder.getI1Type(), referenceBuilder.getI1Type()}); + auto r0S0 = referenceBuilder.allocQubit(); + auto r1S0 = referenceBuilder.allocQubit(); + + auto [r0S1, cr0] = referenceBuilder.measure(r0S0); + + auto [r0S2, r1S1] = referenceBuilder.ctrl({r0S1}, r1S0, [&](Value target) { + auto t = referenceBuilder.z(target); + return referenceBuilder.x(t); + }); + + auto [r1S2, cr1] = referenceBuilder.measure(r1S1); + + referenceBuilder.sink(r0S2); + referenceBuilder.sink(r1S2); + reference = referenceBuilder.finalize({cr0, cr1}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); From 1611c1b25865afe8cfb560a0e99e3810dc319184 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 14 Jul 2026 05:36:14 +0200 Subject: [PATCH 14/25] chore(mlir): :recycle: add docstrings to new tests --- .../test_qco_measurement_lifting.cpp | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp index 647392bb29..f96a4ab947 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -78,6 +78,10 @@ class QCOMeasurementLiftingTest : public testing::Test { } // namespace +/** + * @brief Test: Measurements on control bits can be lifted over the controlled + * gates. + */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPositiveControl) { programBuilder.initialize( {programBuilder.getI1Type(), programBuilder.getI1Type()}); @@ -118,6 +122,10 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPositiveControl) { areModulesEquivalentWithPermutations(module.get(), reference.get())); } +/** + * @brief Test: Tests that lifting also works if there are multiple controls in + * a controlled gate. + */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { programBuilder.initialize({programBuilder.getI1Type(), programBuilder.getI1Type(), @@ -194,6 +202,10 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { areModulesEquivalentWithPermutations(module.get(), reference.get())); } +/** + * @brief Test: Tests that multiple measurements that each target a control + * qubit of a controlled gate can be lifted over the controlled gate. + */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementMultipleOverOneControlledGate) { @@ -242,6 +254,10 @@ TEST_F(QCOMeasurementLiftingTest, areModulesEquivalentWithPermutations(module.get(), reference.get())); } +/** + * @brief Test: Tests that a measurement can also be lifted over the control of + * a parametrized gate. + */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverControlledParametrizedGate) { programBuilder.initialize( @@ -280,6 +296,10 @@ TEST_F(QCOMeasurementLiftingTest, areModulesEquivalentWithPermutations(module.get(), reference.get())); } +/** + * @brief Test: Tests lifting a measurement over a single X (anti-diagonal) + * gate. + */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleX) { programBuilder.initialize({programBuilder.getI1Type()}); @@ -308,6 +328,10 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleX) { areModulesEquivalentWithPermutations(module.get(), reference.get())); } +/** + * @brief Test: Tests lifting a measurement over a single Y (anti-diagonal) + * gate. + */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleY) { programBuilder.initialize({programBuilder.getI1Type()}); auto q0 = programBuilder.allocQubit(); @@ -332,6 +356,9 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleY) { areModulesEquivalentWithPermutations(module.get(), reference.get())); } +/** + * @brief Test: Tests lifting a measurement over different diagonal phase-gates. + */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPhaseGates) { programBuilder.initialize({programBuilder.getI1Type()}); auto q0 = programBuilder.allocQubit(); @@ -360,6 +387,9 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPhaseGates) { areModulesEquivalentWithPermutations(module.get(), reference.get())); } +/** + * @brief Test: Tests lifting a measurement over multiple anti-diagonal gates. + */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverMultipleXY) { programBuilder.initialize({programBuilder.getI1Type()}); auto q0 = programBuilder.allocQubit(); @@ -382,6 +412,10 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverMultipleXY) { areModulesEquivalentWithPermutations(module.get(), reference.get())); } +/** + * @brief Test: Tests lifting a measurement over multiple anti-diagonal and + * controlled gates. + */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { programBuilder.initialize({programBuilder.getI1Type()}); auto q0S0 = programBuilder.allocQubit(); @@ -419,6 +453,9 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { areModulesEquivalentWithPermutations(module.get(), reference.get())); } +/** + * @brief Test: Tests lifting a measurement over a controlled diagonal gate. + */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverDiagonalGateInControl) { programBuilder.initialize( {programBuilder.getI1Type(), programBuilder.getI1Type()}); @@ -453,6 +490,10 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverDiagonalGateInControl) { areModulesEquivalentWithPermutations(module.get(), reference.get())); } +/** + * @brief Test: Tests that a measurement is not lifted over a controlled + * sequence gate if there are multiple gates inside the control block. + */ TEST_F(QCOMeasurementLiftingTest, dontLiftMeasurementMultipleGatesInControl) { programBuilder.initialize( {programBuilder.getI1Type(), referenceBuilder.getI1Type()}); From 4a158fba85a074267f1ec4759750c3cf5d32db10 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 14 Jul 2026 05:45:37 +0200 Subject: [PATCH 15/25] test(mlir): :white_check_mark: add test for inverse gates --- .../test_qco_measurement_lifting.cpp | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp index f96a4ab947..852b154124 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -536,3 +536,30 @@ TEST_F(QCOMeasurementLiftingTest, dontLiftMeasurementMultipleGatesInControl) { EXPECT_TRUE( areModulesEquivalentWithPermutations(module.get(), reference.get())); } + +/** + * @brief Test: Tests lifting a measurement over an inverted phase gate. + */ +TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverInvertedPhaseGates) { + programBuilder.initialize({programBuilder.getI1Type()}); + auto q = programBuilder.allocQubit(); + + q = programBuilder.inv( + q, [&](Value target) { return programBuilder.s(target); }); + + auto [qm, c] = programBuilder.measure(q); + programBuilder.sink(qm); + module = programBuilder.finalize({c}); + + referenceBuilder.initialize({referenceBuilder.getI1Type()}); + auto r0 = referenceBuilder.allocQubit(); + auto [r1, cr] = referenceBuilder.measure(r0); + referenceBuilder.sink(r1); + reference = referenceBuilder.finalize({cr}); + + ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); + ASSERT_TRUE(runCanonicalizerPass(reference.get()).succeeded()); + + EXPECT_TRUE( + areModulesEquivalentWithPermutations(module.get(), reference.get())); +} From 4d986060f11b69897f98ef831ae446742d77a663 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 14 Jul 2026 05:56:46 +0200 Subject: [PATCH 16/25] chore(mlir): :recycle: slight refactoring in tests --- .../test_qco_measurement_lifting.cpp | 347 ++++++++++-------- 1 file changed, 188 insertions(+), 159 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp index 852b154124..7dedaeb2c7 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -30,6 +30,7 @@ #include #include +#include namespace { @@ -85,34 +86,36 @@ class QCOMeasurementLiftingTest : public testing::Test { TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPositiveControl) { programBuilder.initialize( {programBuilder.getI1Type(), programBuilder.getI1Type()}); - auto q0S0 = programBuilder.allocQubit(); - auto q1S0 = programBuilder.allocQubit(); + auto q0 = programBuilder.allocQubit(); + auto q1 = programBuilder.allocQubit(); - auto [q1S1, q0S1] = programBuilder.cx(q1S0, q0S0); - auto [q0S2, q1S2] = programBuilder.ch(q0S1, q1S1); - auto [q0S3, q1S3] = programBuilder.cx(q0S2, q1S2); + std::tie(q1, q0) = programBuilder.cx(q1, q0); + std::tie(q0, q1) = programBuilder.ch(q0, q1); + std::tie(q0, q1) = programBuilder.cx(q0, q1); - auto [q0S4, c0] = programBuilder.measure(q0S3); - auto [q1S4, c1] = programBuilder.measure(q1S3); + Value c0, c1; + std::tie(q0, c0) = programBuilder.measure(q0); + std::tie(q1, c1) = programBuilder.measure(q1); - programBuilder.sink(q0S4); - programBuilder.sink(q1S4); + programBuilder.sink(q0); + programBuilder.sink(q1); module = programBuilder.finalize({c0, c1}); referenceBuilder.initialize( {referenceBuilder.getI1Type(), referenceBuilder.getI1Type()}); - auto r0S0 = referenceBuilder.allocQubit(); - auto r1S0 = referenceBuilder.allocQubit(); + auto r0 = referenceBuilder.allocQubit(); + auto r1 = referenceBuilder.allocQubit(); - auto [r1S1, r0S1] = referenceBuilder.cx(r1S0, r0S0); - auto [r0S2, cr0] = referenceBuilder.measure(r0S1); - auto [r0S3, r1S2] = referenceBuilder.ch(r0S2, r1S1); - auto [r0S4, r1S3] = referenceBuilder.cx(r0S3, r1S2); + std::tie(r1, r0) = referenceBuilder.cx(r1, r0); + Value cr0, cr1; + std::tie(r0, cr0) = referenceBuilder.measure(r0); + std::tie(r0, r1) = referenceBuilder.ch(r0, r1); + std::tie(r0, r1) = referenceBuilder.cx(r0, r1); - auto [r1S4, cr1] = referenceBuilder.measure(r1S3); + std::tie(r1, cr1) = referenceBuilder.measure(r1); - referenceBuilder.sink(r0S4); - referenceBuilder.sink(r1S4); + referenceBuilder.sink(r0); + referenceBuilder.sink(r1); reference = referenceBuilder.finalize({cr0, cr1}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -130,68 +133,74 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { programBuilder.initialize({programBuilder.getI1Type(), programBuilder.getI1Type(), programBuilder.getI1Type()}); - auto q0S0 = programBuilder.allocQubit(); - auto q1S0 = programBuilder.allocQubit(); - auto q2S0 = programBuilder.allocQubit(); + auto q0 = programBuilder.allocQubit(); + auto q1 = programBuilder.allocQubit(); + auto q2 = programBuilder.allocQubit(); - auto [q12_0, q0S1] = - programBuilder.ctrl({q1S0, q2S0}, {q0S0}, [&](const ValueRange target) { + SmallVector q12, q0Vec; + std::tie(q12, q0Vec) = + programBuilder.ctrl({q1, q2}, {q0}, [&](const ValueRange target) { return SmallVector{programBuilder.x(target[0])}; }); - auto [q12_1, q0S2] = programBuilder.ctrl( - {q12_0[1], q12_0[0]}, q0S1, [&](const ValueRange target) { + std::tie(q12, q0Vec) = programBuilder.ctrl( + {q12[1], q12[0]}, q0Vec, [&](const ValueRange target) { return SmallVector{programBuilder.h(target[0])}; }); - auto [q12_2, q0S3] = programBuilder.ctrl( - {q12_1[1], q12_1[0]}, q0S2, [&](const ValueRange target) { + std::tie(q12, q0Vec) = programBuilder.ctrl( + {q12[1], q12[0]}, q0Vec, [&](const ValueRange target) { return SmallVector{programBuilder.x(target[0])}; }); - auto [q1S4, c1] = programBuilder.measure(q12_2[0]); + Value c1; + std::tie(q1, c1) = programBuilder.measure(q12[0]); - auto q0S4 = programBuilder.h(q0S3[0]); - auto q2S4 = programBuilder.h(q12_2[1]); + q0 = programBuilder.h(q0Vec[0]); + q2 = programBuilder.h(q12[1]); - auto [q0S5, c0] = programBuilder.measure(q0S4); - auto [q2S5, c2] = programBuilder.measure(q2S4); + Value c0, c2; + std::tie(q0, c0) = programBuilder.measure(q0); + std::tie(q2, c2) = programBuilder.measure(q2); - programBuilder.sink(q0S5); - programBuilder.sink(q1S4); - programBuilder.sink(q2S5); + programBuilder.sink(q0); + programBuilder.sink(q1); + programBuilder.sink(q2); module = programBuilder.finalize({c0, c1, c2}); referenceBuilder.initialize({referenceBuilder.getI1Type(), referenceBuilder.getI1Type(), referenceBuilder.getI1Type()}); - auto r0S0 = referenceBuilder.allocQubit(); - auto r1S0 = referenceBuilder.allocQubit(); - auto r2S0 = referenceBuilder.allocQubit(); + auto r0 = referenceBuilder.allocQubit(); + auto r1 = referenceBuilder.allocQubit(); + auto r2 = referenceBuilder.allocQubit(); - auto [r1S1, cr1] = referenceBuilder.measure(r1S0); + Value cr1; + std::tie(r1, cr1) = referenceBuilder.measure(r1); - auto [r12_0, r0S1] = - referenceBuilder.ctrl({r1S1, r2S0}, {r0S0}, [&](const ValueRange target) { + SmallVector r12, r0Vec; + std::tie(r12, r0Vec) = + referenceBuilder.ctrl({r1, r2}, {r0}, [&](const ValueRange target) { return SmallVector{referenceBuilder.x(target[0])}; }); - auto [r12_1, r0S2] = referenceBuilder.ctrl( - {r12_0[1], r12_0[0]}, r0S1, [&](const ValueRange target) { + std::tie(r12, r0Vec) = referenceBuilder.ctrl( + {r12[1], r12[0]}, r0Vec, [&](const ValueRange target) { return SmallVector{referenceBuilder.h(target[0])}; }); - auto [r12_2, r0S3] = referenceBuilder.ctrl( - {r12_1[1], r12_1[0]}, r0S2, [&](const ValueRange target) { + std::tie(r12, r0Vec) = referenceBuilder.ctrl( + {r12[1], r12[0]}, r0Vec, [&](const ValueRange target) { return SmallVector{referenceBuilder.x(target[0])}; }); - auto r0S4 = referenceBuilder.h(r0S3[0]); - auto r2S4 = referenceBuilder.h(r12_2[1]); + r0 = referenceBuilder.h(r0Vec[0]); + r2 = referenceBuilder.h(r12[1]); - auto [r0S5, cr0] = referenceBuilder.measure(r0S4); - auto [r2S5, cr2] = referenceBuilder.measure(r2S4); + Value cr0, cr2; + std::tie(r0, cr0) = referenceBuilder.measure(r0); + std::tie(r2, cr2) = referenceBuilder.measure(r2); - referenceBuilder.sink(r0S5); - referenceBuilder.sink(r12_2[0]); - referenceBuilder.sink(r2S5); + referenceBuilder.sink(r0); + referenceBuilder.sink(r12[0]); + referenceBuilder.sink(r2); reference = referenceBuilder.finalize({cr0, cr1, cr2}); @@ -211,40 +220,44 @@ TEST_F(QCOMeasurementLiftingTest, programBuilder.initialize( {programBuilder.getI1Type(), programBuilder.getI1Type()}); - auto q0S0 = programBuilder.allocQubit(); - auto q1S0 = programBuilder.allocQubit(); - auto q2S0 = programBuilder.allocQubit(); + auto q0 = programBuilder.allocQubit(); + auto q1 = programBuilder.allocQubit(); + auto q2 = programBuilder.allocQubit(); - auto [q12_0, q0S1] = - programBuilder.ctrl({q1S0, q2S0}, {q0S0}, [&](const ValueRange target) { + SmallVector q12, q0Vec; + std::tie(q12, q0Vec) = + programBuilder.ctrl({q1, q2}, {q0}, [&](const ValueRange target) { return SmallVector{programBuilder.x(target[0])}; }); - auto [q1S1, c1] = programBuilder.measure(q12_0[0]); - auto [q2S1, c2] = programBuilder.measure(q12_0[1]); + Value c1, c2; + std::tie(q1, c1) = programBuilder.measure(q12[0]); + std::tie(q2, c2) = programBuilder.measure(q12[1]); - programBuilder.sink(q0S1[0]); - programBuilder.sink(q1S1); - programBuilder.sink(q2S1); + programBuilder.sink(q0Vec[0]); + programBuilder.sink(q1); + programBuilder.sink(q2); module = programBuilder.finalize({c1, c2}); referenceBuilder.initialize( {referenceBuilder.getI1Type(), referenceBuilder.getI1Type()}); - auto r0S0 = referenceBuilder.allocQubit(); - auto r1S0 = referenceBuilder.allocQubit(); - auto r2S0 = referenceBuilder.allocQubit(); + auto r0 = referenceBuilder.allocQubit(); + auto r1 = referenceBuilder.allocQubit(); + auto r2 = referenceBuilder.allocQubit(); - auto [r1S1, cr1] = referenceBuilder.measure(r1S0); - auto [r2S1, cr2] = referenceBuilder.measure(r2S0); + Value cr1, cr2; + std::tie(r1, cr1) = referenceBuilder.measure(r1); + std::tie(r2, cr2) = referenceBuilder.measure(r2); - auto [r12_0, r0S1] = - referenceBuilder.ctrl({r1S1, r2S1}, {r0S0}, [&](const ValueRange target) { + SmallVector r12, r0Vec; + std::tie(r12, r0Vec) = + referenceBuilder.ctrl({r1, r2}, {r0}, [&](const ValueRange target) { return SmallVector{referenceBuilder.x(target[0])}; }); - referenceBuilder.sink(r0S1[0]); - referenceBuilder.sink(r12_0[0]); - referenceBuilder.sink(r12_0[1]); + referenceBuilder.sink(r0Vec[0]); + referenceBuilder.sink(r12[0]); + referenceBuilder.sink(r12[1]); reference = referenceBuilder.finalize({cr1, cr2}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -262,31 +275,33 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverControlledParametrizedGate) { programBuilder.initialize( {programBuilder.getI1Type(), programBuilder.getI1Type()}); - auto q0S0 = programBuilder.allocQubit(); - auto q1S0 = programBuilder.allocQubit(); + auto q0 = programBuilder.allocQubit(); + auto q1 = programBuilder.allocQubit(); - auto [q0S1, q1S1] = programBuilder.crx(std::numbers::pi / 2, q0S0, q1S0); + std::tie(q0, q1) = programBuilder.crx(std::numbers::pi / 2, q0, q1); - auto [q0S2, c0] = programBuilder.measure(q0S1); - auto [q1S2, c1] = programBuilder.measure(q1S1); + Value c0, c1; + std::tie(q0, c0) = programBuilder.measure(q0); + std::tie(q1, c1) = programBuilder.measure(q1); - programBuilder.sink(q0S2); - programBuilder.sink(q1S2); + programBuilder.sink(q0); + programBuilder.sink(q1); module = programBuilder.finalize({c0, c1}); referenceBuilder.initialize( {referenceBuilder.getI1Type(), referenceBuilder.getI1Type()}); - auto r0S0 = referenceBuilder.allocQubit(); - auto r1S0 = referenceBuilder.allocQubit(); + auto r0 = referenceBuilder.allocQubit(); + auto r1 = referenceBuilder.allocQubit(); - auto [r0S1, cr0] = referenceBuilder.measure(r0S0); + Value cr0, cr1; + std::tie(r0, cr0) = referenceBuilder.measure(r0); - auto [r0S2, r1S1] = referenceBuilder.crx(std::numbers::pi / 2, r0S1, r1S0); + std::tie(r0, r1) = referenceBuilder.crx(std::numbers::pi / 2, r0, r1); - auto [r1S2, cr1] = referenceBuilder.measure(r1S1); + std::tie(r1, cr1) = referenceBuilder.measure(r1); - referenceBuilder.sink(r0S2); - referenceBuilder.sink(r1S2); + referenceBuilder.sink(r0); + referenceBuilder.sink(r1); reference = referenceBuilder.finalize({cr0, cr1}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -303,22 +318,24 @@ TEST_F(QCOMeasurementLiftingTest, TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleX) { programBuilder.initialize({programBuilder.getI1Type()}); - auto q0 = programBuilder.allocQubit(); - auto q1 = programBuilder.x(q0); - auto [q2, c] = programBuilder.measure(q1); - auto q3 = programBuilder.h(q2); - programBuilder.sink(q3); + auto q = programBuilder.allocQubit(); + q = programBuilder.x(q); + Value c; + std::tie(q, c) = programBuilder.measure(q); + q = programBuilder.h(q); + programBuilder.sink(q); module = programBuilder.finalize(c); referenceBuilder.initialize({referenceBuilder.getI1Type()}); - auto r0 = referenceBuilder.allocQubit(); + auto r = referenceBuilder.allocQubit(); auto trueConstant = referenceBuilder.boolConstant(true); - auto [r1, cr] = referenceBuilder.measure(r0); - auto r2 = referenceBuilder.h(r1); + Value cr; + std::tie(r, cr) = referenceBuilder.measure(r); + r = referenceBuilder.h(r); auto xorOp = arith::XOrIOp::create( referenceBuilder, referenceBuilder.getLoc(), cr, trueConstant); - referenceBuilder.sink(r2); + referenceBuilder.sink(r); reference = referenceBuilder.finalize(xorOp.getResult()); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -334,19 +351,21 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleX) { */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleY) { programBuilder.initialize({programBuilder.getI1Type()}); - auto q0 = programBuilder.allocQubit(); - auto q1 = programBuilder.y(q0); - auto [q2, c] = programBuilder.measure(q1); - programBuilder.sink(q2); + auto q = programBuilder.allocQubit(); + q = programBuilder.y(q); + Value c; + std::tie(q, c) = programBuilder.measure(q); + programBuilder.sink(q); module = programBuilder.finalize({c}); referenceBuilder.initialize({referenceBuilder.getI1Type()}); - auto r0 = referenceBuilder.allocQubit(); + auto r = referenceBuilder.allocQubit(); auto trueConstant = referenceBuilder.boolConstant(true); - auto [r1, cr] = referenceBuilder.measure(r0); + Value cr; + std::tie(r, cr) = referenceBuilder.measure(r); auto xorOp = arith::XOrIOp::create( referenceBuilder, referenceBuilder.getLoc(), cr, trueConstant); - referenceBuilder.sink(r1); + referenceBuilder.sink(r); reference = referenceBuilder.finalize({xorOp.getResult()}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -361,23 +380,25 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverSingleY) { */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPhaseGates) { programBuilder.initialize({programBuilder.getI1Type()}); - auto q0 = programBuilder.allocQubit(); - auto q1 = programBuilder.id(q0); - auto q2 = programBuilder.z(q1); - auto q3 = programBuilder.s(q2); - auto q4 = programBuilder.sdg(q3); - auto q5 = programBuilder.t(q4); - auto q6 = programBuilder.tdg(q5); - auto q7 = programBuilder.p(std::numbers::pi / 2, q6); - auto q8 = programBuilder.rz(std::numbers::pi / 2, q7); - auto [q9, c] = programBuilder.measure(q8); - programBuilder.sink(q9); + auto q = programBuilder.allocQubit(); + q = programBuilder.id(q); + q = programBuilder.z(q); + q = programBuilder.s(q); + q = programBuilder.sdg(q); + q = programBuilder.t(q); + q = programBuilder.tdg(q); + q = programBuilder.p(std::numbers::pi / 2, q); + q = programBuilder.rz(std::numbers::pi / 2, q); + Value c; + std::tie(q, c) = programBuilder.measure(q); + programBuilder.sink(q); module = programBuilder.finalize({c}); referenceBuilder.initialize({referenceBuilder.getI1Type()}); - auto r0 = referenceBuilder.allocQubit(); - auto [r1, cr] = referenceBuilder.measure(r0); - referenceBuilder.sink(r1); + auto r = referenceBuilder.allocQubit(); + Value cr; + std::tie(r, cr) = referenceBuilder.measure(r); + referenceBuilder.sink(r); reference = referenceBuilder.finalize({cr}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -392,17 +413,19 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPhaseGates) { */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverMultipleXY) { programBuilder.initialize({programBuilder.getI1Type()}); - auto q0 = programBuilder.allocQubit(); - auto q1 = programBuilder.x(q0); - auto q2 = programBuilder.y(q1); - auto [q3, c] = programBuilder.measure(q2); - programBuilder.sink(q3); + auto q = programBuilder.allocQubit(); + q = programBuilder.x(q); + q = programBuilder.y(q); + Value c; + std::tie(q, c) = programBuilder.measure(q); + programBuilder.sink(q); module = programBuilder.finalize({c}); referenceBuilder.initialize({referenceBuilder.getI1Type()}); - auto r0 = referenceBuilder.allocQubit(); - auto [r1, cr] = referenceBuilder.measure(r0); - referenceBuilder.sink(r1); + auto r = referenceBuilder.allocQubit(); + Value cr; + std::tie(r, cr) = referenceBuilder.measure(r); + referenceBuilder.sink(r); reference = referenceBuilder.finalize({cr}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -418,32 +441,34 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverMultipleXY) { */ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { programBuilder.initialize({programBuilder.getI1Type()}); - auto q0S0 = programBuilder.allocQubit(); - auto q1S0 = programBuilder.allocQubit(); + auto q0 = programBuilder.allocQubit(); + auto q1 = programBuilder.allocQubit(); - auto [q0S1, q1S1] = programBuilder.cy(q0S0, q1S0); - auto q0S2 = programBuilder.x(q0S1); - auto [q0S3, q1S2] = programBuilder.cy(q0S2, q1S1); - auto q0S4 = programBuilder.x(q0S3); + std::tie(q0, q1) = programBuilder.cy(q0, q1); + q0 = programBuilder.x(q0); + std::tie(q0, q1) = programBuilder.cy(q0, q1); + q0 = programBuilder.x(q0); - auto [q0S5, c0] = programBuilder.measure(q0S4); + Value c0; + std::tie(q0, c0) = programBuilder.measure(q0); - programBuilder.sink(q0S5); - programBuilder.sink(q1S2); + programBuilder.sink(q0); + programBuilder.sink(q1); module = programBuilder.finalize({c0}); referenceBuilder.initialize({referenceBuilder.getI1Type()}); - auto r0S0 = referenceBuilder.allocQubit(); - auto r1S0 = referenceBuilder.allocQubit(); + auto r0 = referenceBuilder.allocQubit(); + auto r1 = referenceBuilder.allocQubit(); - auto [r0S1, cr0] = referenceBuilder.measure(r0S0); + Value cr0; + std::tie(r0, cr0) = referenceBuilder.measure(r0); - auto [r0S2, r1S1] = referenceBuilder.cx(r0S1, r1S0); - auto r0S3 = referenceBuilder.x(r0S2); - auto [r0S4, r1S2] = referenceBuilder.cx(r0S3, r1S1); + std::tie(r0, r1) = referenceBuilder.cx(r0, r1); + r0 = referenceBuilder.x(r0); + std::tie(r0, r1) = referenceBuilder.cx(r0, r1); - referenceBuilder.sink(r0S4); - referenceBuilder.sink(r1S2); + referenceBuilder.sink(r0); + referenceBuilder.sink(r1); reference = referenceBuilder.finalize({cr0}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -459,28 +484,30 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverDiagonalGateInControl) { programBuilder.initialize( {programBuilder.getI1Type(), programBuilder.getI1Type()}); - auto q0S0 = programBuilder.allocQubit(); - auto q1S0 = programBuilder.allocQubit(); + auto q0 = programBuilder.allocQubit(); + auto q1 = programBuilder.allocQubit(); - auto [q0S1, q1S1] = programBuilder.cz(q0S0, q1S0); + std::tie(q0, q1) = programBuilder.cz(q0, q1); - auto [q0S2, c0] = programBuilder.measure(q0S1); - auto [q1S2, c1] = programBuilder.measure(q1S1); + Value c0, c1; + std::tie(q0, c0) = programBuilder.measure(q0); + std::tie(q1, c1) = programBuilder.measure(q1); - programBuilder.sink(q0S2); - programBuilder.sink(q1S2); + programBuilder.sink(q0); + programBuilder.sink(q1); module = programBuilder.finalize({c0, c1}); referenceBuilder.initialize( {referenceBuilder.getI1Type(), programBuilder.getI1Type()}); - auto r0S0 = referenceBuilder.allocQubit(); - auto r1S0 = referenceBuilder.allocQubit(); + auto r0 = referenceBuilder.allocQubit(); + auto r1 = referenceBuilder.allocQubit(); - auto [r0S1, cr0] = referenceBuilder.measure(r0S0); - auto [r1S1, cr1] = referenceBuilder.measure(r1S0); + Value cr0, cr1; + std::tie(r0, cr0) = referenceBuilder.measure(r0); + std::tie(r1, cr1) = referenceBuilder.measure(r1); - referenceBuilder.sink(r0S1); - referenceBuilder.sink(r1S1); + referenceBuilder.sink(r0); + referenceBuilder.sink(r1); reference = referenceBuilder.finalize({cr0, cr1}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); @@ -547,14 +574,16 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverInvertedPhaseGates) { q = programBuilder.inv( q, [&](Value target) { return programBuilder.s(target); }); - auto [qm, c] = programBuilder.measure(q); - programBuilder.sink(qm); + Value c; + std::tie(q, c) = programBuilder.measure(q); + programBuilder.sink(q); module = programBuilder.finalize({c}); referenceBuilder.initialize({referenceBuilder.getI1Type()}); - auto r0 = referenceBuilder.allocQubit(); - auto [r1, cr] = referenceBuilder.measure(r0); - referenceBuilder.sink(r1); + auto r = referenceBuilder.allocQubit(); + Value cr; + std::tie(r, cr) = referenceBuilder.measure(r); + referenceBuilder.sink(r); reference = referenceBuilder.finalize({cr}); ASSERT_TRUE(runMeasurementLiftingPass(module.get()).succeeded()); From 2deb2a48459a4e34d134acbc486632f81a267c29 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 14 Jul 2026 05:58:51 +0200 Subject: [PATCH 17/25] chore(mlir): :recycle: further tests clean up --- .../test_qco_measurement_lifting.cpp | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp index 7dedaeb2c7..f7d40defe0 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -93,7 +93,8 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPositiveControl) { std::tie(q0, q1) = programBuilder.ch(q0, q1); std::tie(q0, q1) = programBuilder.cx(q0, q1); - Value c0, c1; + Value c0; + Value c1; std::tie(q0, c0) = programBuilder.measure(q0); std::tie(q1, c1) = programBuilder.measure(q1); @@ -107,7 +108,8 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverPositiveControl) { auto r1 = referenceBuilder.allocQubit(); std::tie(r1, r0) = referenceBuilder.cx(r1, r0); - Value cr0, cr1; + Value cr0; + Value cr1; std::tie(r0, cr0) = referenceBuilder.measure(r0); std::tie(r0, r1) = referenceBuilder.ch(r0, r1); std::tie(r0, r1) = referenceBuilder.cx(r0, r1); @@ -137,7 +139,8 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { auto q1 = programBuilder.allocQubit(); auto q2 = programBuilder.allocQubit(); - SmallVector q12, q0Vec; + SmallVector q12; + SmallVector q0Vec; std::tie(q12, q0Vec) = programBuilder.ctrl({q1, q2}, {q0}, [&](const ValueRange target) { return SmallVector{programBuilder.x(target[0])}; @@ -157,7 +160,8 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { q0 = programBuilder.h(q0Vec[0]); q2 = programBuilder.h(q12[1]); - Value c0, c2; + Value c0; + Value c2; std::tie(q0, c0) = programBuilder.measure(q0); std::tie(q2, c2) = programBuilder.measure(q2); @@ -177,7 +181,8 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { Value cr1; std::tie(r1, cr1) = referenceBuilder.measure(r1); - SmallVector r12, r0Vec; + SmallVector r12; + SmallVector r0Vec; std::tie(r12, r0Vec) = referenceBuilder.ctrl({r1, r2}, {r0}, [&](const ValueRange target) { return SmallVector{referenceBuilder.x(target[0])}; @@ -194,7 +199,8 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { r0 = referenceBuilder.h(r0Vec[0]); r2 = referenceBuilder.h(r12[1]); - Value cr0, cr2; + Value cr0; + Value cr2; std::tie(r0, cr0) = referenceBuilder.measure(r0); std::tie(r2, cr2) = referenceBuilder.measure(r2); @@ -224,13 +230,15 @@ TEST_F(QCOMeasurementLiftingTest, auto q1 = programBuilder.allocQubit(); auto q2 = programBuilder.allocQubit(); - SmallVector q12, q0Vec; + SmallVector q12; + SmallVector q0Vec; std::tie(q12, q0Vec) = programBuilder.ctrl({q1, q2}, {q0}, [&](const ValueRange target) { return SmallVector{programBuilder.x(target[0])}; }); - Value c1, c2; + Value c1; + Value c2; std::tie(q1, c1) = programBuilder.measure(q12[0]); std::tie(q2, c2) = programBuilder.measure(q12[1]); @@ -245,11 +253,13 @@ TEST_F(QCOMeasurementLiftingTest, auto r1 = referenceBuilder.allocQubit(); auto r2 = referenceBuilder.allocQubit(); - Value cr1, cr2; + Value cr1; + Value cr2; std::tie(r1, cr1) = referenceBuilder.measure(r1); std::tie(r2, cr2) = referenceBuilder.measure(r2); - SmallVector r12, r0Vec; + SmallVector r12; + SmallVector r0Vec; std::tie(r12, r0Vec) = referenceBuilder.ctrl({r1, r2}, {r0}, [&](const ValueRange target) { return SmallVector{referenceBuilder.x(target[0])}; @@ -280,7 +290,8 @@ TEST_F(QCOMeasurementLiftingTest, std::tie(q0, q1) = programBuilder.crx(std::numbers::pi / 2, q0, q1); - Value c0, c1; + Value c0; + Value c1; std::tie(q0, c0) = programBuilder.measure(q0); std::tie(q1, c1) = programBuilder.measure(q1); @@ -293,7 +304,8 @@ TEST_F(QCOMeasurementLiftingTest, auto r0 = referenceBuilder.allocQubit(); auto r1 = referenceBuilder.allocQubit(); - Value cr0, cr1; + Value cr0; + Value cr1; std::tie(r0, cr0) = referenceBuilder.measure(r0); std::tie(r0, r1) = referenceBuilder.crx(std::numbers::pi / 2, r0, r1); @@ -489,7 +501,8 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverDiagonalGateInControl) { std::tie(q0, q1) = programBuilder.cz(q0, q1); - Value c0, c1; + Value c0; + Value c1; std::tie(q0, c0) = programBuilder.measure(q0); std::tie(q1, c1) = programBuilder.measure(q1); @@ -502,7 +515,8 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverDiagonalGateInControl) { auto r0 = referenceBuilder.allocQubit(); auto r1 = referenceBuilder.allocQubit(); - Value cr0, cr1; + Value cr0; + Value cr1; std::tie(r0, cr0) = referenceBuilder.measure(r0); std::tie(r1, cr1) = referenceBuilder.measure(r1); From 096075c6fd441d804cb9dcabb94b594a1fe212a6 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 14 Jul 2026 06:05:05 +0200 Subject: [PATCH 18/25] fix(mlir): :recycle: address coderabbit issues --- mlir/include/mlir/Dialect/QCO/Transforms/Passes.td | 2 +- .../QCO/Transforms/Optimizations/MeasurementLifting.cpp | 5 ++--- .../Optimizations/test_qco_measurement_lifting.cpp | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index 9a95a0642e..eea4d2eea7 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -230,7 +230,7 @@ def MeasurementLifting : Pass<"measurement-lifting", "mlir::ModuleOp"> { ┌───┐┌──────┐ ┌───────┐┌───┐ ┤ X ├┤ Meas ├ => ┤ Meas* ├┤ X ├ └───┘└──────┘ └───────┘└───┘ - (Where Meas* is a measurement after which the outcome is classically negated) + (Where Meas* is a measurement after which the outcome is classically negated and X can be replaced by Y, which is also anti-diagonal) }]; } diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp index 41c8f02011..e018bd77f4 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp @@ -125,7 +125,7 @@ struct LiftMeasurementsAbovePhaseGatesPattern final /** * @brief This pattern is responsible for lifting measurements above any - * non-phase gates. + * anti-diagonal gates. */ struct LiftMeasurementsAboveInvertingGatesPattern final : mlir::OpRewritePattern { @@ -201,8 +201,7 @@ struct LiftMeasurementsAboveControlsPattern final }; /** - * @brief Pass raises Measurements above controlled and uncontrolled gates - * gates. + * @brief Pass raises Measurements above controlled and uncontrolled gates. */ struct MeasurementLifting final : impl::MeasurementLiftingBase { diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp index f7d40defe0..bf4fe3aa78 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -475,9 +475,9 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverXAndControlledGates) { Value cr0; std::tie(r0, cr0) = referenceBuilder.measure(r0); - std::tie(r0, r1) = referenceBuilder.cx(r0, r1); + std::tie(r0, r1) = referenceBuilder.cy(r0, r1); r0 = referenceBuilder.x(r0); - std::tie(r0, r1) = referenceBuilder.cx(r0, r1); + std::tie(r0, r1) = referenceBuilder.cy(r0, r1); referenceBuilder.sink(r0); referenceBuilder.sink(r1); From b1a24cdf334e0aed7ef9b4cefb960882b9141d50 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 14 Jul 2026 06:17:36 +0200 Subject: [PATCH 19/25] chore(mlir): :recycle: some further coderabbit suggestions --- .../mlir/Dialect/QCO/Transforms/Passes.td | 3 +- .../test_qco_measurement_lifting.cpp | 28 ++++++++----------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index eea4d2eea7..00d9ae535f 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -203,8 +203,7 @@ def HadamardLifting : Pass<"hadamard-lifting", "mlir::ModuleOp"> { def MeasurementLifting : Pass<"measurement-lifting", "mlir::ModuleOp"> { let dependentDialects = ["mlir::qco::QCODialect", - "::mlir::arith::ArithDialect", - ]; + "::mlir::arith::ArithDialect"]; let summary = "This pass attempts to move measurements as far up as " "possible, shifting them above gates that commute with them. " "This is done to enable qubit reuse and other optimizations."; diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp index bf4fe3aa78..00f19a9530 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -8,10 +8,6 @@ * Licensed under the MIT License */ -// -// Created by damian on 5/21/26. -// - #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" @@ -142,15 +138,15 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { SmallVector q12; SmallVector q0Vec; std::tie(q12, q0Vec) = - programBuilder.ctrl({q1, q2}, {q0}, [&](const ValueRange target) { + programBuilder.ctrl({q1, q2}, {q0}, [&](ValueRange target) { return SmallVector{programBuilder.x(target[0])}; }); - std::tie(q12, q0Vec) = programBuilder.ctrl( - {q12[1], q12[0]}, q0Vec, [&](const ValueRange target) { + std::tie(q12, q0Vec) = + programBuilder.ctrl({q12[1], q12[0]}, q0Vec, [&](ValueRange target) { return SmallVector{programBuilder.h(target[0])}; }); - std::tie(q12, q0Vec) = programBuilder.ctrl( - {q12[1], q12[0]}, q0Vec, [&](const ValueRange target) { + std::tie(q12, q0Vec) = + programBuilder.ctrl({q12[1], q12[0]}, q0Vec, [&](ValueRange target) { return SmallVector{programBuilder.x(target[0])}; }); @@ -184,15 +180,15 @@ TEST_F(QCOMeasurementLiftingTest, liftMeasurementOverOneOfMultipleControls) { SmallVector r12; SmallVector r0Vec; std::tie(r12, r0Vec) = - referenceBuilder.ctrl({r1, r2}, {r0}, [&](const ValueRange target) { + referenceBuilder.ctrl({r1, r2}, {r0}, [&](ValueRange target) { return SmallVector{referenceBuilder.x(target[0])}; }); - std::tie(r12, r0Vec) = referenceBuilder.ctrl( - {r12[1], r12[0]}, r0Vec, [&](const ValueRange target) { + std::tie(r12, r0Vec) = + referenceBuilder.ctrl({r12[1], r12[0]}, r0Vec, [&](ValueRange target) { return SmallVector{referenceBuilder.h(target[0])}; }); - std::tie(r12, r0Vec) = referenceBuilder.ctrl( - {r12[1], r12[0]}, r0Vec, [&](const ValueRange target) { + std::tie(r12, r0Vec) = + referenceBuilder.ctrl({r12[1], r12[0]}, r0Vec, [&](ValueRange target) { return SmallVector{referenceBuilder.x(target[0])}; }); @@ -233,7 +229,7 @@ TEST_F(QCOMeasurementLiftingTest, SmallVector q12; SmallVector q0Vec; std::tie(q12, q0Vec) = - programBuilder.ctrl({q1, q2}, {q0}, [&](const ValueRange target) { + programBuilder.ctrl({q1, q2}, {q0}, [&](ValueRange target) { return SmallVector{programBuilder.x(target[0])}; }); @@ -261,7 +257,7 @@ TEST_F(QCOMeasurementLiftingTest, SmallVector r12; SmallVector r0Vec; std::tie(r12, r0Vec) = - referenceBuilder.ctrl({r1, r2}, {r0}, [&](const ValueRange target) { + referenceBuilder.ctrl({r1, r2}, {r0}, [&](ValueRange target) { return SmallVector{referenceBuilder.x(target[0])}; }); From 6d4ce738d473195ae227232f2a6cd0aa2a4dd8f5 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Tue, 14 Jul 2026 06:55:51 +0200 Subject: [PATCH 20/25] docs(mlir): :memo: add to changelog --- CHANGELOG.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66fc7b4bdf..6a52c1e419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,12 +53,12 @@ releases may include breaking changes. [#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], [#1755], [#1762], [#1765], - [#1780], [#1781], [#1782], [#1787], [#1806], [#1807], [#1808], [#1823], - [#1824], [#1830], [#1869], [#1872]) ([**@burgholzer**], [**@denialhaag**], - [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], - [**@MatthiasReumann**], [**@simon1hofmann**]) + [#1626], [#1627], [#1635], [#1638], [#1673], [#1675], [#1700], [#1705], + [#1710], [#1717], [#1728], [#1730], [#1749], [#1751], [#1755], [#1762], + [#1765], [#1780], [#1781], [#1782], [#1787], [#1806], [#1807], [#1808], + [#1823], [#1824], [#1830], [#1869], [#1872]) ([**@burgholzer**], + [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], + [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**]) ### Changed @@ -689,7 +689,7 @@ changelogs._ [#1710]: https://github.com/munich-quantum-toolkit/core/pull/1710 [#1709]: https://github.com/munich-quantum-toolkit/core/pull/1709 [#1706]: https://github.com/munich-quantum-toolkit/core/pull/1706 -[#1702]: https://github.com/munich-quantum-toolkit/core/pull/1702 +[#1705]: https://github.com/munich-quantum-toolkit/core/pull/1705 [#1700]: https://github.com/munich-quantum-toolkit/core/pull/1700 [#1694]: https://github.com/munich-quantum-toolkit/core/pull/1694 [#1676]: https://github.com/munich-quantum-toolkit/core/pull/1676 From 91fe2890f853b8f2236248bf01e92f059c927e2f Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Fri, 17 Jul 2026 01:57:41 +0200 Subject: [PATCH 21/25] chore(mlir): :recycle: refactor `isDiagonal` check to use `getSoleBodyUnitary` --- .../Optimizations/MeasurementLifting.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp index e018bd77f4..a43da9a636 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp @@ -44,17 +44,12 @@ static bool isInverting(Operation* op) { return isa(op); } * @return True if the operation is a diagonal gate, false otherwise. */ static bool isDiagonal(Operation* op) { - if (auto c = dyn_cast(op)) { - if (c.getNumBodyUnitaries() != 1) { - return false; - } - return isDiagonal(c.getBodyUnitary(0)); + if (op == nullptr) { + return false; } - if (auto i = dyn_cast(op)) { - if (i.getNumBodyUnitaries() != 1) { - return false; - } - return isDiagonal(i.getBodyUnitary(0)); + if (isa(op)) { + return isDiagonal(utils::getSoleBodyUnitary( + *op->getRegion(0).getBlocks().begin())); } return isa(op); } From 317ebfd0579d1f343dde8f931af21b2e9fa071e3 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Fri, 17 Jul 2026 02:01:42 +0200 Subject: [PATCH 22/25] chore(mlir): :recycle: remove unneeded check --- .../QCO/Transforms/Optimizations/MeasurementLifting.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp index a43da9a636..dcc5f0791b 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp @@ -141,8 +141,7 @@ struct LiftMeasurementsAboveInvertingGatesPattern final return mlir::failure(); } - if (isInverting(predecessor) && - predecessorUnitary.getInputQubits().size() == 1) { + if (isInverting(predecessor)) { swapGateWithMeasurement(predecessorUnitary, op, rewriter); rewriter.setInsertionPointAfter(op); const mlir::Value trueConstant = rewriter.create( From d392185ebc8aed248ff02502a53bd3ae01492ec5 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Fri, 17 Jul 2026 02:03:32 +0200 Subject: [PATCH 23/25] chore(mlir): :recycle: combine lifting of measurements --- .../QCO/Transforms/Optimizations/MeasurementLifting.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp index dcc5f0791b..d0ff298d8e 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp @@ -208,10 +208,9 @@ struct MeasurementLifting final // Define the set of patterns to use. RewritePatternSet patterns(ctx); - patterns.add(patterns.getContext()); - patterns.add( - patterns.getContext()); - patterns.add(patterns.getContext()); + patterns.add(patterns.getContext()); // Apply patterns in an iterative and greedy manner. if (failed(applyPatternsGreedily(op, std::move(patterns)))) { From 92537157a5b727ead383e11dad75429098ca7894 Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Fri, 17 Jul 2026 02:34:53 +0200 Subject: [PATCH 24/25] docs(mlir): :memo: fix up changelog --- CHANGELOG.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fedd0d14f..0d0d690789 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,18 +50,19 @@ releases may include breaking changes. restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588], [#1600], [#1664], [#1709], [#1716], [#1748], [#1805], [#1870], [#1904]) ([**@MatthiasReumann**], [**@burgholzer**]) +- ✨ Add a pass for qubit reuse in quantum programs, as well as related + auxiliary passes and patterns ([#1705], [#1755]) ([**@DRovara**]) - ✨ 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], [#1705], - [#1710], [#1717], [#1728], [#1730], [#1749], [#1751], [#1755], [#1762], - [#1765], [#1780], [#1781], [#1782], [#1787], [#1806], [#1807], [#1815], - [#1808], [#1823], [#1824], [#1830], [#1869], [#1872], [#1914]) - ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], - [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], - [**@simon1hofmann**]) + [#1626], [#1627], [#1635], [#1638], [#1673], [#1675], [#1700], [#1710], + [#1717], [#1728], [#1730], [#1749], [#1751], [#1762], [#1765], [#1780], + [#1781], [#1782], [#1787], [#1806], [#1807], [#1815], [#1808], [#1823], + [#1824], [#1830], [#1869], [#1872], [#1914]) ([**@burgholzer**], + [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], + [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**]) ### Changed @@ -696,6 +697,7 @@ changelogs._ [#1709]: https://github.com/munich-quantum-toolkit/core/pull/1709 [#1706]: https://github.com/munich-quantum-toolkit/core/pull/1706 [#1705]: https://github.com/munich-quantum-toolkit/core/pull/1705 +[#1702]: https://github.com/munich-quantum-toolkit/core/pull/1702 [#1700]: https://github.com/munich-quantum-toolkit/core/pull/1700 [#1694]: https://github.com/munich-quantum-toolkit/core/pull/1694 [#1676]: https://github.com/munich-quantum-toolkit/core/pull/1676 From 10b0f89b36a8729772ad4f6a3fad703689ff3bda Mon Sep 17 00:00:00 2001 From: Damian Rovara Date: Fri, 17 Jul 2026 02:37:12 +0200 Subject: [PATCH 25/25] fix(mlir): :rotating_light: fix linter issues --- .../Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp index d0ff298d8e..b8d6fbb571 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp @@ -15,6 +15,7 @@ #include "mlir/Dialect/QCO/IR/QCOInterfaces.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" +#include "mlir/Dialect/Utils/Utils.h" #include #include