From 6026d16319961b6b7010842ca5d3edf9c658ed30 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Fri, 17 Apr 2026 18:43:05 +0200 Subject: [PATCH 01/29] Add SwapAbsorb pass --- .../mlir/Dialect/QCO/Transforms/Passes.td | 10 + mlir/include/mlir/Dialect/QCO/Utils/Drivers.h | 131 ++++++----- mlir/lib/Compiler/CMakeLists.txt | 1 + mlir/lib/Compiler/CompilerPipeline.cpp | 99 ++++++++- .../QCO/Transforms/Mapping/Mapping.cpp | 14 +- .../QCO/Transforms/Mapping/SwapAbsorb.cpp | 115 ++++++++++ mlir/lib/Dialect/QCO/Utils/Driver.cpp | 207 +++++++++++++++--- mlir/tools/mqt-cc/mqt-cc.cpp | 4 + 8 files changed, 488 insertions(+), 93 deletions(-) create mode 100644 mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index 5d37e25612..87c9988c49 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -72,4 +72,14 @@ def MappingPass : Pass<"place-and-route", "mlir::ModuleOp"> { "The number of inserted SWAPs">]; } +def SwapAbsorb : Pass<"absorb-swaps", "mlir::ModuleOp"> { + let dependentDialects = ["mlir::qco::QCODialect"]; + let summary = ""; + let options = []; + let statistics = [ + Statistic<"numSwaps", "num-inserted-swaps", + "The number of absorbed SWAPs"> + ]; +} + #endif // MLIR_DIALECT_QCO_TRANSFORMS_PASSES_TD diff --git a/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h b/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h index 1a901882dc..2b9aa72bda 100644 --- a/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h +++ b/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h @@ -11,38 +11,49 @@ #pragma once #include "mlir/Dialect/QCO/IR/QCODialect.h" -#include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" +#include "mlir/Dialect/QCO/Utils/WireIterator.h" +#include +#include #include +#include #include #include #include +#include #include -#include namespace mlir::qco { + +/** + * @brief Specifies the layering direction. + */ +enum class WalkDirection : bool { Forward, Backward }; + class Qubits { +public: /** - * @brief Specifies the qubit "location" (hardware or program). + * @brief Add qubit with automatically assigned index. */ - enum class QubitLocation : std::uint8_t { Hardware, Program }; + void add(TypedValue q); -public: /** - * @brief Add qubit with automatically assigned dynamic index. + * @brief Add qubit with index. */ - [[maybe_unused]] void add(TypedValue q); + void add(TypedValue q, std::size_t index); /** - * @brief Add qubit with static index. + * @brief Remap the qubit value from prev to next. */ - void add(TypedValue q, std::size_t hw); + void remap(TypedValue prev, TypedValue next, + const WalkDirection& direction); /** - * @brief Remap the qubit value from prev to next. + * @brief Remap all input qubits of the unitary to its outputs. */ - void remap(TypedValue prev, TypedValue next); + void remap(UnitaryOpInterface op, const WalkDirection& direction); /** * @brief Remove the qubit value. @@ -50,62 +61,80 @@ class Qubits { void remove(TypedValue q); /** - * @returns the qubit value assigned to a program index. + * @returns the qubit value assigned to a index. */ - [[maybe_unused]] TypedValue getProgramQubit(std::size_t index); + [[nodiscard]] TypedValue getQubit(std::size_t index) const; /** - * @returns the qubit value assigned to a hardware index. + * @returns the index assigned to the given qubit value. */ - TypedValue getHardwareQubit(std::size_t index); + [[nodiscard]] std::size_t getIndex(TypedValue q) const; private: - DenseMap> programToValue_; - DenseMap> hardwareToValue_; - DenseMap, std::pair> - valueToIndex_; + DenseMap> indexToValue_; + DenseMap, std::size_t> valueToIndex_; }; +using WalkUnitFn = function_ref; + /** * @brief Perform top-down non-recursive walk of all operations within a * region and apply callback function. - * @details The signature of the callback function is: + * @details + * The signature of the callback function is: * * (Operation*, Qubits& q) -> WalkResult * * where the Qubits object tracks the front of qubit SSA values. + * * @param region The targeted region. * @param fn The callback function. */ -template void walkUnit(Region& region, Fn&& fn) { - const auto ffn = std::forward(fn); - - Qubits qubits; - for (Operation& curr : region.getOps()) { - if (ffn(&curr, qubits).wasInterrupted()) { - break; - }; - - TypeSwitch(&curr) - .template Case( - [&](StaticOp op) { qubits.add(op.getQubit(), op.getIndex()); }) - .template Case([&](AllocOp op) { qubits.add(op.getResult()); }) - .template Case([&](UnitaryOpInterface op) { - for (const auto& [prevV, nextV] : - llvm::zip(op.getInputQubits(), op.getOutputQubits())) { - const auto prevQ = cast>(prevV); - const auto nextQ = cast>(nextV); - qubits.remap(prevQ, nextQ); - } - }) - .template Case([&](ResetOp op) { - qubits.remap(op.getQubitIn(), op.getQubitOut()); - }) - .template Case([&](MeasureOp op) { - qubits.remap(op.getQubitIn(), op.getQubitOut()); - }) - .template Case( - [&](SinkOp op) { qubits.remove(op.getQubit()); }); +void walkUnit(Region& region, WalkUnitFn fn); + +using ReleasedOps = SmallVector; + +using PendingWiresMap = + DenseMap>; + +struct IsReady { + bool operator()(PendingWiresMap::value_type& kv) const { + return kv.second.size() == kv.first.getNumQubits(); } -} -} // namespace mlir::qco +}; + +using ReadyRange = + decltype(make_filter_range(std::declval(), IsReady{})); + +using WalkCircuitGraphFn = + function_ref; + +/** + * @brief Walk the graph-like circuit IR of QCO dialect programs. + * @details + * Depending on the template parameter, the function collects the + * layers in forward or backward direction, respectively. Towards that end, + * the function traverses the def-use chain of each qubit until a multi-qubit + * gate (including barriers) is found. If a multi-qubit gate is visited twice, + * it is considered ready and inserted into the layer. This process is repeated + * until no more multi-qubit gates are found anymore. + * + * The signature of the callback function is: + * + * (FrontArrayRef, ReleasedOps&) -> WalkResult + * + * The operations inserted into the parameter "released" determine which + * multi-qubit gates are released in next iteration. + * + * @param wires A mutable array-ref of circuit wires (wire iterators). + * @param direction The traversal direction. + * @param fn The callback function. + * + * @returns + * failure(), if the callback returns WalkResult::interrupt() + * failure(), if the callback returns WalkResult::skipped() + * success(), otherwise. + */ +LogicalResult walkCircuitGraph(MutableArrayRef wires, + WalkDirection direction, WalkCircuitGraphFn fn); +} // namespace mlir::qco \ No newline at end of file diff --git a/mlir/lib/Compiler/CMakeLists.txt b/mlir/lib/Compiler/CMakeLists.txt index 66afb3515a..1dc45532fa 100644 --- a/mlir/lib/Compiler/CMakeLists.txt +++ b/mlir/lib/Compiler/CMakeLists.txt @@ -20,6 +20,7 @@ add_mlir_library( MLIRQCToQCO MLIRQCOToQC MLIRQCToQIR + MLIRQCOTransforms MQT::MLIRSupport) mqt_mlir_target_use_project_options(MQTCompilerPipeline) diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index 0bab524e53..0d867e5f49 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -13,6 +13,9 @@ #include "mlir/Conversion/QCOToQC/QCOToQC.h" #include "mlir/Conversion/QCToQCO/QCToQCO.h" #include "mlir/Conversion/QCToQIR/QCToQIR.h" +#include "mlir/Dialect/QCO/Transforms/Mapping/Architecture.h" +// #include "mlir/Dialect/QCO/Transforms/Mapping/Mapping.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Support/Passes.h" #include "mlir/Support/PrettyPrinting.h" @@ -22,6 +25,7 @@ #include #include +#include #include namespace mlir { @@ -135,10 +139,101 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, totalStages); } } + + const static qco::Architecture::CouplingSet COUPLING{ + {{0, 12}, {12, 0}, {0, 1}, {1, 0}, {1, 13}, {13, 1}, + {1, 2}, {2, 1}, {2, 14}, {14, 2}, {2, 3}, {3, 2}, + {3, 15}, {15, 3}, {3, 4}, {4, 3}, {4, 16}, {16, 4}, + {4, 5}, {5, 4}, {5, 17}, {17, 5}, {5, 6}, {6, 5}, + {6, 18}, {18, 6}, {6, 7}, {7, 6}, {7, 19}, {19, 7}, + {7, 8}, {8, 7}, {8, 20}, {20, 8}, {8, 9}, {9, 8}, + {9, 21}, {21, 9}, {9, 10}, {10, 9}, {10, 22}, {22, 10}, + {10, 11}, {11, 10}, {11, 23}, {23, 11}, {12, 24}, {24, 12}, + {12, 13}, {13, 12}, {13, 25}, {25, 13}, {13, 14}, {14, 13}, + {14, 26}, {26, 14}, {14, 15}, {15, 14}, {15, 27}, {27, 15}, + {15, 16}, {16, 15}, {16, 28}, {28, 16}, {16, 17}, {17, 16}, + {17, 29}, {29, 17}, {17, 18}, {18, 17}, {18, 30}, {30, 18}, + {18, 19}, {19, 18}, {19, 31}, {31, 19}, {19, 20}, {20, 19}, + {20, 32}, {32, 20}, {20, 21}, {21, 20}, {21, 33}, {33, 21}, + {21, 22}, {22, 21}, {22, 34}, {34, 22}, {22, 23}, {23, 22}, + {23, 35}, {35, 23}, {24, 36}, {36, 24}, {24, 25}, {25, 24}, + {25, 37}, {37, 25}, {25, 26}, {26, 25}, {26, 38}, {38, 26}, + {26, 27}, {27, 26}, {27, 39}, {39, 27}, {27, 28}, {28, 27}, + {28, 40}, {40, 28}, {28, 29}, {29, 28}, {29, 41}, {41, 29}, + {29, 30}, {30, 29}, {30, 42}, {42, 30}, {30, 31}, {31, 30}, + {31, 43}, {43, 31}, {31, 32}, {32, 31}, {32, 44}, {44, 32}, + {32, 33}, {33, 32}, {33, 45}, {45, 33}, {33, 34}, {34, 33}, + {34, 46}, {46, 34}, {34, 35}, {35, 34}, {35, 47}, {47, 35}, + {36, 48}, {48, 36}, {36, 37}, {37, 36}, {37, 49}, {49, 37}, + {37, 38}, {38, 37}, {38, 50}, {50, 38}, {38, 39}, {39, 38}, + {39, 51}, {51, 39}, {39, 40}, {40, 39}, {40, 52}, {52, 40}, + {40, 41}, {41, 40}, {41, 53}, {53, 41}, {41, 42}, {42, 41}, + {42, 54}, {54, 42}, {42, 43}, {43, 42}, {43, 55}, {55, 43}, + {43, 44}, {44, 43}, {44, 56}, {56, 44}, {44, 45}, {45, 44}, + {45, 57}, {57, 45}, {45, 46}, {46, 45}, {46, 58}, {58, 46}, + {46, 47}, {47, 46}, {47, 59}, {59, 47}, {48, 60}, {60, 48}, + {48, 49}, {49, 48}, {49, 61}, {61, 49}, {49, 50}, {50, 49}, + {50, 62}, {62, 50}, {50, 51}, {51, 50}, {51, 63}, {63, 51}, + {51, 52}, {52, 51}, {52, 64}, {64, 52}, {52, 53}, {53, 52}, + {53, 65}, {65, 53}, {53, 54}, {54, 53}, {54, 66}, {66, 54}, + {54, 55}, {55, 54}, {55, 67}, {67, 55}, {55, 56}, {56, 55}, + {56, 68}, {68, 56}, {56, 57}, {57, 56}, {57, 69}, {69, 57}, + {57, 58}, {58, 57}, {58, 70}, {70, 58}, {58, 59}, {59, 58}, + {59, 71}, {71, 59}, {60, 72}, {72, 60}, {60, 61}, {61, 60}, + {61, 73}, {73, 61}, {61, 62}, {62, 61}, {62, 74}, {74, 62}, + {62, 63}, {63, 62}, {63, 75}, {75, 63}, {63, 64}, {64, 63}, + {64, 76}, {76, 64}, {64, 65}, {65, 64}, {65, 77}, {77, 65}, + {65, 66}, {66, 65}, {66, 78}, {78, 66}, {66, 67}, {67, 66}, + {67, 79}, {79, 67}, {67, 68}, {68, 67}, {68, 80}, {80, 68}, + {68, 69}, {69, 68}, {69, 81}, {81, 69}, {69, 70}, {70, 69}, + {70, 82}, {82, 70}, {70, 71}, {71, 70}, {71, 83}, {83, 71}, + {72, 84}, {84, 72}, {72, 73}, {73, 72}, {73, 85}, {85, 73}, + {73, 74}, {74, 73}, {74, 86}, {86, 74}, {74, 75}, {75, 74}, + {75, 87}, {87, 75}, {75, 76}, {76, 75}, {76, 88}, {88, 76}, + {76, 77}, {77, 76}, {77, 89}, {89, 77}, {77, 78}, {78, 77}, + {78, 90}, {90, 78}, {78, 79}, {79, 78}, {79, 91}, {91, 79}, + {79, 80}, {80, 79}, {80, 92}, {92, 80}, {80, 81}, {81, 80}, + {81, 93}, {93, 81}, {81, 82}, {82, 81}, {82, 94}, {94, 82}, + {82, 83}, {83, 82}, {83, 95}, {95, 83}, {84, 96}, {96, 84}, + {84, 85}, {85, 84}, {85, 97}, {97, 85}, {85, 86}, {86, 85}, + {86, 98}, {98, 86}, {86, 87}, {87, 86}, {87, 99}, {99, 87}, + {87, 88}, {88, 87}, {88, 100}, {100, 88}, {88, 89}, {89, 88}, + {89, 101}, {101, 89}, {89, 90}, {90, 89}, {90, 102}, {102, 90}, + {90, 91}, {91, 90}, {91, 103}, {103, 91}, {91, 92}, {92, 91}, + {92, 104}, {104, 92}, {92, 93}, {93, 92}, {93, 105}, {105, 93}, + {93, 94}, {94, 93}, {94, 106}, {106, 94}, {94, 95}, {95, 94}, + {95, 107}, {107, 95}, {96, 108}, {108, 96}, {96, 97}, {97, 96}, + {97, 109}, {109, 97}, {97, 98}, {98, 97}, {98, 110}, {110, 98}, + {98, 99}, {99, 98}, {99, 111}, {111, 99}, {99, 100}, {100, 99}, + {100, 112}, {112, 100}, {100, 101}, {101, 100}, {101, 113}, {113, 101}, + {101, 102}, {102, 101}, {102, 114}, {114, 102}, {102, 103}, {103, 102}, + {103, 115}, {115, 103}, {103, 104}, {104, 103}, {104, 116}, {116, 104}, + {104, 105}, {105, 104}, {105, 117}, {117, 105}, {105, 106}, {106, 105}, + {106, 118}, {118, 106}, {106, 107}, {107, 106}, {107, 119}, {119, 107}, + {108, 109}, {109, 108}, {109, 110}, {110, 109}, {110, 111}, {111, 110}, + {111, 112}, {112, 111}, {112, 113}, {113, 112}, {113, 114}, {114, 113}, + {114, 115}, {115, 114}, {115, 116}, {116, 115}, {116, 117}, {117, 116}, + {117, 118}, {118, 117}, {118, 119}, {119, 118}}}; + auto arch = std::make_shared("Nighthawk", 120, COUPLING); + + // const static qco::Architecture::CouplingSet COUPLING{ + // {0, 3}, {3, 0}, {0, 1}, {1, 0}, {1, 4}, {4, 1}, {1, 2}, {2, 1}, + // {2, 5}, {5, 2}, {3, 6}, {6, 3}, {3, 4}, {4, 3}, {4, 7}, {7, 4}, + // {4, 5}, {5, 4}, {5, 8}, {8, 5}, {6, 7}, {7, 6}, {7, 8}, {8, 7}}; + // auto arch = std::make_shared("RigettiNovera", 9, COUPLING); + // Stage 5: Optimization passes // TODO: Add optimization passes - if (failed( - runStage([&](PassManager& pm) { populateQCOCleanupPipeline(pm); }))) { + if (failed(runStage([&](PassManager& pm) { + // const qco::MappingPassOptions options{.nlookahead = 15, + // .lambda = 0.5, + // .niterations = 1, + // .ntrials = 16, + // .seed = 42}; + // pm.addPass(qco::createMappingPass(std::move(arch), options)); + pm.addPass(qco::createSwapAbsorb()); + populateQCOCleanupPipeline(pm); + }))) { return failure(); } if (record != nullptr && config_.recordIntermediates) { diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp index 0682b1dd87..97e55fd835 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp @@ -810,7 +810,7 @@ struct MappingPass : impl::MappingPassBase { ArrayRef::iterator anchorIt = anchors.begin(); ArrayRef>::iterator swapIt = swaps.begin(); - walkUnit(funcBody, [&](Operation* op, Qubits& qubits) { + walkUnit(funcBody, [&](Operation* op, const Qubits& qubits) { // Early exit if we've processed all layers. if (anchorIt == anchors.end()) { return WalkResult::interrupt(); @@ -820,8 +820,8 @@ struct MappingPass : impl::MappingPassBase { rewriter.setInsertionPoint(*anchorIt); for (const auto& [hw0, hw1] : *swapIt) { - const auto in0 = qubits.getHardwareQubit(hw0); - const auto in1 = qubits.getHardwareQubit(hw1); + const auto in0 = qubits.getQubit(hw0); + const auto in1 = qubits.getQubit(hw1); auto insertedOp = SWAPOp::create(rewriter, op->getLoc(), in0, in1); @@ -830,14 +830,6 @@ struct MappingPass : impl::MappingPassBase { rewriter.replaceAllUsesExcept(in0, out1, insertedOp); rewriter.replaceAllUsesExcept(in1, out0, insertedOp); - - // Remove old qubit values. - qubits.remove(in0); - qubits.remove(in1); - - // Add permutated qubit value - hw index pair. - qubits.add(out0, hw1); - qubits.add(out1, hw0); } // Collect statistics. diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp new file mode 100644 index 0000000000..6a891a7a31 --- /dev/null +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp @@ -0,0 +1,115 @@ +#include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" +#include "mlir/Dialect/QCO/Utils/Drivers.h" +#include "mlir/Dialect/QCO/Utils/WireIterator.h" +#include "mlir/Dialect/QTensor/IR/QTensorOps.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mlir::qco { +#define GEN_PASS_DEF_SWAPABSORB +#include "mlir/Dialect/QCO/Transforms/Passes.h.inc" + +namespace { +struct SwapAbsorb : impl::SwapAbsorbBase { +public: + using SwapAbsorbBase::SwapAbsorbBase; + + void runOnOperation() override { + ModuleOp anchor = getOperation(); + IRRewriter rewriter(&getContext()); + insertStatics(anchor, rewriter); + + for (auto func : anchor.getOps()) { + SmallVector wires; + for (auto op : func.getOps()) { + wires.emplace_back(op.getQubit()); + } + + SmallVector readyToAbsorb; + readyToAbsorb.reserve((wires.size() + 1) / 2); + + std::ignore = + walkCircuitGraph(wires, WalkDirection::Forward, + [&](const ReadyRange& ready, ReleasedOps& released) { + for (const auto& [op, indices] : ready) { + if (isa(op)) { + readyToAbsorb.emplace_back(op); + } + released.emplace_back(op); + } + return WalkResult::interrupt(); + }); + + for (auto swapOp : readyToAbsorb) { + auto in0 = swapOp.getQubit0In(); + auto in1 = swapOp.getQubit1In(); + + auto out0 = swapOp.getQubit0Out(); + auto out1 = swapOp.getQubit1Out(); + + // TODO: What if single qubit gates are in front of the SWAP input? + // Tipp: Use the WireIterator. + StaticOp op0 = cast(in0.getDefiningOp()); + StaticOp op1 = cast(in1.getDefiningOp()); + + rewriter.replaceAllUsesWith(out0, op1.getQubit()); + rewriter.replaceAllUsesWith(out1, op0.getQubit()); + rewriter.eraseOp(swapOp); + } + } + } + +private: + static void insertStatics(ModuleOp anchor, IRRewriter& rewriter) { + for (auto func : anchor.getOps()) { + SmallVector worklist; + for (Operation& op : func.getOps()) { + worklist.emplace_back(&op); + } + + std::size_t n = llvm::range_size(func.getOps()) - 1; + for (Operation* op : llvm::reverse(worklist)) { + rewriter.setInsertionPoint(op); + + if (auto tensorDealloc = dyn_cast(op)) { + rewriter.eraseOp(tensorDealloc); + continue; + } + + if (auto tensorInsert = dyn_cast(op)) { + auto q = tensorInsert.getScalar(); + rewriter.create(rewriter.getUnknownLoc(), q); + rewriter.eraseOp(tensorInsert); + continue; + } + + if (auto tensorExtract = dyn_cast(op)) { + auto q = tensorExtract.getResult(); + auto staticOp = + rewriter.create(rewriter.getUnknownLoc(), n); + rewriter.replaceAllUsesWith(q, staticOp.getQubit()); + rewriter.eraseOp(tensorExtract); + n--; + continue; + } + + if (auto tensorAlloc = dyn_cast(op)) { + rewriter.eraseOp(tensorAlloc); + continue; + } + } + } + } +}; +} // namespace +} // namespace mlir::qco \ No newline at end of file diff --git a/mlir/lib/Dialect/QCO/Utils/Driver.cpp b/mlir/lib/Dialect/QCO/Utils/Driver.cpp index 7f96a17003..adae2df04b 100644 --- a/mlir/lib/Dialect/QCO/Utils/Driver.cpp +++ b/mlir/lib/Dialect/QCO/Utils/Driver.cpp @@ -9,62 +9,211 @@ */ #include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Utils/Drivers.h" +#include "mlir/Dialect/QCO/Utils/WireIterator.h" +#include "mlir/Dialect/QTensor/IR/QTensorOps.h" +#include +#include #include +#include +#include #include #include +#include +#include +#include +#include #include namespace mlir::qco { -void Qubits::add(TypedValue q) { - const auto index = programToValue_.size(); - programToValue_.try_emplace(index, q); - valueToIndex_.try_emplace(q, std::make_pair(QubitLocation::Program, index)); -} -void Qubits::add(TypedValue q, std::size_t hw) { - hardwareToValue_.try_emplace(hw, q); - valueToIndex_.try_emplace(q, std::make_pair(QubitLocation::Hardware, hw)); +using namespace mlir::qtensor; + +namespace { +enum class CircuitWalkResult : std::uint8_t { Advance, Hold, Fail }; +} // namespace + +void Qubits::add(TypedValue q) { add(q, indexToValue_.size()); } + +void Qubits::add(TypedValue q, std::size_t index) { + indexToValue_.try_emplace(index, q); + valueToIndex_.try_emplace(q, index); } -void Qubits::remap(TypedValue prev, TypedValue next) { +void Qubits::remap(TypedValue prev, TypedValue next, + const WalkDirection& direction) { + if (direction == WalkDirection::Backward) { + std::swap(prev, next); + } + assert(valueToIndex_.contains(prev)); - const auto& [location, index] = valueToIndex_.lookup(prev); + const auto index = valueToIndex_.lookup(prev); valueToIndex_.erase(prev); - valueToIndex_.try_emplace(next, std::make_pair(location, index)); + valueToIndex_.try_emplace(next, index); + indexToValue_[index] = next; +} - if (location == QubitLocation::Program) { - programToValue_[index] = next; - return; +void Qubits::remap(UnitaryOpInterface op, const WalkDirection& direction) { + for (const auto& [in, out] : + llvm::zip_equal(op.getInputQubits(), op.getOutputQubits())) { + remap(cast>(in), cast>(out), + direction); } - - hardwareToValue_[index] = next; } void Qubits::remove(TypedValue q) { assert(valueToIndex_.contains(q)); - const auto& [location, index] = valueToIndex_.lookup(q); + const auto index = valueToIndex_.lookup(q); valueToIndex_.erase(q); + indexToValue_.erase(index); +} - if (location == QubitLocation::Program) { - programToValue_.erase(index); - return; - } +TypedValue Qubits::getQubit(std::size_t index) const { + assert(indexToValue_.contains(index)); + return indexToValue_.lookup(index); +} - hardwareToValue_.erase(index); +std::size_t Qubits::getIndex(TypedValue q) const { + assert(valueToIndex_.contains(q)); + return valueToIndex_.lookup(q); } -TypedValue Qubits::getProgramQubit(std::size_t index) { - assert(programToValue_.contains(index)); - return programToValue_.lookup(index); +void walkUnit(Region& region, WalkUnitFn fn) { + Qubits qubits; + for (Operation& curr : region.getOps()) { + if (fn(&curr, qubits).wasInterrupted()) { + break; + }; + + TypeSwitch(&curr) + .Case( + [&](StaticOp op) { qubits.add(op.getQubit(), op.getIndex()); }) + .Case([&](AllocOp op) { qubits.add(op.getResult()); }) + .Case([&](ExtractOp op) { qubits.add(op.getResult()); }) + .Case([&](UnitaryOpInterface op) { + qubits.remap(op, WalkDirection::Forward); + }) + .Case([&](ResetOp op) { + qubits.remap(op.getQubitIn(), op.getQubitOut(), + WalkDirection::Forward); + }) + .Case([&](MeasureOp op) { + qubits.remap(op.getQubitIn(), op.getQubitOut(), + WalkDirection::Forward); + }) + .Case([&](InsertOp op) { qubits.remove(op.getScalar()); }) + .Case([&](SinkOp op) { qubits.remove(op.getQubit()); }); + } } -TypedValue Qubits::getHardwareQubit(std::size_t index) { - assert(hardwareToValue_.contains(index)); - return hardwareToValue_.lookup(index); +LogicalResult walkCircuitGraph(MutableArrayRef wires, + WalkDirection direction, WalkCircuitGraphFn fn) { + const auto step = direction == WalkDirection::Forward ? 1 : -1; + const auto proceed = [&](const WireIterator& it) { + if (direction == WalkDirection::Forward) { + return it != std::default_sentinel; + } + + if (it.operation() == nullptr) { + return false; + } + + return !isa(it.operation()); + }; + + ReleasedOps released; + + PendingWiresMap pending; + pending.reserve(wires.size()); + + SmallVector curr(wires.size()); + std::iota(curr.begin(), curr.end(), 0UL); + + SmallVector next; + next.reserve(wires.size()); + + while (!curr.empty()) { + for (std::size_t i : curr) { + auto& it = wires[i]; + while (proceed(it)) { + const auto res = + TypeSwitch(it.operation()) + .Case([&](UnitaryOpInterface op) { + // If there are fewer wires than the qubit requires inputs, + // it's impossible to release the operation. Hence, fail. + if (op.getNumQubits() > wires.size()) { + return CircuitWalkResult::Fail; + } + + if (op.getNumQubits() == 1) { + std::ranges::advance(it, step); + return CircuitWalkResult::Advance; + } + + // Insert the unitary to the pending map. + // The caller decides if this op should be released. + const auto [it, inserted] = pending.try_emplace(op); + auto& indices = it->second; + + if (inserted) { + indices.reserve(op.getNumQubits()); + } + + indices.emplace_back(i); + + return CircuitWalkResult::Hold; // Stop at multi-qubit gate. + }) + .Case([&](auto) { + std::ranges::advance(it, step); + return CircuitWalkResult::Advance; + }) + .Default([&](Operation* op) { + const auto name = op->getName().getStringRef(); + report_fatal_error("unknown op encountered: " + name); + return CircuitWalkResult::Fail; + }); + + if (res == CircuitWalkResult::Hold) { + break; + } + + if (res == CircuitWalkResult::Fail) { + return failure(); + } + } + } + + released.clear(); + const auto ready = make_filter_range(pending, IsReady{}); + const auto res = std::invoke(fn, ready, released); + if (res.wasInterrupted() || res.wasSkipped()) { + return failure(); + } + + for (UnitaryOpInterface op : released) { + const auto& mapIt = pending.find(op); + assert(mapIt != pending.end()); + + auto& indices = mapIt->second; + for (std::size_t i : mapIt->second) { + std::ranges::advance(wires[i], step); + next.emplace_back(i); + } + + pending.erase(mapIt); + } + + curr.swap(next); + next.clear(); + } + + return success(); } -} // namespace mlir::qco +} // namespace mlir::qco \ No newline at end of file diff --git a/mlir/tools/mqt-cc/mqt-cc.cpp b/mlir/tools/mqt-cc/mqt-cc.cpp index 76848b6594..dd52818b1d 100644 --- a/mlir/tools/mqt-cc/mqt-cc.cpp +++ b/mlir/tools/mqt-cc/mqt-cc.cpp @@ -13,6 +13,7 @@ #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/Translation/TranslateQuantumComputationToQC.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QTensor/IR/QTensorDialect.h" #include "qasm3/Exception.hpp" #include "qasm3/Importer.hpp" @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -143,6 +145,8 @@ int main(int argc, char** argv) { registry.insert(); registry.insert(); registry.insert(); + registry.insert(); + registry.insert(); MLIRContext context(registry); context.loadAllAvailableDialects(); From 4b315aa642755062d04f1abfd32a8163fbbf57c7 Mon Sep 17 00:00:00 2001 From: Johannes Moosburger Date: Thu, 23 Apr 2026 08:26:33 +0200 Subject: [PATCH 02/29] fix build for test_drivers.cpp --- mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp b/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp index b7e664e309..ce6b3805b3 100644 --- a/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp +++ b/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp @@ -70,12 +70,12 @@ TEST_F(DriversTest, FullWalk) { Value ex2 = nullptr; Value ex3 = nullptr; - qco::walkUnit(func.getBody(), [&](Operation* op, qco::Qubits& qubits) { + qco::walkUnit(func.getBody(), [&](Operation* op, const qco::Qubits& qubits) { if (op == q03.getDefiningOp()) { - ex0 = qubits.getProgramQubit(0); - ex1 = qubits.getProgramQubit(1); - ex2 = qubits.getProgramQubit(2); - ex3 = qubits.getProgramQubit(3); + ex0 = qubits.getQubit(0); + ex1 = qubits.getQubit(1); + ex2 = qubits.getQubit(2); + ex3 = qubits.getQubit(3); return WalkResult::interrupt(); } return WalkResult::advance(); From 096e881a4e56857bb34d91c2c0cffa4753e64e8e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 06:34:06 +0000 Subject: [PATCH 03/29] =?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 --- mlir/include/mlir/Dialect/QCO/Transforms/Passes.td | 6 ++---- mlir/include/mlir/Dialect/QCO/Utils/Drivers.h | 2 +- mlir/lib/Compiler/CompilerPipeline.cpp | 3 ++- .../Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp | 12 +++++++++++- mlir/lib/Dialect/QCO/Utils/Driver.cpp | 2 +- 5 files changed, 17 insertions(+), 8 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index 87c9988c49..02e8f42cb4 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -76,10 +76,8 @@ def SwapAbsorb : Pass<"absorb-swaps", "mlir::ModuleOp"> { let dependentDialects = ["mlir::qco::QCODialect"]; let summary = ""; let options = []; - let statistics = [ - Statistic<"numSwaps", "num-inserted-swaps", - "The number of absorbed SWAPs"> - ]; + let statistics = [Statistic<"numSwaps", "num-inserted-swaps", + "The number of absorbed SWAPs">]; } #endif // MLIR_DIALECT_QCO_TRANSFORMS_PASSES_TD diff --git a/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h b/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h index 2b9aa72bda..3b8f934ab7 100644 --- a/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h +++ b/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h @@ -137,4 +137,4 @@ using WalkCircuitGraphFn = */ LogicalResult walkCircuitGraph(MutableArrayRef wires, WalkDirection direction, WalkCircuitGraphFn fn); -} // namespace mlir::qco \ No newline at end of file +} // namespace mlir::qco diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index 0d867e5f49..fd04ea1a18 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -220,7 +220,8 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, // {0, 3}, {3, 0}, {0, 1}, {1, 0}, {1, 4}, {4, 1}, {1, 2}, {2, 1}, // {2, 5}, {5, 2}, {3, 6}, {6, 3}, {3, 4}, {4, 3}, {4, 7}, {7, 4}, // {4, 5}, {5, 4}, {5, 8}, {8, 5}, {6, 7}, {7, 6}, {7, 8}, {8, 7}}; - // auto arch = std::make_shared("RigettiNovera", 9, COUPLING); + // auto arch = std::make_shared("RigettiNovera", 9, + // COUPLING); // Stage 5: Optimization passes // TODO: Add optimization passes diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp index 6a891a7a31..b902395ede 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp @@ -1,3 +1,13 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOInterfaces.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" @@ -112,4 +122,4 @@ struct SwapAbsorb : impl::SwapAbsorbBase { } }; } // namespace -} // namespace mlir::qco \ No newline at end of file +} // namespace mlir::qco diff --git a/mlir/lib/Dialect/QCO/Utils/Driver.cpp b/mlir/lib/Dialect/QCO/Utils/Driver.cpp index adae2df04b..ac0dadbfc2 100644 --- a/mlir/lib/Dialect/QCO/Utils/Driver.cpp +++ b/mlir/lib/Dialect/QCO/Utils/Driver.cpp @@ -216,4 +216,4 @@ LogicalResult walkCircuitGraph(MutableArrayRef wires, return success(); } -} // namespace mlir::qco \ No newline at end of file +} // namespace mlir::qco From eddce92805544a9bfcd64e6c9139e9b1822dfdbc Mon Sep 17 00:00:00 2001 From: Johannes Moosburger Date: Sat, 25 Apr 2026 12:57:36 +0200 Subject: [PATCH 04/29] revert CompilerPipeline --- mlir/lib/Compiler/CompilerPipeline.cpp | 100 +------------------------ 1 file changed, 3 insertions(+), 97 deletions(-) diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index fd04ea1a18..0f67e5ae10 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -13,9 +13,6 @@ #include "mlir/Conversion/QCOToQC/QCOToQC.h" #include "mlir/Conversion/QCToQCO/QCToQCO.h" #include "mlir/Conversion/QCToQIR/QCToQIR.h" -#include "mlir/Dialect/QCO/Transforms/Mapping/Architecture.h" -// #include "mlir/Dialect/QCO/Transforms/Mapping/Mapping.h" -#include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Support/Passes.h" #include "mlir/Support/PrettyPrinting.h" @@ -25,7 +22,6 @@ #include #include -#include #include namespace mlir { @@ -140,101 +136,11 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, } } - const static qco::Architecture::CouplingSet COUPLING{ - {{0, 12}, {12, 0}, {0, 1}, {1, 0}, {1, 13}, {13, 1}, - {1, 2}, {2, 1}, {2, 14}, {14, 2}, {2, 3}, {3, 2}, - {3, 15}, {15, 3}, {3, 4}, {4, 3}, {4, 16}, {16, 4}, - {4, 5}, {5, 4}, {5, 17}, {17, 5}, {5, 6}, {6, 5}, - {6, 18}, {18, 6}, {6, 7}, {7, 6}, {7, 19}, {19, 7}, - {7, 8}, {8, 7}, {8, 20}, {20, 8}, {8, 9}, {9, 8}, - {9, 21}, {21, 9}, {9, 10}, {10, 9}, {10, 22}, {22, 10}, - {10, 11}, {11, 10}, {11, 23}, {23, 11}, {12, 24}, {24, 12}, - {12, 13}, {13, 12}, {13, 25}, {25, 13}, {13, 14}, {14, 13}, - {14, 26}, {26, 14}, {14, 15}, {15, 14}, {15, 27}, {27, 15}, - {15, 16}, {16, 15}, {16, 28}, {28, 16}, {16, 17}, {17, 16}, - {17, 29}, {29, 17}, {17, 18}, {18, 17}, {18, 30}, {30, 18}, - {18, 19}, {19, 18}, {19, 31}, {31, 19}, {19, 20}, {20, 19}, - {20, 32}, {32, 20}, {20, 21}, {21, 20}, {21, 33}, {33, 21}, - {21, 22}, {22, 21}, {22, 34}, {34, 22}, {22, 23}, {23, 22}, - {23, 35}, {35, 23}, {24, 36}, {36, 24}, {24, 25}, {25, 24}, - {25, 37}, {37, 25}, {25, 26}, {26, 25}, {26, 38}, {38, 26}, - {26, 27}, {27, 26}, {27, 39}, {39, 27}, {27, 28}, {28, 27}, - {28, 40}, {40, 28}, {28, 29}, {29, 28}, {29, 41}, {41, 29}, - {29, 30}, {30, 29}, {30, 42}, {42, 30}, {30, 31}, {31, 30}, - {31, 43}, {43, 31}, {31, 32}, {32, 31}, {32, 44}, {44, 32}, - {32, 33}, {33, 32}, {33, 45}, {45, 33}, {33, 34}, {34, 33}, - {34, 46}, {46, 34}, {34, 35}, {35, 34}, {35, 47}, {47, 35}, - {36, 48}, {48, 36}, {36, 37}, {37, 36}, {37, 49}, {49, 37}, - {37, 38}, {38, 37}, {38, 50}, {50, 38}, {38, 39}, {39, 38}, - {39, 51}, {51, 39}, {39, 40}, {40, 39}, {40, 52}, {52, 40}, - {40, 41}, {41, 40}, {41, 53}, {53, 41}, {41, 42}, {42, 41}, - {42, 54}, {54, 42}, {42, 43}, {43, 42}, {43, 55}, {55, 43}, - {43, 44}, {44, 43}, {44, 56}, {56, 44}, {44, 45}, {45, 44}, - {45, 57}, {57, 45}, {45, 46}, {46, 45}, {46, 58}, {58, 46}, - {46, 47}, {47, 46}, {47, 59}, {59, 47}, {48, 60}, {60, 48}, - {48, 49}, {49, 48}, {49, 61}, {61, 49}, {49, 50}, {50, 49}, - {50, 62}, {62, 50}, {50, 51}, {51, 50}, {51, 63}, {63, 51}, - {51, 52}, {52, 51}, {52, 64}, {64, 52}, {52, 53}, {53, 52}, - {53, 65}, {65, 53}, {53, 54}, {54, 53}, {54, 66}, {66, 54}, - {54, 55}, {55, 54}, {55, 67}, {67, 55}, {55, 56}, {56, 55}, - {56, 68}, {68, 56}, {56, 57}, {57, 56}, {57, 69}, {69, 57}, - {57, 58}, {58, 57}, {58, 70}, {70, 58}, {58, 59}, {59, 58}, - {59, 71}, {71, 59}, {60, 72}, {72, 60}, {60, 61}, {61, 60}, - {61, 73}, {73, 61}, {61, 62}, {62, 61}, {62, 74}, {74, 62}, - {62, 63}, {63, 62}, {63, 75}, {75, 63}, {63, 64}, {64, 63}, - {64, 76}, {76, 64}, {64, 65}, {65, 64}, {65, 77}, {77, 65}, - {65, 66}, {66, 65}, {66, 78}, {78, 66}, {66, 67}, {67, 66}, - {67, 79}, {79, 67}, {67, 68}, {68, 67}, {68, 80}, {80, 68}, - {68, 69}, {69, 68}, {69, 81}, {81, 69}, {69, 70}, {70, 69}, - {70, 82}, {82, 70}, {70, 71}, {71, 70}, {71, 83}, {83, 71}, - {72, 84}, {84, 72}, {72, 73}, {73, 72}, {73, 85}, {85, 73}, - {73, 74}, {74, 73}, {74, 86}, {86, 74}, {74, 75}, {75, 74}, - {75, 87}, {87, 75}, {75, 76}, {76, 75}, {76, 88}, {88, 76}, - {76, 77}, {77, 76}, {77, 89}, {89, 77}, {77, 78}, {78, 77}, - {78, 90}, {90, 78}, {78, 79}, {79, 78}, {79, 91}, {91, 79}, - {79, 80}, {80, 79}, {80, 92}, {92, 80}, {80, 81}, {81, 80}, - {81, 93}, {93, 81}, {81, 82}, {82, 81}, {82, 94}, {94, 82}, - {82, 83}, {83, 82}, {83, 95}, {95, 83}, {84, 96}, {96, 84}, - {84, 85}, {85, 84}, {85, 97}, {97, 85}, {85, 86}, {86, 85}, - {86, 98}, {98, 86}, {86, 87}, {87, 86}, {87, 99}, {99, 87}, - {87, 88}, {88, 87}, {88, 100}, {100, 88}, {88, 89}, {89, 88}, - {89, 101}, {101, 89}, {89, 90}, {90, 89}, {90, 102}, {102, 90}, - {90, 91}, {91, 90}, {91, 103}, {103, 91}, {91, 92}, {92, 91}, - {92, 104}, {104, 92}, {92, 93}, {93, 92}, {93, 105}, {105, 93}, - {93, 94}, {94, 93}, {94, 106}, {106, 94}, {94, 95}, {95, 94}, - {95, 107}, {107, 95}, {96, 108}, {108, 96}, {96, 97}, {97, 96}, - {97, 109}, {109, 97}, {97, 98}, {98, 97}, {98, 110}, {110, 98}, - {98, 99}, {99, 98}, {99, 111}, {111, 99}, {99, 100}, {100, 99}, - {100, 112}, {112, 100}, {100, 101}, {101, 100}, {101, 113}, {113, 101}, - {101, 102}, {102, 101}, {102, 114}, {114, 102}, {102, 103}, {103, 102}, - {103, 115}, {115, 103}, {103, 104}, {104, 103}, {104, 116}, {116, 104}, - {104, 105}, {105, 104}, {105, 117}, {117, 105}, {105, 106}, {106, 105}, - {106, 118}, {118, 106}, {106, 107}, {107, 106}, {107, 119}, {119, 107}, - {108, 109}, {109, 108}, {109, 110}, {110, 109}, {110, 111}, {111, 110}, - {111, 112}, {112, 111}, {112, 113}, {113, 112}, {113, 114}, {114, 113}, - {114, 115}, {115, 114}, {115, 116}, {116, 115}, {116, 117}, {117, 116}, - {117, 118}, {118, 117}, {118, 119}, {119, 118}}}; - auto arch = std::make_shared("Nighthawk", 120, COUPLING); - - // const static qco::Architecture::CouplingSet COUPLING{ - // {0, 3}, {3, 0}, {0, 1}, {1, 0}, {1, 4}, {4, 1}, {1, 2}, {2, 1}, - // {2, 5}, {5, 2}, {3, 6}, {6, 3}, {3, 4}, {4, 3}, {4, 7}, {7, 4}, - // {4, 5}, {5, 4}, {5, 8}, {8, 5}, {6, 7}, {7, 6}, {7, 8}, {8, 7}}; - // auto arch = std::make_shared("RigettiNovera", 9, - // COUPLING); - + // auto arch = std::make_shared("RigettiNovera", 9, COUPLING); // Stage 5: Optimization passes // TODO: Add optimization passes - if (failed(runStage([&](PassManager& pm) { - // const qco::MappingPassOptions options{.nlookahead = 15, - // .lambda = 0.5, - // .niterations = 1, - // .ntrials = 16, - // .seed = 42}; - // pm.addPass(qco::createMappingPass(std::move(arch), options)); - pm.addPass(qco::createSwapAbsorb()); - populateQCOCleanupPipeline(pm); - }))) { + if (failed( + runStage([&](PassManager& pm) { populateQCOCleanupPipeline(pm); }))) { return failure(); } if (record != nullptr && config_.recordIntermediates) { From cc11a85de66cdd911903618f6d82bdef462eaa6e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 02:51:26 +0000 Subject: [PATCH 05/29] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=A9=B9=20Update?= =?UTF-8?q?=20patch=20updates=20(#1643)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | Pending | |---|---|---|---|---| | [astral-sh/ruff-pre-commit](https://redirect.github.com/astral-sh/ruff-pre-commit) | repository | patch | `v0.15.9` → `v0.15.10` | `v0.15.11` | | [astral-sh/uv-pre-commit](https://redirect.github.com/astral-sh/uv-pre-commit) | repository | patch | `0.11.3` → `0.11.6` | `0.11.7` | | [henryiii/validate-pyproject-schema-store](https://redirect.github.com/henryiii/validate-pyproject-schema-store) | repository | patch | `2026.04.03` → `2026.04.10` | `2026.04.17` (+2) | | [pre-commit/mirrors-clang-format](https://redirect.github.com/pre-commit/mirrors-clang-format) | repository | patch | `v22.1.2` → `v22.1.3` | | | [rbubley/mirrors-prettier](https://redirect.github.com/rbubley/mirrors-prettier) | repository | patch | `v3.8.1` → `v3.8.2` | `v3.8.3` | Note: The `pre-commit` manager in Renovate is not supported by the `pre-commit` maintainers or community. Please do not report any problems there, instead [create a Discussion in the Renovate repository](https://redirect.github.com/renovatebot/renovate/discussions/new) if you have any questions. --- ### Release Notes
astral-sh/ruff-pre-commit (astral-sh/ruff-pre-commit) ### [`v0.15.10`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.15.10) [Compare Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.15.9...v0.15.10) See:
astral-sh/uv-pre-commit (astral-sh/uv-pre-commit) ### [`v0.11.6`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.6) [Compare Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.5...0.11.6) See: ### [`v0.11.5`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.5) [Compare Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.4...0.11.5) See: ### [`v0.11.4`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.4) [Compare Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.3...0.11.4) See:
henryiii/validate-pyproject-schema-store (henryiii/validate-pyproject-schema-store) ### [`v2026.04.10`](https://redirect.github.com/henryiii/validate-pyproject-schema-store/compare/2026.04.03...2026.04.10) [Compare Source](https://redirect.github.com/henryiii/validate-pyproject-schema-store/compare/2026.04.03...2026.04.10)
pre-commit/mirrors-clang-format (pre-commit/mirrors-clang-format) ### [`v22.1.3`](https://redirect.github.com/pre-commit/mirrors-clang-format/compare/v22.1.2...v22.1.3) [Compare Source](https://redirect.github.com/pre-commit/mirrors-clang-format/compare/v22.1.2...v22.1.3)
rbubley/mirrors-prettier (rbubley/mirrors-prettier) ### [`v3.8.2`](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.8.1...v3.8.2) [Compare Source](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.8.1...v3.8.2)
--- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "every weekend" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9c0176901e..1ad31544aa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,7 +30,7 @@ repos: ## Check the pyproject.toml file - repo: https://github.com/henryiii/validate-pyproject-schema-store - rev: 2026.04.03 + rev: 2026.04.10 hooks: - id: validate-pyproject priority: 0 @@ -78,7 +78,7 @@ repos: ## Ensure uv lock file is up-to-date - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.11.3 + rev: 0.11.6 hooks: - id: uv-lock priority: 0 @@ -106,7 +106,7 @@ repos: ## Format C++ files with clang-format - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v22.1.2 + rev: v22.1.3 hooks: - id: clang-format types_or: [c++, c, cuda] @@ -129,7 +129,7 @@ repos: ## Format configuration files with prettier - repo: https://github.com/rbubley/mirrors-prettier - rev: v3.8.1 + rev: v3.8.2 hooks: - id: prettier types_or: [yaml, markdown, html, css, scss, javascript, json, json5] @@ -137,7 +137,7 @@ repos: ## Python linting using ruff - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.9 + rev: v0.15.10 hooks: - id: ruff-format priority: 1 From 245e062d120d78a731ffcf552deb080dc51e8bda Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 03:57:16 +0000 Subject: [PATCH 06/29] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=A9=B9=20Update?= =?UTF-8?q?=20minor=20stable=20updates=20(#1644)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | Pending | |---|---|---|---|---| | [actions/create-github-app-token](https://redirect.github.com/actions/create-github-app-token) | action | minor | `v3.0.0` → `v3.1.0` | `v3.1.1` | | [pypa/gh-action-pypi-publish](https://redirect.github.com/pypa/gh-action-pypi-publish) | action | minor | `v1.13.0` → `v1.14.0` | | | [release-drafter/release-drafter](https://redirect.github.com/release-drafter/release-drafter) | action | minor | `v7.1.1` → `v7.2.0` | | --- ### Release Notes
actions/create-github-app-token (actions/create-github-app-token) ### [`v3.1.0`](https://redirect.github.com/actions/create-github-app-token/releases/tag/v3.1.0) [Compare Source](https://redirect.github.com/actions/create-github-app-token/compare/v3.0.0...v3.1.0) ##### Bug Fixes - **deps:** bump p-retry from 7.1.1 to 8.0.0 ([#​357](https://redirect.github.com/actions/create-github-app-token/issues/357)) ([3bbe07d](https://redirect.github.com/actions/create-github-app-token/commit/3bbe07d928e2d6c30bf3e37c6b89edbc4045facf)) ##### Features - add `client-id` input and deprecate `app-id` ([#​353](https://redirect.github.com/actions/create-github-app-token/issues/353)) ([e6bd4e6](https://redirect.github.com/actions/create-github-app-token/commit/e6bd4e6970172bed9fe138b2eaf4cbffa4cca8f9)) - update permission inputs ([#​358](https://redirect.github.com/actions/create-github-app-token/issues/358)) ([076e948](https://redirect.github.com/actions/create-github-app-token/commit/076e9480ca6e9633bff412d05eff0fc2f1e7d2be))
pypa/gh-action-pypi-publish (pypa/gh-action-pypi-publish) ### [`v1.14.0`](https://redirect.github.com/pypa/gh-action-pypi-publish/releases/tag/v1.14.0) [Compare Source](https://redirect.github.com/pypa/gh-action-pypi-publish/compare/v1.13.0...v1.14.0)

Audit your supply chain regularly!

#### ✨ What's Changed The main change in this release is that `verbose` and `print-hash` inputs are now on by default. This was contributed by [@​whitequark](https://redirect.github.com/whitequark)[💰](https://redirect.github.com/sponsors/whitequark) in [#​397](https://redirect.github.com/pypa/gh-action-pypi-publish/issues/397). #### 📝 Docs [@​woodruffw](https://redirect.github.com/woodruffw)[💰](https://redirect.github.com/sponsors/woodruffw) updated the mentions of PEP 740 to stop implying that it might be experimental (it hasn't been for quite a while!) in [#​388](https://redirect.github.com/pypa/gh-action-pypi-publish/issues/388) and [@​him2him2](https://redirect.github.com/him2him2)[💰](https://redirect.github.com/sponsors/him2him2) brushed up some grammar in the README and SECURITY docs via [#​395](https://redirect.github.com/pypa/gh-action-pypi-publish/issues/395). #### 🛠️ Internal Updates [@​woodruffw](https://redirect.github.com/woodruffw)[💰](https://redirect.github.com/sponsors/woodruffw) bumped `sigstore` and `pypi-attestations` in the lock file ([#​391](https://redirect.github.com/pypa/gh-action-pypi-publish/issues/391)) and [@​webknjaz](https://redirect.github.com/webknjaz)[💰][GH Sponsors URL] added infra for using type annotations in the project ([#​381](https://redirect.github.com/pypa/gh-action-pypi-publish/issues/381)). #### 💪 New Contributors - [@​him2him2](https://redirect.github.com/him2him2) made their first contribution in [#​395](https://redirect.github.com/pypa/gh-action-pypi-publish/issues/395) - [@​whitequark](https://redirect.github.com/whitequark) made their first contribution in [#​397](https://redirect.github.com/pypa/gh-action-pypi-publish/issues/397) **🪞 Full Diff**: **🧔‍♂️ Release Manager:** [@​webknjaz](https://redirect.github.com/sponsors/webknjaz) [🇺🇦](https://stand-with-ukraine.pp.ua) **🙏 Special Thanks** to [@​facutuesca](https://redirect.github.com/facutuesca)[💰](https://redirect.github.com/sponsors/facutuesca) and [@​woodruffw](https://redirect.github.com/woodruffw)[💰](https://redirect.github.com/sponsors/woodruffw) for helping maintain this project when [I][GH Sponsors URL] can't! **💬 Discuss** [on Bluesky 🦋](https://bsky.app/profile/webknjaz.me/post/3mivwsz3qzk2e), [on Mastodon 🐘](https://mastodon.social/@​webknjaz/116363779997051422) and [on GitHub][release discussion]. [![GH Sponsors badge]][GH Sponsors URL] [GH Sponsors badge]: https://img.shields.io/badge/%40webknjaz-transparent?logo=githubsponsors&logoColor=%23EA4AAA&label=Sponsor&color=2a313c [GH Sponsors URL]: https://redirect.github.com/sponsors/webknjaz [release discussion]: https://redirect.github.com/pypa/gh-action-pypi-publish/discussions/404
release-drafter/release-drafter (release-drafter/release-drafter) ### [`v7.2.0`](https://redirect.github.com/release-drafter/release-drafter/releases/tag/v7.2.0) [Compare Source](https://redirect.github.com/release-drafter/release-drafter/compare/v7.1.1...v7.2.0) ### What's Changed #### New - feat: allow always collapsing a category ([#​1444](https://redirect.github.com/release-drafter/release-drafter/issues/1444)) [@​mhanberg](https://redirect.github.com/mhanberg) #### Bug Fixes - fix: improve advanced substitutions in replacers ([#​1555](https://redirect.github.com/release-drafter/release-drafter/issues/1555)) [@​jetersen](https://redirect.github.com/jetersen) - fix: support repo-only \_extends and prevent .github/ path doubling ([#​1577](https://redirect.github.com/release-drafter/release-drafter/issues/1577)) [@​jetersen](https://redirect.github.com/jetersen) #### Maintenance - chore(deps): update dependency typescript to 6.0.2 ([#​1587](https://redirect.github.com/release-drafter/release-drafter/issues/1587)) @​[renovate\[bot\]](https://redirect.github.com/apps/renovate) - chore(deps): update vitest to 4.1.4 ([#​1585](https://redirect.github.com/release-drafter/release-drafter/issues/1585)) @​[renovate\[bot\]](https://redirect.github.com/apps/renovate) - ci(deps): update peter-evans/create-pull-request action to v8 ([#​1588](https://redirect.github.com/release-drafter/release-drafter/issues/1588)) @​[renovate\[bot\]](https://redirect.github.com/apps/renovate) - chore(deps): update dependency vite to 8.0.5 ([#​1579](https://redirect.github.com/release-drafter/release-drafter/issues/1579)) @​[renovate\[bot\]](https://redirect.github.com/apps/renovate) - chore(deps): update dependency nock to 14.0.12 ([#​1583](https://redirect.github.com/release-drafter/release-drafter/issues/1583)) @​[renovate\[bot\]](https://redirect.github.com/apps/renovate) - chore(deps): update dependency [@​types/node](https://redirect.github.com/types/node) to 24.12.2 ([#​1582](https://redirect.github.com/release-drafter/release-drafter/issues/1582)) @​[renovate\[bot\]](https://redirect.github.com/apps/renovate) - chore(deps): update dependency [@​biomejs/biome](https://redirect.github.com/biomejs/biome) to 2.4.10 ([#​1581](https://redirect.github.com/release-drafter/release-drafter/issues/1581)) @​[renovate\[bot\]](https://redirect.github.com/apps/renovate) - chore: move codegen to monthly scheduled workflow ([#​1578](https://redirect.github.com/release-drafter/release-drafter/issues/1578)) [@​jetersen](https://redirect.github.com/jetersen) - chore: replace vite-tsconfig-paths plugin with native resolve.tsconfigPaths ([#​1571](https://redirect.github.com/release-drafter/release-drafter/issues/1571)) [@​jetersen](https://redirect.github.com/jetersen) #### Documentation - docs: fix autolabeler example tag ([#​1568](https://redirect.github.com/release-drafter/release-drafter/issues/1568)) [@​cchanche](https://redirect.github.com/cchanche) #### Dependency Updates - build(deps): bump lodash and [@​graphql-codegen/plugin-helpers](https://redirect.github.com/graphql-codegen/plugin-helpers) ([#​1589](https://redirect.github.com/release-drafter/release-drafter/issues/1589)) @​[dependabot\[bot\]](https://redirect.github.com/apps/dependabot) - fix(deps): update dependency [@​actions/github](https://redirect.github.com/actions/github) to 9.1.0 ([#​1586](https://redirect.github.com/release-drafter/release-drafter/issues/1586)) @​[renovate\[bot\]](https://redirect.github.com/apps/renovate) - chore(deps): update dependency yaml to 2.8.3 ([#​1580](https://redirect.github.com/release-drafter/release-drafter/issues/1580)) @​[renovate\[bot\]](https://redirect.github.com/apps/renovate) - chore(deps): update node.js to v24.14.1 ([#​1584](https://redirect.github.com/release-drafter/release-drafter/issues/1584)) @​[renovate\[bot\]](https://redirect.github.com/apps/renovate) - chore(deps): update dependency [@​biomejs/biome](https://redirect.github.com/biomejs/biome) to 2.4.10 ([#​1581](https://redirect.github.com/release-drafter/release-drafter/issues/1581)) @​[renovate\[bot\]](https://redirect.github.com/apps/renovate) **Full Changelog**:
--- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "every weekend" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/cd.yml | 2 +- .github/workflows/release-drafter.yml | 2 +- .github/workflows/templating.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index f62963f382..ee8c4cb710 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -53,4 +53,4 @@ jobs: uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 with: subject-path: dist/* - - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 0417a60d54..9b42c2b327 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -15,4 +15,4 @@ jobs: contents: write runs-on: ubuntu-slim steps: - - uses: release-drafter/release-drafter@139054aeaa9adc52ab36ddf67437541f039b88e2 # v7.1.1 + - uses: release-drafter/release-drafter@5de93583980a40bd78603b6dfdcda5b4df377b32 # v7.2.0 diff --git a/.github/workflows/templating.yml b/.github/workflows/templating.yml index 27f294d062..c6a66aba6d 100644 --- a/.github/workflows/templating.yml +++ b/.github/workflows/templating.yml @@ -22,7 +22,7 @@ jobs: JSON_CONTENT=$(cat .github/workflow_inputs/release_drafter_categories.json | jq -c .) echo "release_drafter_categories=$JSON_CONTENT" >> $GITHUB_OUTPUT - id: create-token - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 + uses: actions/create-github-app-token@7bd03711494f032dfa3be3558f7dc8787b0be333 # v3.1.0 with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} From 2d04f320812d2af2c745076b3a8385860fe0060c Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sat, 18 Apr 2026 10:41:18 +0200 Subject: [PATCH 07/29] =?UTF-8?q?=F0=9F=94=A7=20Maintenance=20round=20(#16?= =?UTF-8?q?45)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .license-tools-config.json | 3 ++- .pre-commit-config.yaml | 12 ++---------- LICENSE.md => LICENSE | 0 docs/qdmi/qdmi_backend.md | 8 ++------ pyproject.toml | 24 ++++++++++++------------ 5 files changed, 18 insertions(+), 29 deletions(-) rename LICENSE.md => LICENSE (100%) diff --git a/.license-tools-config.json b/.license-tools-config.json index cccb855b53..7012f899ba 100644 --- a/.license-tools-config.json +++ b/.license-tools-config.json @@ -34,6 +34,7 @@ ".*\\.tex", "uv\\.lock", "py\\.typed", - ".*build.*" + ".*build.*", + "LICENSE" ] } diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1ad31544aa..a541eac67d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -117,7 +117,7 @@ repos: files: \.td$ priority: 1 - ## Format CMakeLists.txt files + ## Format the CMakeLists.txt files - repo: https://github.com/cheshirekow/cmake-format-precommit rev: v0.6.13 hooks: @@ -140,6 +140,7 @@ repos: rev: v0.15.10 hooks: - id: ruff-format + types_or: [python, pyi, jupyter, markdown] priority: 1 - id: ruff-check require_serial: true @@ -147,15 +148,6 @@ repos: # Priority 2+: Final checks and fixers - ## Also run Black on examples in the documentation (needs to run after ruff format) - - repo: https://github.com/adamchainz/blacken-docs - rev: 1.20.0 - hooks: - - id: blacken-docs - language: python - additional_dependencies: [black==26.*] - priority: 2 - ## Static type checking using ty (needs to run after lockfile update/ruff format, and ruff lint) - repo: local hooks: diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE diff --git a/docs/qdmi/qdmi_backend.md b/docs/qdmi/qdmi_backend.md index c50110ae89..3b89fac043 100644 --- a/docs/qdmi/qdmi_backend.md +++ b/docs/qdmi/qdmi_backend.md @@ -93,9 +93,7 @@ print(f"Qubits: {backend.target.num_qubits}") ```python # Filter backends by name substring -filtered_qdmi = provider.backends( - name="QDMI" -) # Matches all backends with "QDMI" in name +filtered_qdmi = provider.backends(name="QDMI") # Matches all backends with "QDMI" in name filtered_ddsim = provider.backends(name="DDSIM") # Matches "MQT Core DDSIM QDMI Device" # Filter by full name also works @@ -175,9 +173,7 @@ Associate your session with a specific project or organization: ```python # Specify a project ID -provider = QDMIProvider( - token="your_api_token", project_id="quantum-research-project-2024" -) +provider = QDMIProvider(token="your_api_token", project_id="quantum-research-project-2024") ``` ### Combining Authentication Parameters diff --git a/pyproject.toml b/pyproject.toml index 551cb08104..586456c41f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ authors = [ ] keywords = ["MQT", "quantum-computing", "design-automation", "decision-diagrams", "zx-calculus"] license = "MIT" -license-files = ["LICENSE.md"] +license-files = ["LICENSE"] classifiers = [ "Development Status :: 5 - Production/Stable", @@ -205,6 +205,10 @@ unsafe-fixes = true show-fixes = true src = ["python"] +[tool.ruff.format] +preview = true +docstring-code-format = true + [tool.ruff.lint] select = ["ALL"] ignore = [ @@ -242,9 +246,6 @@ known-first-party = ["mqt.core"] [tool.ruff.lint.pydocstyle] convention = "google" -[tool.ruff.format] -docstring-code-format = true - [tool.typos] default.extend-ignore-re = [ @@ -262,14 +263,13 @@ optin = "optin" nd = "nd" -[tool.repo-review] -ignore = [ - "GH200", # We use Renovate instead of Dependabot - "MY100", # We use ty instead of mypy - "PC140", # We use ty instead of mypy - "PC160", # We use a mirror of crate-ci/typos - "PC170", # We do not use rST files anymore -] +[tool.repo-review.ignore] +GH200 = "We use Renovate instead of Dependabot" +MY100 = "We use ty instead of mypy" +PC111 = "We use ruff instead of blacken-docs" +PC140 = "We use ty instead of mypy" +PC160 = "We use a mirror of crate-ci/typos" +PC170 = "We do not use rST files anymore" [tool.cibuildwheel] From 23be39d89242d8b296e85b8547331af179b20909 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 08:53:05 +0000 Subject: [PATCH 08/29] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=A9=B9=20Update?= =?UTF-8?q?=20actions/create-github-app-token=20action=20to=20v3.1.1=20(#1?= =?UTF-8?q?646)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [actions/create-github-app-token](https://redirect.github.com/actions/create-github-app-token) | action | patch | `v3.1.0` → `v3.1.1` | --- ### Release Notes
actions/create-github-app-token (actions/create-github-app-token) ### [`v3.1.1`](https://redirect.github.com/actions/create-github-app-token/releases/tag/v3.1.1) [Compare Source](https://redirect.github.com/actions/create-github-app-token/compare/v3.1.0...v3.1.1) ##### Bug Fixes - improve error message when app identifier is empty ([#​362](https://redirect.github.com/actions/create-github-app-token/issues/362)) ([07e2b76](https://redirect.github.com/actions/create-github-app-token/commit/07e2b760664f080c40eec4eacf7477256582db36)), closes [#​249](https://redirect.github.com/actions/create-github-app-token/issues/249)
--- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "every weekend" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/templating.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/templating.yml b/.github/workflows/templating.yml index c6a66aba6d..44fa9b65ff 100644 --- a/.github/workflows/templating.yml +++ b/.github/workflows/templating.yml @@ -22,7 +22,7 @@ jobs: JSON_CONTENT=$(cat .github/workflow_inputs/release_drafter_categories.json | jq -c .) echo "release_drafter_categories=$JSON_CONTENT" >> $GITHUB_OUTPUT - id: create-token - uses: actions/create-github-app-token@7bd03711494f032dfa3be3558f7dc8787b0be333 # v3.1.0 + uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} From 23cf7322d5f5a7bd2d5f2303f0a41944e5077ea4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 18 Apr 2026 09:55:32 +0000 Subject: [PATCH 09/29] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=A9=B9=20Update?= =?UTF-8?q?=20pre-commit=20hook=20henryiii/validate-pyproject-schema-store?= =?UTF-8?q?=20to=20v2026.04.11=20(#1647)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | Pending | |---|---|---|---|---| | [henryiii/validate-pyproject-schema-store](https://redirect.github.com/henryiii/validate-pyproject-schema-store) | repository | patch | `2026.04.10` → `2026.04.11` | `2026.04.17` (+1) | Note: The `pre-commit` manager in Renovate is not supported by the `pre-commit` maintainers or community. Please do not report any problems there, instead [create a Discussion in the Renovate repository](https://redirect.github.com/renovatebot/renovate/discussions/new) if you have any questions. --- ### Release Notes
henryiii/validate-pyproject-schema-store (henryiii/validate-pyproject-schema-store) ### [`v2026.04.11`](https://redirect.github.com/henryiii/validate-pyproject-schema-store/compare/2026.04.10...2026.04.11) [Compare Source](https://redirect.github.com/henryiii/validate-pyproject-schema-store/compare/2026.04.10...2026.04.11)
--- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "every weekend" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a541eac67d..b6c3ace3a7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,7 +30,7 @@ repos: ## Check the pyproject.toml file - repo: https://github.com/henryiii/validate-pyproject-schema-store - rev: 2026.04.10 + rev: 2026.04.11 hooks: - id: validate-pyproject priority: 0 From 0f63f41a7236f0ed9ff7a8b9178bebd5f776db9c Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Sun, 19 Apr 2026 09:52:54 +0200 Subject: [PATCH 10/29] =?UTF-8?q?=F0=9F=8E=A8=20Reduce=20sorting=20during?= =?UTF-8?q?=20QIR=20creation=20(#1648)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/lib/Conversion/QCToQIR/QCToQIR.cpp | 27 +++---------------- .../Dialect/QIR/Builder/QIRProgramBuilder.cpp | 26 +++--------------- 2 files changed, 6 insertions(+), 47 deletions(-) diff --git a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp index 6196336a18..ad081810da 100644 --- a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp +++ b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp @@ -1085,21 +1085,11 @@ struct QCToQIR final : impl::QCToQIRBase { builder.setInsertionPoint(&outputBlock.back()); if (!resultPtrs.empty()) { - // Sort result pointers for deterministic output - llvm::SmallVector> sortedPtrs; - for (const auto& [index, resultPtr] : resultPtrs) { - sortedPtrs.emplace_back(index, resultPtr); - } - llvm::sort(sortedPtrs, [](const auto& a, const auto& b) { - return a.first < b.first; - }); - - // Create output recording for each result pointer auto fnSig = LLVM::LLVMFunctionType::get(voidType, {ptrType, ptrType}); auto fnDec = getOrCreateFunctionDeclaration(builder, main, QIR_RECORD_OUTPUT, fnSig); - - for (const auto& [index, ptr] : sortedPtrs) { + // Create output recording for each result pointer + for (const auto& [index, ptr] : resultPtrs) { auto label = createResultLabel(builder, main, "__unnamed__" + std::to_string(index)) .getResult(); @@ -1109,25 +1099,14 @@ struct QCToQIR final : impl::QCToQIRBase { } if (!resultArrays.empty()) { - // Sort registers by name for deterministic output - SmallVector> sortedRegisters; - for (auto& [name, results] : resultArrays) { - sortedRegisters.emplace_back(name, results); - } - llvm::sort(sortedRegisters, [](const auto& a, const auto& b) { - return a.first < b.first; - }); - auto fnSig = LLVM::LLVMFunctionType::get( voidType, {builder.getI64Type(), ptrType, ptrType}); auto fnDec = getOrCreateFunctionDeclaration( builder, main, QIR_ARRAY_RECORD_OUTPUT, fnSig); - // Generate output recording for each register - for (auto& [name, results] : sortedRegisters) { + for (const auto& [name, results] : resultArrays) { auto size = results.getDefiningOp().getArraySize(); auto label = createResultLabel(builder, main, name).getResult(); - LLVM::CallOp::create(builder, main->getLoc(), fnDec, ValueRange{size, results, label}); } diff --git a/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp b/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp index 35800e7607..bca47a0265 100644 --- a/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp +++ b/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp @@ -12,7 +12,6 @@ #include "mlir/Dialect/QIR/Utils/QIRUtils.h" -#include #include #include #include @@ -666,20 +665,11 @@ void QIRProgramBuilder::generateOutputRecording() { setInsertionPoint(outputBlock->getTerminator()); if (!resultPtrs.empty()) { - // Sort result pointers for deterministic output - llvm::SmallVector> sortedPtrs; - for (const auto& [index, resultPtr] : resultPtrs) { - sortedPtrs.emplace_back(index, resultPtr); - } - llvm::sort(sortedPtrs, - [](const auto& a, const auto& b) { return a.first < b.first; }); - - // Create output recording for each result pointer auto fnSig = LLVM::LLVMFunctionType::get(voidType, {ptrType, ptrType}); auto fnDec = getOrCreateFunctionDeclaration(*this, module, QIR_RECORD_OUTPUT, fnSig); - - for (const auto& [index, ptr] : sortedPtrs) { + // Create output recording for each result pointer + for (const auto& [index, ptr] : resultPtrs) { auto label = createResultLabel(*this, module, "__unnamed__" + std::to_string(index)) .getResult(); @@ -688,24 +678,14 @@ void QIRProgramBuilder::generateOutputRecording() { } if (!resultArrays.empty()) { - // Sort registers by name for deterministic output - SmallVector> sortedArrays; - for (auto& [name, results] : resultArrays) { - sortedArrays.emplace_back(name, results); - } - llvm::sort(sortedArrays, - [](const auto& a, const auto& b) { return a.first < b.first; }); - auto fnSig = LLVM::LLVMFunctionType::get(voidType, {getI64Type(), ptrType, ptrType}); auto fnDec = getOrCreateFunctionDeclaration(*this, module, QIR_ARRAY_RECORD_OUTPUT, fnSig); - // Create output recording for each register - for (auto& [name, results] : sortedArrays) { + for (const auto& [name, results] : resultArrays) { auto size = results.getDefiningOp().getArraySize(); auto label = createResultLabel(*this, module, name).getResult(); - LLVM::CallOp::create(*this, fnDec, ValueRange{size, results, label}); } } From 14dab481958d460a7452111fe83dc6d99a7abd4c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 20 Apr 2026 02:27:19 +0000 Subject: [PATCH 11/29] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=94=92=EF=B8=8F?= =?UTF-8?q?=20Lock=20file=20maintenance=20(#1649)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Update | Change | |---|---| | lockFileMaintenance | All locks refreshed | 🔧 This Pull Request updates lock files to use the latest dependency versions. --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "before 4am on monday" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). --------- Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com> --- python/mqt/core/plugins/qiskit/provider.py | 2 +- test/python/plugins/qiskit/test_provider.py | 8 +++- uv.lock | 42 ++++++++++----------- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/python/mqt/core/plugins/qiskit/provider.py b/python/mqt/core/plugins/qiskit/provider.py index f3e0378fe8..5d44c55ae9 100644 --- a/python/mqt/core/plugins/qiskit/provider.py +++ b/python/mqt/core/plugins/qiskit/provider.py @@ -110,7 +110,7 @@ def backends(self, name: str | None = None) -> list[QDMIBackend]: """ # Filter by name substring if specified if name is not None: - return [b for b in self._backends if name in b.name] + return [b for b in self._backends if b.name is not None and name in b.name] return self._backends diff --git a/test/python/plugins/qiskit/test_provider.py b/test/python/plugins/qiskit/test_provider.py index 50f0e63b4c..7584f275ef 100644 --- a/test/python/plugins/qiskit/test_provider.py +++ b/test/python/plugins/qiskit/test_provider.py @@ -49,12 +49,16 @@ def test_provider_backends_filter_by_substring() -> None: # Filter by "QDMI" substring (should match "MQT Core DDSIM QDMI Device") filtered = provider.backends(name="QDMI") assert len(filtered) > 0 - assert all("QDMI" in b.name for b in filtered) + for backend in filtered: + assert backend.name is not None + assert "QDMI" in backend.name # Filter by "DDSIM" substring filtered_ddsim = provider.backends(name="DDSIM") assert len(filtered_ddsim) > 0 - assert all("DDSIM" in b.name for b in filtered_ddsim) + for backend in filtered_ddsim: + assert backend.name is not None + assert "DDSIM" in backend.name def test_provider_backends_filter_nonexistent_name() -> None: diff --git a/uv.lock b/uv.lock index dad4804cee..3c5abef803 100644 --- a/uv.lock +++ b/uv.lock @@ -807,11 +807,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.25.2" +version = "3.29.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, + { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, ] [[package]] @@ -2065,11 +2065,11 @@ parser = [ [[package]] name = "packaging" -version = "26.0" +version = "26.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, + { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, ] [[package]] @@ -2729,7 +2729,7 @@ wheels = [ [[package]] name = "qiskit" -version = "2.3.1" +version = "2.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, @@ -2741,15 +2741,15 @@ dependencies = [ { name = "stevedore" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f9/9a/a366dd56b6a4520e5b2856e47d573c6813b4c3f62f28969a37205fd845ab/qiskit-2.3.1.tar.gz", hash = "sha256:7b3b7e1c8a50f7f423204143a1bd9f21bf27659c57459d582eaff4035d8d7f75", size = 3909915, upload-time = "2026-03-13T00:43:03.257Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/cb/a348ec6595a8a2163fad714692c5b2d82ffb6a7f82e7a64f5a58e0dbc37e/qiskit-2.4.0.tar.gz", hash = "sha256:57a43f869608fd8319444c2c1fbf6674fa36c6346646e41f4b128233693970d6", size = 4080145, upload-time = "2026-04-16T17:21:13.532Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/70/dba6afec66d2bf4df64b7059cd144bf0c775d998cb57b941d1065b331d28/qiskit-2.3.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:25a85465e8c860627e8a565cb9af824b2df1a2c42f5958b1a7b014aa085b32d6", size = 8614500, upload-time = "2026-03-13T02:30:24.247Z" }, - { url = "https://files.pythonhosted.org/packages/55/0b/47d67d58b4de120e10d49255b635429bc162a982b0cd58cf75b1a9f594fa/qiskit-2.3.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:b1404b72987d0c7db32203a87565775115f6a77b2a3539d40eb1e354df6d08bb", size = 7819922, upload-time = "2026-03-13T00:42:54.205Z" }, - { url = "https://files.pythonhosted.org/packages/02/f0/2af86ce0e11b06248bc6a66550acf98c19e606cb317c27bab4fbed2ce37f/qiskit-2.3.1-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68eec14320654065a158bf73b3d6fffaa31e865fe61b0db1510cfeeea76c87b0", size = 8176336, upload-time = "2026-03-13T00:42:56.891Z" }, - { url = "https://files.pythonhosted.org/packages/8a/fc/0bf71353c68b9090ed05e5c1d08730301c58a2b00f3001f0050f032bdcc9/qiskit-2.3.1-cp310-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:96545ad531c5e2ff6eecd348501620e0db0f3d73f49c20037ec3b5b2e9268f94", size = 8915234, upload-time = "2026-03-13T02:30:27.055Z" }, - { url = "https://files.pythonhosted.org/packages/1b/0c/19ffdb15d2e72156de406fe27cad26c6015071a385e6b48c6db87d8ce676/qiskit-2.3.1-cp310-abi3-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a1a030b4a0cd75f3299c9866a3e9489a02078efc343abd42eec74ad2a5f2fa80", size = 8513126, upload-time = "2026-03-13T02:30:30.288Z" }, - { url = "https://files.pythonhosted.org/packages/11/06/41f4197fcb3c38e99f22f400ea25375c9057190cd094cd6c0f97be730796/qiskit-2.3.1-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:350027279bb171e9746fee5756c674872df97f4608b079129d4edb25b5bb1fdd", size = 8833222, upload-time = "2026-03-13T00:42:59.092Z" }, - { url = "https://files.pythonhosted.org/packages/a5/db/6ddc52eaf22ab1f95ee6251ff7a375c0a1a7453b5864528992303b166198/qiskit-2.3.1-cp310-abi3-win_amd64.whl", hash = "sha256:f884abd92c0173e246705f89f8b4808badaa0e1b167ed3277a3cb83c2564b4b8", size = 8639580, upload-time = "2026-03-13T00:43:01.022Z" }, + { url = "https://files.pythonhosted.org/packages/ac/cc/47fa03c0a52d603f978b95bc45c0f99bd436bc172f0727fe0f63f2758ce7/qiskit-2.4.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:a6dfd5875a77736749e851c631ff5a328dc60e863c8b0aafd60fdc745a4c7591", size = 9350416, upload-time = "2026-04-16T19:34:53.371Z" }, + { url = "https://files.pythonhosted.org/packages/f9/37/9744dedced76513cb5517a574e268adaa8c10d127eb8add72a6236b3f51e/qiskit-2.4.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:4ff99307a8fac97962964b02afe22ae7ee4997a49e98538599ff4b44e6659d31", size = 8328830, upload-time = "2026-04-16T17:21:02.609Z" }, + { url = "https://files.pythonhosted.org/packages/f0/70/fbd7634fbd66fe6419741051e00d719398466fc437e4cb148effeb057ce1/qiskit-2.4.0-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5352c66c5affd54dfb2473e99247031aaaf3af6c7cefe0875a0bd302ca31fcc1", size = 8625281, upload-time = "2026-04-16T17:21:05.012Z" }, + { url = "https://files.pythonhosted.org/packages/ee/e1/1167314fb2c0d31f41975201604708ddef83192a9289e05f69a32898804a/qiskit-2.4.0-cp310-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:fd04cfb30b59fd257a1108fd6f337eceafd26039c8e86603bcced9b45137971f", size = 9680387, upload-time = "2026-04-16T19:34:56.044Z" }, + { url = "https://files.pythonhosted.org/packages/87/c4/6e639125851e3d6b709aea0d71b10ffc8fce14af322d2de6112cf57627f7/qiskit-2.4.0-cp310-abi3-manylinux_2_28_s390x.whl", hash = "sha256:446c8d6da61e9e134ba5e30f933240ce9920f668d0e010fc79cc5a22c03e9a78", size = 9186598, upload-time = "2026-04-16T19:34:58.786Z" }, + { url = "https://files.pythonhosted.org/packages/62/55/1e6d61a1c8a83d685d281f5ae15e7c2c62080aed358a9c4b79309aa393d9/qiskit-2.4.0-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cf9020bf79934c7bf490973d99b80464957efc1fa13456d680c42bce23262687", size = 9329265, upload-time = "2026-04-16T17:21:08.193Z" }, + { url = "https://files.pythonhosted.org/packages/94/26/b4d51da621e80a3aa4fccf72502f218773bb2f1bd63f9979e8a7b33ffaaf/qiskit-2.4.0-cp310-abi3-win_amd64.whl", hash = "sha256:0292d0d247f1603c225c3e83897aa8c93bc3010523f337f766750f70a8fff5ba", size = 9111120, upload-time = "2026-04-16T17:21:10.623Z" }, ] [package.optional-dependencies] @@ -3739,7 +3739,7 @@ wheels = [ [[package]] name = "virtualenv" -version = "21.2.1" +version = "21.2.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -3748,9 +3748,9 @@ dependencies = [ { name = "python-discovery" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/c5/aff062c66b42e2183201a7ace10c6b2e959a9a16525c8e8ca8e59410d27a/virtualenv-21.2.1.tar.gz", hash = "sha256:b66ffe81301766c0d5e2208fc3576652c59d44e7b731fc5f5ed701c9b537fa78", size = 5844770, upload-time = "2026-04-09T18:47:11.482Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/98/3a7e644e19cb26133488caff231be390579860bbbb3da35913c49a1d0a46/virtualenv-21.2.4.tar.gz", hash = "sha256:b294ef68192638004d72524ce7ef303e9d0cf5a44c95ce2e54a7500a6381cada", size = 5850742, upload-time = "2026-04-14T22:15:31.438Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/0e/f083a76cb590e60dff3868779558eefefb8dfb7c9ed020babc7aa014ccbf/virtualenv-21.2.1-py3-none-any.whl", hash = "sha256:bd16b49c53562b28cf1a3ad2f36edb805ad71301dee70ddc449e5c88a9f919a2", size = 5828326, upload-time = "2026-04-09T18:47:09.331Z" }, + { url = "https://files.pythonhosted.org/packages/27/8d/edd0bd910ff803c308ee9a6b7778621af0d10252219ad9f19ef4d4982a61/virtualenv-21.2.4-py3-none-any.whl", hash = "sha256:29d21e941795206138d0f22f4e45ff7050e5da6c6472299fb7103318763861ac", size = 5831232, upload-time = "2026-04-14T22:15:29.342Z" }, ] [[package]] @@ -3764,9 +3764,9 @@ wheels = [ [[package]] name = "zipp" -version = "3.23.0" +version = "3.23.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/21/093488dfc7cc8964ded15ab726fad40f25fd3d788fd741cc1c5a17d78ee8/zipp-3.23.1.tar.gz", hash = "sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110", size = 25965, upload-time = "2026-04-13T23:21:46.6Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/08/8a/0861bec20485572fbddf3dfba2910e38fe249796cb73ecdeb74e07eeb8d3/zipp-3.23.1-py3-none-any.whl", hash = "sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc", size = 10378, upload-time = "2026-04-13T23:21:45.386Z" }, ] From 011d4dc2f415bb99856df8bc18f202f9f99723b4 Mon Sep 17 00:00:00 2001 From: Keefe Huang <30915130+keefehuang@users.noreply.github.com> Date: Mon, 20 Apr 2026 14:37:32 +0200 Subject: [PATCH 12/29] =?UTF-8?q?=E2=9C=A8=20Add=20support=20for=20multi-c?= =?UTF-8?q?ontrolled=20gates=20to=20ZX=20package=20(#1380)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Add CRZ, MCX, MCZ, MCRZ functionality to ZX package. Add convenience addCCZ function as it avoids one additional Hardamard edge (optimizes it away). Fixes #1357 ## Checklist: - [X] The pull request only contains commits that are focused and relevant to this change. - [X] I have added appropriate tests that cover the new/changed functionality. - [X] ~~I have updated the documentation to reflect these changes.~~ - [x] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [x] ~~I have added migration instructions to the upgrade guide (if needed).~~ - [x] The changes follow the project's style guidelines and introduce no new warnings. - [x] The changes are fully tested and pass the CI checks. - [X] I have reviewed my own code changes. --------- Signed-off-by: Marcel Walter Signed-off-by: Keefe Huang <30915130+keefehuang@users.noreply.github.com> Signed-off-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Signed-off-by: Lukas Burgholzer Co-authored-by: Keefe Huang Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Marcel Walter Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Lukas Burgholzer Co-authored-by: Keefe Huang Co-authored-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com> --- CHANGELOG.md | 3 + .../mqt-core/zx/FunctionalityConstruction.hpp | 29 +- src/zx/FunctionalityConstruction.cpp | 349 +++++++++++- test/zx/test_zx_functionality.cpp | 511 ++++++++++++++---- 4 files changed, 755 insertions(+), 137 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cf9efb0be..91cf031f82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Added +- ✨ Add support for multi-controlled gates to ZX package ([#1380]) ([**@keefehuang**]) - ✨ Add Sampler and Estimator Primitives to the QDMI-Qiskit Interface ([#1507]) ([**@marcelwa**]) - ✨ Add conversions between `jeff` and QCO ([#1479], [#1548], [#1565], [#1637]) ([**@denialhaag**]) - ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588]) ([**@MatthiasReumann**]) @@ -403,6 +404,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [#1383]: https://github.com/munich-quantum-toolkit/core/pull/1383 [#1382]: https://github.com/munich-quantum-toolkit/core/pull/1382 [#1381]: https://github.com/munich-quantum-toolkit/core/pull/1381 +[#1380]: https://github.com/munich-quantum-toolkit/core/pull/1380 [#1378]: https://github.com/munich-quantum-toolkit/core/pull/1378 [#1375]: https://github.com/munich-quantum-toolkit/core/pull/1375 [#1371]: https://github.com/munich-quantum-toolkit/core/pull/1371 @@ -551,6 +553,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [**@lirem101**]: https://github.com/lirem101 [**@Ectras**]: https://github.com/Ectras [**@simon1hofmann**]: https://github.com/simon1hofmann +[**@keefehuang**]: https://github.com/keefehuang diff --git a/include/mqt-core/zx/FunctionalityConstruction.hpp b/include/mqt-core/zx/FunctionalityConstruction.hpp index b22c3e12e5..ab4a9e6f14 100644 --- a/include/mqt-core/zx/FunctionalityConstruction.hpp +++ b/include/mqt-core/zx/FunctionalityConstruction.hpp @@ -85,8 +85,14 @@ class FunctionalityConstruction { EdgeType type = EdgeType::Simple); static void addCphase(ZXDiagram& diag, const PiExpression& phase, Qubit ctrl, Qubit target, std::vector& qubits); - static void addSwap(ZXDiagram& diag, Qubit target, Qubit target2, + static void addMcphase(ZXDiagram& diag, const PiExpression& phase, + const std::vector& controls, Qubit target, + std::vector& qubits); + static void addSwap(ZXDiagram& diag, Qubit target1, Qubit target2, std::vector& qubits); + static void addMcswap(ZXDiagram& diag, const std::vector& controls, + Qubit target1, Qubit target2, + std::vector& qubits); static void addRzz(ZXDiagram& diag, const PiExpression& phase, Qubit target, Qubit target2, std::vector& qubits, @@ -99,6 +105,11 @@ class FunctionalityConstruction { addRzx(ZXDiagram& diag, const PiExpression& phase, Qubit target, Qubit target2, std::vector& qubits, const std::optional& unconvertedPhase = std::nullopt); + static void + addMcrzz(ZXDiagram& diag, const PiExpression& phase, + const std::vector& controls, Qubit target, Qubit target2, + std::vector& qubits, + const std::optional& unconvertedPhase = std::nullopt); static void addDcx(ZXDiagram& diag, Qubit qubit1, Qubit qubit2, std::vector& qubits); static void @@ -113,6 +124,22 @@ class FunctionalityConstruction { const std::optional& unconvertedBeta = std::nullopt); static void addCcx(ZXDiagram& diag, Qubit ctrl0, Qubit ctrl1, Qubit target, std::vector& qubits); + static void addCcz(ZXDiagram& diag, Qubit ctrl0, Qubit ctrl1, Qubit target, + std::vector& qubits); + static void addCrx(ZXDiagram& diag, const PiExpression& phase, Qubit control, + Qubit target, std::vector& qubits); + static void addMcrx(ZXDiagram& diag, const PiExpression& phase, + const std::vector& controls, Qubit target, + std::vector& qubits); + static void addCrz(ZXDiagram& diag, const PiExpression& phase, Qubit control, + Qubit target, std::vector& qubits); + static void addMcrz(ZXDiagram& diag, const PiExpression& phase, + std::vector controls, Qubit target, + std::vector& qubits); + static void addMcx(ZXDiagram& diag, std::vector controls, Qubit target, + std::vector& qubits); + static void addMcz(ZXDiagram& diag, const std::vector& controls, + Qubit target, std::vector& qubits); static op_it parseOp(ZXDiagram& diag, op_it it, op_it end, std::vector& qubits, const qc::Permutation& p); static op_it parseCompoundOp(ZXDiagram& diag, op_it it, op_it end, diff --git a/src/zx/FunctionalityConstruction.cpp b/src/zx/FunctionalityConstruction.cpp index 23697afaef..519fde6fd8 100644 --- a/src/zx/FunctionalityConstruction.cpp +++ b/src/zx/FunctionalityConstruction.cpp @@ -140,6 +140,32 @@ void FunctionalityConstruction::addCphase(ZXDiagram& diag, addZSpider(diag, target, qubits, newPhase); } +void FunctionalityConstruction::addMcphase(ZXDiagram& diag, + const PiExpression& phase, + const std::vector& controls, + const Qubit target, + std::vector& qubits) { + auto newConst = phase.getConst() / 2; + auto newPhase = phase / 2.0; + newPhase.setConst(newConst); + addZSpider(diag, target, qubits, newPhase); + addMcx(diag, controls, target, qubits); + addZSpider(diag, target, qubits, -newPhase); + addMcx(diag, controls, target, qubits); + switch (controls.size()) { + case 1: + addZSpider(diag, controls[0], qubits, newPhase); + return; + case 2: + addCphase(diag, newPhase, controls[0], controls[1], qubits); + return; + default: + addMcphase(diag, newPhase, + std::vector(controls.begin(), controls.end() - 1), + controls.back(), qubits); + } +} + void FunctionalityConstruction::addRzz( ZXDiagram& diag, const PiExpression& phase, const Qubit target, const Qubit target2, std::vector& qubits, @@ -207,6 +233,23 @@ void FunctionalityConstruction::addRzx( } } +void FunctionalityConstruction::addMcrzz( + ZXDiagram& diag, const PiExpression& phase, + const std::vector& controls, const Qubit target, const Qubit target2, + std::vector& qubits, + const std::optional& unconvertedPhase) { + addRzz(diag, phase / 2, target, target2, qubits, + unconvertedPhase.has_value() + ? std::optional(unconvertedPhase.value() / 2) + : std::nullopt); + addMcx(diag, controls, target, qubits); + addRzz(diag, -phase / 2, target, target2, qubits, + unconvertedPhase.has_value() + ? std::optional(-unconvertedPhase.value() / 2) + : std::nullopt); + addMcx(diag, controls, target, qubits); +} + void FunctionalityConstruction::addDcx(ZXDiagram& diag, const Qubit qubit1, const Qubit qubit2, std::vector& qubits) { @@ -263,10 +306,10 @@ void FunctionalityConstruction::addXXminusYY( addRz(diag, PiExpression(-PiRational(1, 2)), qubit1, qubits); } -void FunctionalityConstruction::addSwap(ZXDiagram& diag, const Qubit target, +void FunctionalityConstruction::addSwap(ZXDiagram& diag, const Qubit target1, const Qubit target2, std::vector& qubits) { - const auto c = static_cast(target); + const auto c = static_cast(target1); const auto t = static_cast(target2); const auto s0 = qubits[t]; @@ -279,13 +322,27 @@ void FunctionalityConstruction::addSwap(ZXDiagram& diag, const Qubit target, const auto col = vData->col + 1; const auto t0 = diag.addVertex(target2, col); - const auto t1 = diag.addVertex(target, col); + const auto t1 = diag.addVertex(target1, col); diag.addEdge(s0, t1); diag.addEdge(s1, t0); qubits[t] = t0; qubits[c] = t1; } +void FunctionalityConstruction::addMcswap(ZXDiagram& diag, + const std::vector& controls, + const Qubit target1, + const Qubit target2, + std::vector& qubits) { + + std::vector mcxControls = controls; + mcxControls.emplace_back(target2); + + addCnot(diag, target1, target2, qubits); + addMcx(diag, mcxControls, target1, qubits); + addCnot(diag, target1, target2, qubits); +} + void FunctionalityConstruction::addCcx(ZXDiagram& diag, const Qubit ctrl0, const Qubit ctrl1, const Qubit target, std::vector& qubits) { @@ -307,6 +364,118 @@ void FunctionalityConstruction::addCcx(ZXDiagram& diag, const Qubit ctrl0, addCnot(diag, ctrl0, ctrl1, qubits); } +void FunctionalityConstruction::addCcz(ZXDiagram& diag, const Qubit ctrl0, + const Qubit ctrl1, const Qubit target, + std::vector& qubits) { + addCnot(diag, ctrl1, target, qubits); + addZSpider(diag, target, qubits, PiExpression(PiRational(-1, 4))); + addCnot(diag, ctrl0, target, qubits); + addZSpider(diag, target, qubits, PiExpression(PiRational(1, 4))); + addCnot(diag, ctrl1, target, qubits); + addZSpider(diag, ctrl1, qubits, PiExpression(PiRational(1, 4))); + addZSpider(diag, target, qubits, PiExpression(PiRational(-1, 4))); + addCnot(diag, ctrl0, target, qubits); + addZSpider(diag, target, qubits, PiExpression(PiRational(1, 4))); + addCnot(diag, ctrl0, ctrl1, qubits); + addZSpider(diag, ctrl0, qubits, PiExpression(PiRational(1, 4))); + addZSpider(diag, ctrl1, qubits, PiExpression(PiRational(-1, 4))); + addCnot(diag, ctrl0, ctrl1, qubits); +} + +void FunctionalityConstruction::addCrx(ZXDiagram& diag, + const PiExpression& phase, + const Qubit control, const Qubit target, + std::vector& qubits) { + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addCrz(diag, phase, control, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); +} + +void FunctionalityConstruction::addMcrx(ZXDiagram& diag, + const PiExpression& phase, + const std::vector& controls, + const Qubit target, + std::vector& qubits) { + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addMcrz(diag, phase, controls, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); +} + +void FunctionalityConstruction::addCrz(ZXDiagram& diag, + const PiExpression& phase, + const Qubit control, const Qubit target, + std::vector& qubits) { + // CRZ decomposition uses reversed CNOT direction + addZSpider(diag, target, qubits, phase / 2); + // NOLINTNEXTLINE(readability-suspicious-call-argument) + addCnot(diag, target, control, qubits); + addZSpider(diag, control, qubits, -phase / 2); + // NOLINTNEXTLINE(readability-suspicious-call-argument) + addCnot(diag, target, control, qubits); +} + +void FunctionalityConstruction::addMcrz(ZXDiagram& diag, + const PiExpression& phase, + std::vector controls, + const Qubit target, + std::vector& qubits) { + const Qubit nextControl = controls.back(); + controls.pop_back(); + + addCrz(diag, phase / 2, nextControl, target, qubits); + addMcx(diag, controls, target, qubits); + addCrz(diag, -phase / 2, nextControl, target, qubits); + addMcx(diag, controls, target, qubits); +} + +void FunctionalityConstruction::addMcx(ZXDiagram& diag, + std::vector controls, + const Qubit target, + std::vector& qubits) { + switch (controls.size()) { + case 1: + addCnot(diag, controls.front(), target, qubits); + return; + case 2: + addCcx(diag, controls.front(), controls.back(), target, qubits); + return; + default: + const auto half = static_cast((controls.size() + 1) / 2); + const std::vector first(controls.begin(), controls.begin() + half); + const std::vector second(controls.begin() + half, controls.end()); + + addRx(diag, PiExpression(PiRational(1, 4)), target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addMcx(diag, first, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addRx(diag, PiExpression(-PiRational(1, 4)), target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addMcx(diag, second, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addRx(diag, PiExpression(PiRational(1, 4)), target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addMcx(diag, first, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addRx(diag, PiExpression(-PiRational(1, 4)), target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addMcx(diag, second, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + const Qubit lastControl = controls.back(); + controls.pop_back(); + addMcphase(diag, PiExpression(PiRational(1, 2)), controls, lastControl, + qubits); + } +} + +void FunctionalityConstruction::addMcz(ZXDiagram& diag, + const std::vector& controls, + const Qubit target, + std::vector& qubits) { + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addMcx(diag, controls, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); +} + FunctionalityConstruction::op_it FunctionalityConstruction::parseOp(ZXDiagram& diag, op_it it, op_it end, std::vector& qubits, @@ -516,7 +685,6 @@ FunctionalityConstruction::parseOp(ZXDiagram& diag, op_it it, op_it end, qc::toString(op->getType())); } } else if (op->getNcontrols() == 1 && op->getNtargets() == 1) { - // two-qubit controlled gates const auto target = static_cast(p.at(op->getTargets().front())); const auto ctrl = static_cast(p.at((*op->getControls().begin()).qubit)); @@ -538,10 +706,15 @@ FunctionalityConstruction::parseOp(ZXDiagram& diag, op_it it, op_it end, qubits[static_cast(target)], EdgeType::Hadamard); break; + case qc::OpType::RZ: + addCrz(diag, parseParam(op.get(), 0), ctrl, target, qubits); + break; + case qc::OpType::RX: + addCrx(diag, parseParam(op.get(), 0), ctrl, target, qubits); + break; case qc::OpType::I: break; - case qc::OpType::P: addCphase(diag, parseParam(op.get(), 0), ctrl, target, qubits); break; @@ -565,8 +738,7 @@ FunctionalityConstruction::parseOp(ZXDiagram& diag, op_it it, op_it end, throw ZXException("Unsupported Controlled Operation: " + qc::toString(op->getType())); } - } else if (op->getNcontrols() == 2) { - // three-qubit controlled gates (ccx or ccz) + } else if (op->getNcontrols() == 2 && op->getNtargets() == 1) { Qubit ctrl0 = 0; Qubit ctrl1 = 0; const auto target = static_cast(p.at(op->getTargets().front())); @@ -582,20 +754,142 @@ FunctionalityConstruction::parseOp(ZXDiagram& diag, op_it it, op_it end, case qc::OpType::X: addCcx(diag, ctrl0, ctrl1, target, qubits); break; - case qc::OpType::Z: + addCcz(diag, ctrl0, ctrl1, target, qubits); + break; + case qc::OpType::P: + addMcphase(diag, parseParam(op.get(), 0), {ctrl0, ctrl1}, target, qubits); + break; + case qc::OpType::T: + addMcphase(diag, PiExpression{PiRational(1, 4)}, {ctrl0, ctrl1}, target, + qubits); + break; + case qc::OpType::Tdg: + addMcphase(diag, PiExpression{PiRational(-1, 4)}, {ctrl0, ctrl1}, target, + qubits); + break; + case qc::OpType::S: + addMcphase(diag, PiExpression{PiRational(1, 2)}, {ctrl0, ctrl1}, target, + qubits); + break; + case qc::OpType::Sdg: + addMcphase(diag, PiExpression{PiRational(-1, 2)}, {ctrl0, ctrl1}, target, + qubits); + break; + case qc::OpType::RZ: + addMcrz(diag, parseParam(op.get(), 0), {ctrl0, ctrl1}, target, qubits); + break; + case qc::OpType::RX: + addMcrx(diag, parseParam(op.get(), 0), {ctrl0, ctrl1}, target, qubits); + break; + default: + throw ZXException("Unsupported multi-control operation (" + + std::to_string(op->getNcontrols()) + + " ctrls): " + qc::toString(op->getType())); + } + } else if (op->getNtargets() == 1) { + const auto target = static_cast(p.at(op->getTargets().front())); + std::vector controls; + controls.reserve(op->getNcontrols()); + for (const auto& ctrl : op->getControls()) { + controls.emplace_back(static_cast(p.at(ctrl.qubit))); + } + switch (op->getType()) { + case qc::OpType::X: + addMcx(diag, controls, target, qubits); + break; + case qc::OpType::Z: + addMcz(diag, controls, target, qubits); + break; + case qc::OpType::P: + addMcphase(diag, parseParam(op.get(), 0), controls, target, qubits); + break; + case qc::OpType::T: + addMcphase(diag, PiExpression{PiRational(1, 4)}, controls, target, + qubits); + break; + case qc::OpType::Tdg: + addMcphase(diag, PiExpression{PiRational(-1, 4)}, controls, target, + qubits); + break; + case qc::OpType::S: + addMcphase(diag, PiExpression{PiRational(1, 2)}, controls, target, + qubits); + break; + case qc::OpType::Sdg: + addMcphase(diag, PiExpression{PiRational(-1, 2)}, controls, target, + qubits); + break; + case qc::OpType::RZ: + addMcrz(diag, parseParam(op.get(), 0), controls, target, qubits); + break; + case qc::OpType::RX: + addMcrx(diag, parseParam(op.get(), 0), controls, target, qubits); + break; + default: + throw ZXException("Unsupported multi-control operation (" + + std::to_string(op->getNcontrols()) + + " ctrls): " + qc::toString(op->getType())); + } + } else if (op->getNtargets() == 2) { + // at this point, op must have getNtargets() == 2 + // all 1-target cases handled above + const auto target = static_cast(p.at(op->getTargets().front())); + const auto target2 = static_cast(p.at(op->getTargets()[1])); + std::vector controls; + controls.reserve(op->getNcontrols()); + for (const auto& ctrl : op->getControls()) { + controls.emplace_back(static_cast(p.at(ctrl.qubit))); + } + switch (op->getType()) { + case qc::OpType::SWAP: + addMcswap(diag, controls, target, target2, qubits); + break; + case qc::OpType::RZZ: { + const auto& phase = parseParam(op.get(), 0); + if (phase.isConstant()) { + addMcrzz(diag, phase, controls, target, target2, qubits, + op->getParameter().at(0)); + } else { + addMcrzz(diag, phase, controls, target, target2, qubits); + } + break; + } + case qc::OpType::RXX: { + const auto& phase = parseParam(op.get(), 0); addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); - addCcx(diag, ctrl0, ctrl1, target, qubits); + addZSpider(diag, target2, qubits, PiExpression(), EdgeType::Hadamard); + if (phase.isConstant()) { + addMcrzz(diag, phase, controls, target, target2, qubits, + op->getParameter().at(0)); + } else { + addMcrzz(diag, phase, controls, target, target2, qubits); + } + addZSpider(diag, target2, qubits, PiExpression(), EdgeType::Hadamard); addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); break; + } + case qc::OpType::RZX: { + const auto& phase = parseParam(op.get(), 0); + addZSpider(diag, target2, qubits, PiExpression(), EdgeType::Hadamard); + if (phase.isConstant()) { + addMcrzz(diag, phase, controls, target, target2, qubits, + op->getParameter().at(0)); + } else { + addMcrzz(diag, phase, controls, target, target2, qubits); + } + addZSpider(diag, target2, qubits, PiExpression(), EdgeType::Hadamard); + break; + } default: - throw ZXException("Unsupported Multi-control operation: " + - qc::toString(op->getType())); + throw ZXException("Unsupported multi-control operation (" + + std::to_string(op->getNcontrols()) + + " ctrls): " + qc::toString(op->getType())); } } else { - throw ZXException("Unsupported Multi-control operation (" + - std::to_string(op->getNcontrols()) + " ctrls)" + - qc::toString(op->getType())); + throw ZXException("Unsupported multi-control operation (" + + std::to_string(op->getNcontrols()) + + " ctrls): " + qc::toString(op->getType())); } return it + 1; } @@ -634,6 +928,7 @@ ZXDiagram FunctionalityConstruction::buildFunctionality( } return diag; } + bool FunctionalityConstruction::transformableToZX( const qc::QuantumComputation* qc) { return std::ranges::all_of( @@ -698,18 +993,36 @@ bool FunctionalityConstruction::transformableToZX(const qc::Operation* op) { case qc::OpType::I: case qc::OpType::P: case qc::OpType::T: - case qc::OpType::S: case qc::OpType::Tdg: + case qc::OpType::S: case qc::OpType::Sdg: + case qc::OpType::RX: + case qc::OpType::RZ: return true; - default: return false; } - } else if (op->getNcontrols() == 2) { + } else if (op->getNtargets() == 1) { switch (op->getType()) { case qc::OpType::X: case qc::OpType::Z: + case qc::OpType::P: + case qc::OpType::T: + case qc::OpType::Tdg: + case qc::OpType::S: + case qc::OpType::Sdg: + case qc::OpType::RZ: + case qc::OpType::RX: + return true; + default: + return false; + } + } else if (op->getNtargets() == 2) { + switch (op->getType()) { + case qc::OpType::SWAP: + case qc::OpType::RZZ: + case qc::OpType::RXX: + case qc::OpType::RZX: return true; default: return false; @@ -725,6 +1038,7 @@ PiExpression FunctionalityConstruction::parseParam(const qc::Operation* op, } return PiExpression{PiRational{op->getParameter().at(i)}}; } + PiExpression FunctionalityConstruction::toPiExpr(const qc::SymbolOrNumber& param) { if (std::holds_alternative(param)) { @@ -732,4 +1046,5 @@ FunctionalityConstruction::toPiExpr(const qc::SymbolOrNumber& param) { } return std::get(param).convert(); } + } // namespace zx diff --git a/test/zx/test_zx_functionality.cpp b/test/zx/test_zx_functionality.cpp index 79edeec944..3667a47e1b 100644 --- a/test/zx/test_zx_functionality.cpp +++ b/test/zx/test_zx_functionality.cpp @@ -29,6 +29,7 @@ #include #include #include +#include namespace zx { @@ -39,6 +40,25 @@ class ZXFunctionalityTest : public ::testing::Test { qc::QuantumComputation qc; }; +void checkEquivalence(const qc::QuantumComputation& qc1, + const qc::QuantumComputation& qc2, + const std::vector& qubits) { + ASSERT_TRUE(FunctionalityConstruction::transformableToZX(&qc1)); + ASSERT_TRUE(FunctionalityConstruction::transformableToZX(&qc2)); + ASSERT_EQ(qc1.getNqubits(), qc2.getNqubits()); + + auto d1 = FunctionalityConstruction::buildFunctionality(&qc1); + auto d2 = FunctionalityConstruction::buildFunctionality(&qc2); + d1.concat(d2.invert()); + fullReduce(d1); + EXPECT_TRUE(d1.isIdentity()); + EXPECT_TRUE(d1.globalPhaseIsZero()); + for (const auto q : qubits) { + ASSERT_LT(q, qc1.getNqubits()); + EXPECT_TRUE(d1.connected(d1.getInput(q), d1.getOutput(q))); + } +} + } // namespace TEST_F(ZXFunctionalityTest, parseQasm) { @@ -97,70 +117,29 @@ TEST_F(ZXFunctionalityTest, complexCircuit) { std::stringstream ss{}; ss << "// i 1 0 2\n" << "// o 0 1 2\n" - << "OPENQASM 2.0;" - << "include \"qelib1.inc\";" - << "qreg q[3];" - << "sx q[0];" - << "sxdg q[0];" - << "h q[0];" - << "cx q[0],q[1];" - << "z q[1];" - << "x q[2];" - << "y q[0];" - << "rx(pi/4) q[0];" - << "rz(0.1) q[1];" - << "p(0.1) q[1];" - << "ry(pi/4) q[2];" - << "t q[0];" - << "s q[2];" - << "u2(pi/4, pi/4) q[1];" - << "u3(pi/4, pi/4, pi/4) q[2];" - << "barrier q[0],q[1],q[2];" - << "swap q[0],q[1];" - << "cz q[1],q[2];" - << "cp(pi/4) q[0],q[1];" - << "ctrl(2) @ x q[0],q[1],q[2];" - << "ctrl(2) @ z q[1],q[2],q[0];" - << "cp(pi/2) q[0], q[1];" - << "cp(pi/4) q[0], q[1];" - << "cp(pi/8) q[0], q[1];" - << "rzz(pi/4) q[0], q[1];" - << "rxx(pi/4) q[0], q[1];" - << "ryy(pi/4) q[0], q[1];" - << "rzx(pi/4) q[0], q[1];" - << "ecr q[0], q[1];" - << "dcx q[0], q[1];" - << "r(pi/8, pi/4) q[2];" - << "r(-pi/8, pi/4) q[2];" - << "dcx q[1], q[0];" - << "ecr q[0], q[1];" - << "rzx(-pi/4) q[0], q[1];" - << "ryy(-pi/4) q[0], q[1];" - << "rxx(-pi/4) q[0], q[1];" - << "rzz(-pi/4) q[0], q[1];" - << "cp(-pi/8) q[0], q[1];" - << "cp(-pi/4) q[0], q[1];" - << "cp(-pi/2) q[0], q[1];" - << "ctrl(2) @ z q[1],q[2],q[0];" - << "ctrl(2) @ x q[0],q[1],q[2];" - << "cp(-pi/4) q[0],q[1];" - << "cz q[1],q[2];" - << "cx q[1],q[0];" - << "cx q[0],q[1];" - << "cx q[1],q[0];" - << "u3(-pi/4,-pi/4,-pi/4) q[2];" - << "u2(-5*pi/4,3*pi/4) q[1];" - << "sdg q[2];" - << "tdg q[0];" - << "ry(-pi/4) q[2];" - << "p(-0.1) q[1];" - << "rz(-0.1) q[1];" - << "rx(-pi/4) q[0];" - << "y q[0];" - << "x q[2];" - << "z q[1];" - << "cx q[0],q[1];" - << "h q[0];\n"; + << "OPENQASM 2.0;" << "include \"qelib1.inc\";" << "qreg q[3];" + << "sx q[0];" << "sxdg q[0];" << "h q[0];" << "cx q[0],q[1];" << "z q[1];" + << "x q[2];" << "y q[0];" << "rx(pi/4) q[0];" << "rz(0.1) q[1];" + << "p(0.1) q[1];" << "ry(pi/4) q[2];" << "t q[0];" << "s q[2];" + << "u2(pi/4, pi/4) q[1];" << "u3(pi/4, pi/4, pi/4) q[2];" + << "barrier q[0],q[1],q[2];" << "swap q[0],q[1];" << "cz q[1],q[2];" + << "cp(pi/4) q[0],q[1];" << "ctrl(2) @ x q[0],q[1],q[2];" + << "ctrl(2) @ z q[1],q[2],q[0];" << "cp(pi/2) q[0], q[1];" + << "cp(pi/4) q[0], q[1];" << "cp(pi/8) q[0], q[1];" + << "rzz(pi/4) q[0], q[1];" << "rxx(pi/4) q[0], q[1];" + << "ryy(pi/4) q[0], q[1];" << "rzx(pi/4) q[0], q[1];" << "ecr q[0], q[1];" + << "dcx q[0], q[1];" << "r(pi/8, pi/4) q[2];" << "r(-pi/8, pi/4) q[2];" + << "dcx q[1], q[0];" << "ecr q[0], q[1];" << "rzx(-pi/4) q[0], q[1];" + << "ryy(-pi/4) q[0], q[1];" << "rxx(-pi/4) q[0], q[1];" + << "rzz(-pi/4) q[0], q[1];" << "cp(-pi/8) q[0], q[1];" + << "cp(-pi/4) q[0], q[1];" << "cp(-pi/2) q[0], q[1];" + << "ctrl(2) @ z q[1],q[2],q[0];" << "ctrl(2) @ x q[0],q[1],q[2];" + << "cp(-pi/4) q[0],q[1];" << "cz q[1],q[2];" << "cx q[1],q[0];" + << "cx q[0],q[1];" << "cx q[1],q[0];" << "u3(-pi/4,-pi/4,-pi/4) q[2];" + << "u2(-5*pi/4,3*pi/4) q[1];" << "sdg q[2];" << "tdg q[0];" + << "ry(-pi/4) q[2];" << "p(-0.1) q[1];" << "rz(-0.1) q[1];" + << "rx(-pi/4) q[0];" << "y q[0];" << "x q[2];" << "z q[1];" + << "cx q[0],q[1];" << "h q[0];\n"; qc = qasm3::Importer::import(ss); EXPECT_TRUE(FunctionalityConstruction::transformableToZX(&qc)); @@ -193,7 +172,7 @@ TEST_F(ZXFunctionalityTest, nestedCompoundGate) { } TEST_F(ZXFunctionalityTest, Phase) { - using namespace qc::literals; + qc = qc::QuantumComputation(2); qc.p(PI / 4, 0); qc.cp(PI / 4, 1, 0); @@ -225,18 +204,149 @@ TEST_F(ZXFunctionalityTest, Compound) { EXPECT_TRUE(diag.isIdentity()); } -TEST_F(ZXFunctionalityTest, UnsupportedMultiControl) { - using namespace qc::literals; +TEST_F(ZXFunctionalityTest, CRZ) { + qc = qc::QuantumComputation(2); + qc.crz(PI / 2, 0, 1); + + auto qcPrime = qc::QuantumComputation(2); + + qcPrime.cx(0, 1); + qcPrime.rz(-PI / 4, 1); + qcPrime.cx(0, 1); + qcPrime.rz(PI / 4, 1); + + checkEquivalence(qc, qcPrime, {0, 1}); +} + +TEST_F(ZXFunctionalityTest, CCZ) { + + qc = qc::QuantumComputation(3); + qc.mcz({1, 2}, 0); + + auto qcPrime = qc::QuantumComputation(3); + qcPrime.h(0); + qcPrime.mcx({1, 2}, 0); + qcPrime.h(0); + + checkEquivalence(qc, qcPrime, {0, 1, 2}); +} + +TEST_F(ZXFunctionalityTest, MCX) { + qc = qc::QuantumComputation(4); qc.mcx({1, 2, 3}, 0); - EXPECT_FALSE(FunctionalityConstruction::transformableToZX(&qc)); - EXPECT_THROW(const ZXDiagram diag = - FunctionalityConstruction::buildFunctionality(&qc), - ZXException); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcx({3, 2, 1}, 0); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCX0) { + + qc = qc::QuantumComputation(1); + qc.mcx({}, 0); + + auto qcPrime = qc::QuantumComputation(1); + + qcPrime.x(0); + checkEquivalence(qc, qcPrime, {0}); +} + +TEST_F(ZXFunctionalityTest, MCX1) { + + qc = qc::QuantumComputation(2); + qc.mcx({1}, 0); + + auto qcPrime = qc::QuantumComputation(2); + + qcPrime.cx(1, 0); + + checkEquivalence(qc, qcPrime, {0, 1}); +} + +TEST_F(ZXFunctionalityTest, MCZ) { + + qc = qc::QuantumComputation(4); + qc.mcz({1, 2, 3}, 0); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcz({1, 2, 3}, 0); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCZ0) { + + qc = qc::QuantumComputation(1); + qc.mcz({}, 0); + + auto qcPrime = qc::QuantumComputation(1); + qcPrime.z(0); + + checkEquivalence(qc, qcPrime, {0}); +} + +TEST_F(ZXFunctionalityTest, MCZ1) { + + qc = qc::QuantumComputation(2); + qc.mcz({1}, 0); + + auto qcPrime = qc::QuantumComputation(2); + qcPrime.cz(1, 0); + + checkEquivalence(qc, qcPrime, {0, 1}); +} + +TEST_F(ZXFunctionalityTest, MCZ2) { + + qc = qc::QuantumComputation(4); + qc.mcz({1, 2}, 0); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.h(0); + qcPrime.mcx({1, 2}, 0); + qcPrime.h(0); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCRZ) { + + qc = qc::QuantumComputation(4); + qc.mcrz(PI / 4, {1, 2, 3}, 0); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.h(0); + qcPrime.mcrx(PI / 4, {1, 2, 3}, 0); + qcPrime.h(0); + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCRZ0) { + + qc = qc::QuantumComputation(1); + qc.mcrz(PI / 4, {}, 0); + + auto qcPrime = qc::QuantumComputation(1); + qcPrime.rz(PI / 4, 0); + + checkEquivalence(qc, qcPrime, {0}); +} + +TEST_F(ZXFunctionalityTest, MCRZ1) { + + qc = qc::QuantumComputation(2); + qc.mcrz(PI / 4, {1}, 0); + + auto qcPrime = qc::QuantumComputation(2); + qcPrime.crz(PI / 4, 1, 0); + + checkEquivalence(qc, qcPrime, {0, 1}); } TEST_F(ZXFunctionalityTest, UnsupportedControl) { - using namespace qc::literals; + qc = qc::QuantumComputation(2); qc.cy(1, 0); EXPECT_FALSE(FunctionalityConstruction::transformableToZX(&qc)); @@ -246,7 +356,7 @@ TEST_F(ZXFunctionalityTest, UnsupportedControl) { } TEST_F(ZXFunctionalityTest, UnsupportedControl2) { - using namespace qc::literals; + qc = qc::QuantumComputation(3); qc.mcy({1, 2}, 0); EXPECT_FALSE(FunctionalityConstruction::transformableToZX(&qc)); @@ -255,6 +365,26 @@ TEST_F(ZXFunctionalityTest, UnsupportedControl2) { ZXException); } +TEST_F(ZXFunctionalityTest, UnsupportedControl3) { + + qc = qc::QuantumComputation(4); + qc.mcy({1, 2, 3}, 0); + EXPECT_FALSE(FunctionalityConstruction::transformableToZX(&qc)); + EXPECT_THROW(const ZXDiagram diag = + FunctionalityConstruction::buildFunctionality(&qc), + ZXException); +} + +TEST_F(ZXFunctionalityTest, UnsupportedTwoTargetControl) { + + qc = qc::QuantumComputation(4); + qc.mcdcx({2, 3}, 0, 1); + EXPECT_FALSE(FunctionalityConstruction::transformableToZX(&qc)); + EXPECT_THROW(const ZXDiagram diag = + FunctionalityConstruction::buildFunctionality(&qc), + ZXException); +} + TEST_F(ZXFunctionalityTest, InitialLayout) { qc = qc::QuantumComputation(2); qc::Permutation layout{}; @@ -268,13 +398,7 @@ TEST_F(ZXFunctionalityTest, InitialLayout) { qcPrime.x(1); qcPrime.z(0); - auto d = FunctionalityConstruction::buildFunctionality(&qc); - auto dPrime = FunctionalityConstruction::buildFunctionality(&qcPrime); - - d.concat(dPrime); - - fullReduce(d); - EXPECT_TRUE(d.isIdentity()); + checkEquivalence(qc, qcPrime, {0, 1}); } TEST_F(ZXFunctionalityTest, FromSymbolic) { @@ -295,21 +419,13 @@ TEST_F(ZXFunctionalityTest, RZ) { qc.rz(PI / 8, 0); auto qcPrime = qc::QuantumComputation(1); - qcPrime.p(PI / 8, 0); - - auto d = FunctionalityConstruction::buildFunctionality(&qc); - auto dPrime = FunctionalityConstruction::buildFunctionality(&qcPrime); + qcPrime.rz(PI / 8, 0); - d.concat(dPrime.invert()); - - fullReduce(d); - EXPECT_FALSE(d.isIdentity()); - EXPECT_FALSE(d.globalPhaseIsZero()); - EXPECT_TRUE(d.connected(d.getInput(0), d.getOutput(0))); + checkEquivalence(qc, qcPrime, {0}); } TEST_F(ZXFunctionalityTest, ISWAP) { - using namespace qc::literals; + qc = qc::QuantumComputation(2); qc.iswap(0, 1); @@ -319,17 +435,9 @@ TEST_F(ZXFunctionalityTest, ISWAP) { qcPrime.h(0); qcPrime.cx(0, 1); qcPrime.cx(1, 0); - qc.h(1); + qcPrime.h(1); - auto d = FunctionalityConstruction::buildFunctionality(&qc); - auto dPrime = FunctionalityConstruction::buildFunctionality(&qcPrime); - - d.concat(dPrime.invert()); - - fullReduce(d); - EXPECT_TRUE(d.isIdentity()); - EXPECT_TRUE(d.globalPhaseIsZero()); - EXPECT_TRUE(d.connected(d.getInput(0), d.getOutput(0))); + checkEquivalence(qc, qcPrime, {0, 1}); } TEST_F(ZXFunctionalityTest, XXplusYY) { @@ -355,17 +463,7 @@ TEST_F(ZXFunctionalityTest, XXplusYY) { qcPrime.rz(qc::PI_2, 0); qcPrime.rz(-beta, 1); - auto d = FunctionalityConstruction::buildFunctionality(&qc); - - auto dPrime = FunctionalityConstruction::buildFunctionality(&qcPrime); - - d.concat(dPrime.invert()); - - fullReduce(d); - - EXPECT_TRUE(d.isIdentity()); - EXPECT_TRUE(d.globalPhaseIsZero()); - EXPECT_TRUE(d.connected(d.getInput(0), d.getOutput(0))); + checkEquivalence(qc, qcPrime, {0, 1}); } TEST_F(ZXFunctionalityTest, XXminusYY) { @@ -391,16 +489,191 @@ TEST_F(ZXFunctionalityTest, XXminusYY) { qcPrime.rz(qc::PI_2, 0); qcPrime.rz(beta, 1); - auto d = FunctionalityConstruction::buildFunctionality(&qc); + checkEquivalence(qc, qcPrime, {0, 1}); +} + +TEST_F(ZXFunctionalityTest, SWAP) { + qc = qc::QuantumComputation(2); + qc.swap(0, 1); + + auto qcPrime = qc::QuantumComputation(2); + qcPrime.cx(1, 0); + qcPrime.cx(0, 1); + qcPrime.cx(1, 0); + + checkEquivalence(qc, qcPrime, {0, 1}); +} - auto dPrime = FunctionalityConstruction::buildFunctionality(&qcPrime); +TEST_F(ZXFunctionalityTest, CSWAP) { + qc = qc::QuantumComputation(3); + qc.mcswap({0}, 1, 2); + + auto qcPrime = qc::QuantumComputation(3); + qcPrime.cx(1, 2); + qcPrime.mcx({0, 2}, 1); + qcPrime.cx(1, 2); + + checkEquivalence(qc, qcPrime, {0, 1, 2}); +} + +TEST_F(ZXFunctionalityTest, MCSWAP) { + qc = qc::QuantumComputation(4); + qc.mcswap({0, 1}, 2, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.cx(2, 3); + qcPrime.mcx({0, 1, 3}, 2); + qcPrime.cx(2, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCRzz) { + qc = qc::QuantumComputation(4); + qc.mcrzz(qc::PI_2 / 3, {0, 1}, 2, 3); + qc.mcrzz(2 * qc::PI, {0, 1}, 2, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcrzz(qc::PI_2 / 3, {0, 1}, 2, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCRxx) { + qc = qc::QuantumComputation(4); + qc.mcrxx(qc::PI_2 / 3, {0, 1}, 2, 3); + qc.mcrxx(2 * qc::PI, {0, 1}, 2, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcrxx(qc::PI_2 / 3, {0, 1}, 2, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCRzx) { + qc = qc::QuantumComputation(4); + qc.mcrzx(qc::PI_2 / 3, {0, 1}, 2, 3); + qc.mcrzx(2 * qc::PI, {0, 1}, 2, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcrzx(qc::PI_2 / 3, {0, 1}, 2, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCRx0) { + qc = qc::QuantumComputation(1); + qc.mcrx(PI / 4, {}, 0); - d.concat(dPrime.invert()); + auto qcPrime = qc::QuantumComputation(1); + qcPrime.mcrx(PI / 4, {}, 0); + checkEquivalence(qc, qcPrime, {0}); +} + +TEST_F(ZXFunctionalityTest, MCRx1) { + qc = qc::QuantumComputation(2); + qc.mcrx(PI / 4, {0}, 1); + + auto qcPrime = qc::QuantumComputation(2); + qcPrime.mcrx(PI / 4, {0}, 1); + checkEquivalence(qc, qcPrime, {0, 1}); +} + +TEST_F(ZXFunctionalityTest, MCRx2) { + qc = qc::QuantumComputation(3); + qc.mcrx(PI / 4, {0, 1}, 2); + + auto qcPrime = qc::QuantumComputation(3); + qcPrime.mcrx(PI / 4, {0, 1}, 2); + checkEquivalence(qc, qcPrime, {0, 1, 2}); +} + +TEST_F(ZXFunctionalityTest, MCRx3) { + qc = qc::QuantumComputation(4); + qc.mcrx(PI / 4, {0, 1, 2}, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcrx(PI / 4, {0, 1, 2}, 3); + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCPhase) { + qc = qc::QuantumComputation(4); + qc.mcp(PI / 2, {0, 1, 2}, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcp(PI / 2, {0, 1, 2}, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCS) { + qc = qc::QuantumComputation(4); + qc.mcs({0, 1, 2}, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcs({0, 1, 2}, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCSdg) { + qc = qc::QuantumComputation(4); + qc.mcsdg({0, 1, 2}, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcsdg({0, 1, 2}, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCT) { + qc = qc::QuantumComputation(4); + qc.mct({0, 1, 2}, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mct({0, 1, 2}, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCTdg) { + qc = qc::QuantumComputation(4); + qc.mctdg({0, 1, 2}, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mctdg({0, 1, 2}, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCPhase2) { + qc = qc::QuantumComputation(3); + qc.mcp(PI / 2, {0, 1}, 2); + + auto qcPrime = qc::QuantumComputation(3); + qcPrime.mcp(PI / 2, {0, 1}, 2); + + checkEquivalence(qc, qcPrime, {0, 1, 2}); +} + +TEST_F(ZXFunctionalityTest, MCS2) { + qc = qc::QuantumComputation(3); + qc.mcs({0, 1}, 2); + + auto qcPrime = qc::QuantumComputation(3); + qcPrime.mcs({0, 1}, 2); + + checkEquivalence(qc, qcPrime, {0, 1, 2}); +} + +TEST_F(ZXFunctionalityTest, MCT2) { + qc = qc::QuantumComputation(3); + qc.mct({0, 1}, 2); - fullReduce(d); + auto qcPrime = qc::QuantumComputation(3); + qcPrime.mct({0, 1}, 2); - EXPECT_TRUE(d.isIdentity()); - EXPECT_TRUE(d.globalPhaseIsZero()); - EXPECT_TRUE(d.connected(d.getInput(0), d.getOutput(0))); + checkEquivalence(qc, qcPrime, {0, 1, 2}); } } // namespace zx From b4fc18618027da373ca51e7f5dd62ea55d4f5bd9 Mon Sep 17 00:00:00 2001 From: J4MMlE <113241498+J4MMlE@users.noreply.github.com> Date: Mon, 20 Apr 2026 23:47:30 +0200 Subject: [PATCH 13/29] =?UTF-8?q?=E2=9C=A8=20Add=20rotation-gate=20merging?= =?UTF-8?q?=20using=20quaternions=20(#1407)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR extends the rotation merging pass in the QCO ~~MQTOpt~~ dialect to support quaternion-based gate fusion. The existing rotation merge pass only merges consecutive rotation gates of the **same type** (e.g., `rx + rx` or `ry + ry`) by adding their angles. This PR introduces quaternion-based merging, which can merge rotation gates of different types (currently only single qubit gates `rx`, `ry`, `rz`, `r`, `p`, `u2`, `u`). Quaternions are widely used to represent rotations in three-dimensional space and naturally map to qubit gate rotations around the Bloch sphere. The implementation: 1. Converts rotation gates to quaternion representation 2. Multiplies quaternions using the Hamilton product 3. Converts the resulting quaternion back to a `u` gate. (This could also be done differently in the future, and directly decompose to the correct base gates by using the decomposition from #1182) This optimization is enabled by default but can be **disabled** by adding this flag to the compiler invocation: ```bash mqt-cc --disable-merge-single-qubit-rotation-gates ``` Closes #1029 ## Checklist: - [x] The pull request only contains commits that are focused and relevant to this change. - [x] I have added appropriate tests that cover the new/changed functionality. - [x] I have updated the documentation to reflect these changes. - [x] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [x] ~~I have added migration instructions to the upgrade guide (if needed).~~ - [x] The changes follow the project's style guidelines and introduce no new warnings. - [x] The changes are fully tested and pass the CI checks. - [x] I have reviewed my own code changes. --------- Signed-off-by: burgholzer Signed-off-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Co-authored-by: Matthias Reumann Co-authored-by: burgholzer Co-authored-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com> --- CHANGELOG.md | 3 + mlir/include/mlir/Compiler/CompilerPipeline.h | 3 + .../mlir/Dialect/QCO/Transforms/Passes.td | 29 + mlir/lib/Compiler/CompilerPipeline.cpp | 9 +- .../lib/Dialect/QCO/Transforms/CMakeLists.txt | 2 + .../MergeSingleQubitRotationGates.cpp | 683 +++++++++++++++ mlir/tools/mqt-cc/mqt-cc.cpp | 7 + .../Compiler/test_compiler_pipeline.cpp | 32 +- .../Dialect/QCO/Transforms/CMakeLists.txt | 1 + .../QCO/Transforms/Mapping/CMakeLists.txt | 4 +- .../Transforms/Optimizations/CMakeLists.txt | 26 + ...te_expected_merge_single_qubit_rotation.py | 208 +++++ .../test_qco_merge_single_qubit_rotation.cpp | 810 ++++++++++++++++++ pyproject.toml | 1 + 14 files changed, 1812 insertions(+), 6 deletions(-) create mode 100644 mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp create mode 100644 mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt create mode 100644 mlir/unittests/Dialect/QCO/Transforms/Optimizations/compute_expected_merge_single_qubit_rotation.py create mode 100644 mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 91cf031f82..3b1dfddcc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Added +- ✨ Add a `merge-single-qubit-rotation-gates` pass for merging consecutive rotation gates using quaternions ([#1407]) ([**@J4MMlE**]) - ✨ Add support for multi-controlled gates to ZX package ([#1380]) ([**@keefehuang**]) - ✨ Add Sampler and Estimator Primitives to the QDMI-Qiskit Interface ([#1507]) ([**@marcelwa**]) - ✨ Add conversions between `jeff` and QCO ([#1479], [#1548], [#1565], [#1637]) ([**@denialhaag**]) @@ -396,6 +397,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [#1413]: https://github.com/munich-quantum-toolkit/core/pull/1413 [#1412]: https://github.com/munich-quantum-toolkit/core/pull/1412 [#1411]: https://github.com/munich-quantum-toolkit/core/pull/1411 +[#1407]: https://github.com/munich-quantum-toolkit/core/pull/1407 [#1406]: https://github.com/munich-quantum-toolkit/core/pull/1406 [#1403]: https://github.com/munich-quantum-toolkit/core/pull/1403 [#1402]: https://github.com/munich-quantum-toolkit/core/pull/1402 @@ -554,6 +556,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [**@Ectras**]: https://github.com/Ectras [**@simon1hofmann**]: https://github.com/simon1hofmann [**@keefehuang**]: https://github.com/keefehuang +[**@J4MMlE**]: https://github.com/J4MMlE diff --git a/mlir/include/mlir/Compiler/CompilerPipeline.h b/mlir/include/mlir/Compiler/CompilerPipeline.h index f43bac99d7..d15585827f 100644 --- a/mlir/include/mlir/Compiler/CompilerPipeline.h +++ b/mlir/include/mlir/Compiler/CompilerPipeline.h @@ -40,6 +40,9 @@ struct QuantumCompilerConfig { /// Print IR after each stage bool printIRAfterAllStages = false; + + /// Disable quaternion-based single-qubit rotation gate merging + bool disableMergeSingleQubitRotationGates = false; }; /** diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index 02e8f42cb4..276d0ba294 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -11,6 +11,35 @@ include "mlir/Pass/PassBase.td" +def MergeSingleQubitRotationGates + : Pass<"merge-single-qubit-rotation-gates", "mlir::ModuleOp"> { + let dependentDialects = ["mlir::qco::QCODialect", + "::mlir::arith::ArithDialect", + "::mlir::math::MathDialect"]; + let summary = "Merge rotation gates using quaternion-based fusion"; + let description = [{ + Merges consecutive single-qubit rotation gates acting on the same qubit into a single equivalent U gate, reducing circuit depth and gate count. + + Supported gate types: `rx`, `ry`, `rz`, `p`, `r`, `u2`, `u`. + + The pass greedily collects the longest possible chain of consecutive mergeable gates. + Each gate is converted to a unit quaternion: + + - `rx`, `ry`, `rz`, `p`: single-axis rotations via half-angle formulas. + - `r(theta, phi)`: rotation by `theta` around axis `(cos(phi), sin(phi), 0)`. + - `u2(phi, lambda) = u(pi / 2, phi, lambda)`. + - `u(theta, phi, lambda)`: ZYZ decomposition `rz(phi) * ry(theta) * rz(lambda)`, each factor converted to a quaternion and merged via the Hamilton product. + + The gates are then folded one by one via the Hamilton product into a single quaternion, which is decomposed back into ZYZ Euler angles and emitted as a single `UOp`, representing the same rotation as the chain of single gates. + The global phase of each gate is tracked alongside and combined together. + + The emitted `UOp` is defined by $U = \exp [i (\phi + \lambda) / 2] R_z (\phi) R_y (\theta) R_z (\lambda)$. + Each merge emits a `GPhaseOp` carrying the accumulated input phase of the chain. + Because the synthesized `UOp` introduces an additional intrinsic phase of $(\phi + \lambda) / 2$, the `GPhaseOp` must compensate for it. + This applies even to chains composed entirely of $\mathrm{SU} (2)$ gates (`rx`, `ry`, `rz`, `r`) because the synthesis into a `UOp` still produces the intrinsic phase term. + }]; +} + //===----------------------------------------------------------------------===// // Transpilation Passes //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index 0f67e5ae10..3ca8becf2e 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -13,6 +13,7 @@ #include "mlir/Conversion/QCOToQC/QCOToQC.h" #include "mlir/Conversion/QCToQCO/QCToQCO.h" #include "mlir/Conversion/QCToQIR/QCToQIR.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Support/Passes.h" #include "mlir/Support/PrettyPrinting.h" @@ -138,9 +139,11 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, // auto arch = std::make_shared("RigettiNovera", 9, COUPLING); // Stage 5: Optimization passes - // TODO: Add optimization passes - if (failed( - runStage([&](PassManager& pm) { populateQCOCleanupPipeline(pm); }))) { + if (failed(runStage([&](PassManager& pm) { + if (!config_.disableMergeSingleQubitRotationGates) { + pm.addPass(qco::createMergeSingleQubitRotationGates()); + } + }))) { return failure(); } if (record != nullptr && config_.recordIntermediates) { diff --git a/mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt b/mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt index 2268584167..fc6ee74b9d 100644 --- a/mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt +++ b/mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt @@ -15,6 +15,8 @@ add_mlir_library( PRIVATE MLIRQCODialect MLIRQCOUtils + MLIRArithDialect + MLIRMathDialect DEPENDS MLIRQCOTransformsIncGen) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp new file mode 100644 index 0000000000..fd4b44f291 --- /dev/null +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp @@ -0,0 +1,683 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace mlir::qco { + +#define GEN_PASS_DEF_MERGESINGLEQUBITROTATIONGATES +#include "mlir/Dialect/QCO/Transforms/Passes.h.inc" + +namespace { + +/** + * @brief Pattern that merges consecutive rotation gates using quaternion + * multiplication. + */ +struct MergeSingleQubitRotationGatesPattern final + : OpInterfaceRewritePattern { + explicit MergeSingleQubitRotationGatesPattern(MLIRContext* context) + : OpInterfaceRewritePattern(context) {} + + /// Quaternion representation (w + xi + yj + zk) using MLIR Values. + struct Quaternion { + Value w; + Value x; + Value y; + Value z; + }; + + /// Axis of a single-axis rotation gate. + enum class RotationAxis : std::uint8_t { X, Y, Z }; + + /// Cached frequently-used constant Values. + struct Constants { + Value negOne; + Value zero; + Value one; + Value two; + Value eps; + Value pi; + }; + + /// Euler-angle triple for a U gate (theta, phi, lambda). + struct UOpAngles { + Value theta; + Value phi; + Value lambda; + }; + + /// Returns whether an operation is considered mergeable + static bool isMergeable(Operation* op) { + return isa(op); + } + + /// Checks if two gates a and b are mergeable via quaternion-based merging. + [[nodiscard]] static bool areQuaternionMergeable(Operation& a, Operation& b) { + return isMergeable(&a) && isMergeable(&b); + } + + /** + * @brief Returns the rotation axis for an RXOp, RYOp, or RZOp. + * + * @param op The operation to query + * @return The rotation axis, or std::nullopt if the operation is not + * RXOp, RYOp, or RZOp. + */ + static std::optional getRotationAxis(Operation* op) { + return llvm::TypeSwitch>(op) + .Case([](auto) { return RotationAxis::X; }) + .Case([](auto) { return RotationAxis::Y; }) + .Case([](auto) { return RotationAxis::Z; }) + .Default([](auto) { return std::nullopt; }); + } + + /** + * @brief Creates shared f64 arithmetic constants used throughout the pass. + * + * These constants are created once and reused across quaternion construction, + * Hamilton product, and Euler angle extraction to avoid redundant ops in the + * generated IR. + * + * @param loc Source location for the created operations + * @param rewriter Pattern rewriter for creating new operations + * @return A Constants struct with all pre-built constant ops + */ + static Constants createConstants(Location loc, PatternRewriter& rewriter) { + return { + .negOne = arith::ConstantFloatOp::create( + rewriter, loc, rewriter.getF64Type(), APFloat(-1.0)), + .zero = arith::ConstantFloatOp::create( + rewriter, loc, rewriter.getF64Type(), APFloat(0.0)), + .one = arith::ConstantFloatOp::create( + rewriter, loc, rewriter.getF64Type(), APFloat(1.0)), + .two = arith::ConstantFloatOp::create( + rewriter, loc, rewriter.getF64Type(), APFloat(2.0)), + // Tolerance for gimbal-lock detection in quaternion-to-Euler + // conversion. Value from reference implementation: + // https://github.com/evbernardes/quaternion_to_euler/blob/main/euler_from_quat.py + .eps = arith::ConstantFloatOp::create( + rewriter, loc, rewriter.getF64Type(), APFloat(1e-12)), + .pi = arith::ConstantFloatOp::create( + rewriter, loc, rewriter.getF64Type(), APFloat(std::numbers::pi)), + }; + } + + /** + * @brief Normalizes an angle to the range [-PI, PI]. + * + * Uses floor-based modular arithmetic: + * normalize(a) = a - floor((a + π) / 2π) * 2π + * + * @param angle The angle value to normalize + * @param loc Source location for the created operations + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return The normalized angle value + */ + static Value normalizeAngle(Value angle, Location loc, + const Constants& constants, + PatternRewriter& rewriter) { + auto twoPi = + arith::MulFOp::create(rewriter, loc, constants.two, constants.pi); + auto shifted = arith::AddFOp::create(rewriter, loc, angle, constants.pi); + auto divided = arith::DivFOp::create(rewriter, loc, shifted, twoPi); + auto floored = math::FloorOp::create(rewriter, loc, divided); + auto multiple = arith::MulFOp::create(rewriter, loc, floored, twoPi); + return arith::SubFOp::create(rewriter, loc, angle, multiple); + } + + /** + * @brief Converts a single-axis rotation to quaternion representation. + * + * Uses half-angle formulas: + * RX(a) = Q(cos(a/2), sin(a/2), 0, 0) + * RY(a) = Q(cos(a/2), 0, sin(a/2), 0) + * RZ(a) = Q(cos(a/2), 0, 0, sin(a/2)) + * + * @see + * https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles + * @param angle The rotation angle + * @param axis The rotation axis (X, Y, or Z) + * @param loc Location in the IR + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return Quaternion representing the rotation + */ + static Quaternion createAxisQuaternion(Value angle, RotationAxis axis, + Location loc, + const Constants& constants, + PatternRewriter& rewriter) { + auto half = arith::DivFOp::create(rewriter, loc, angle, constants.two); + // cos(angle/2) + auto cos = math::CosOp::create(rewriter, loc, half); + // sin(angle/2) + auto sin = math::SinOp::create(rewriter, loc, half); + + switch (axis) { + case RotationAxis::X: + return {.w = cos, .x = sin, .y = constants.zero, .z = constants.zero}; + case RotationAxis::Y: + return {.w = cos, .x = constants.zero, .y = sin, .z = constants.zero}; + case RotationAxis::Z: + return {.w = cos, .x = constants.zero, .y = constants.zero, .z = sin}; + } + } + + /** + * @brief Converts a ZYZ Euler angle decomposition to quaternion. + * + * U(theta, phi, lambda) uses ZYZ decomposition: RZ(lambda) -> RY(theta) -> + * RZ(phi). + * + * When composing rotations, quaternion multiplication follows matrix + * multiplication order (right-to-left), which is the reverse of the + * application sequence: + * Sequential application: RZ(lambda), then RY(theta), then RZ(phi) + * Quaternion product: qPhi * qTheta * qLambda + * + * @note U is defined as P(phi)*RY(theta)*P(lambda), which equals + * e^{i*(phi+lambda)/2} * RZ(phi)*RY(theta)*RZ(lambda). + * Since quaternions represent SU(2), this pass works with the SU(2) part + * RZ(phi)*RY(theta)*RZ(lambda) and tracks the factored-out global phase + * (phi+lambda)/2 separately via globalPhaseOf. + * + * @param theta The Y-rotation angle + * @param phi The first Z-rotation angle + * @param lambda The second Z-rotation angle + * @param loc Location in the IR + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return Quaternion representing the ZYZ rotation + */ + static Quaternion quaternionFromZYZ(Value theta, Value phi, Value lambda, + Location loc, const Constants& constants, + PatternRewriter& rewriter) { + auto qTheta = + createAxisQuaternion(theta, RotationAxis::Y, loc, constants, rewriter); + auto qPhi = + createAxisQuaternion(phi, RotationAxis::Z, loc, constants, rewriter); + auto qLambda = + createAxisQuaternion(lambda, RotationAxis::Z, loc, constants, rewriter); + + // qPhi * qTheta * qLambda (multiplication in reverse order!) + auto temp = hamiltonProduct(qPhi, qTheta, loc, rewriter); + return hamiltonProduct(temp, qLambda, loc, rewriter); + } + + /** + * @brief Converts a UOp to quaternion representation. + * + * U(theta, phi, lambda) is decomposed via ZYZ Euler angles. + * + * @note Global phase is discarded; see quaternionFromZYZ for details. + * + * @param op The UOp to convert + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return Quaternion representing the UOp + */ + static Quaternion quaternionFromUOp(UOp op, const Constants& constants, + PatternRewriter& rewriter) { + return quaternionFromZYZ(op.getParameter(0), op.getParameter(1), + op.getParameter(2), op->getLoc(), constants, + rewriter); + } + + /** + * @brief Converts a U2Op to quaternion representation. + * + * U2(phi, lambda) = U(pi / 2, phi, lambda), using ZYZ decomposition with + * theta = pi / 2. + * + * @note Global phase is discarded; see quaternionFromZYZ for details. + * + * @param op The U2Op to convert + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return Quaternion representing the U2Op + */ + static Quaternion quaternionFromU2Op(U2Op op, const Constants& constants, + PatternRewriter& rewriter) { + auto loc = op->getLoc(); + auto piHalf = + arith::DivFOp::create(rewriter, loc, constants.pi, constants.two); + return quaternionFromZYZ(piHalf, op.getParameter(0), op.getParameter(1), + loc, constants, rewriter); + } + + /** + * @brief Converts an ROp to quaternion representation. + * + * R(theta, phi) represents a rotation by theta around axis + * (cos(phi), sin(phi), 0) in the XY plane: + * Q(cos(theta / 2), sin(theta / 2) * cos(phi), sin(theta / 2) * sin(phi), 0) + * + * @param op The ROp to convert + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return Quaternion representing the ROp + */ + static Quaternion quaternionFromROp(ROp op, const Constants& constants, + PatternRewriter& rewriter) { + auto loc = op->getLoc(); + auto theta = op.getParameter(0); + auto phi = op.getParameter(1); + + auto halfTheta = arith::DivFOp::create(rewriter, loc, theta, constants.two); + auto cosHalf = math::CosOp::create(rewriter, loc, halfTheta); + auto sinHalf = math::SinOp::create(rewriter, loc, halfTheta); + auto cosPhi = math::CosOp::create(rewriter, loc, phi); + auto sinPhi = math::SinOp::create(rewriter, loc, phi); + + auto x = arith::MulFOp::create(rewriter, loc, sinHalf, cosPhi); + auto y = arith::MulFOp::create(rewriter, loc, sinHalf, sinPhi); + + return {.w = cosHalf, .x = x, .y = y, .z = constants.zero}; + } + + /** + * @brief Converts a rotation gate to quaternion representation. + * + * @note Global phase is discarded; see quaternionFromZYZ for details. + * + * @param op The rotation gate to convert (RXOp, RYOp, RZOp, POp, ROp, U2Op, + * UOp) + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return Quaternion representing the rotation gate + */ + static Quaternion quaternionFromRotation(UnitaryOpInterface op, + const Constants& constants, + PatternRewriter& rewriter) { + // Single-axis rotations (RX, RY, RZ, P) share the same conversion pattern + if (auto axis = getRotationAxis(op.getOperation())) { + return createAxisQuaternion(op.getParameter(0), *axis, op->getLoc(), + constants, rewriter); + } + + // Multi-parameter gates each need their own conversion + return llvm::TypeSwitch(op.getOperation()) + .Case( + [&](ROp o) { return quaternionFromROp(o, constants, rewriter); }) + .Case( + [&](U2Op o) { return quaternionFromU2Op(o, constants, rewriter); }) + .Case( + [&](UOp o) { return quaternionFromUOp(o, constants, rewriter); }) + .Default([](auto) -> Quaternion { + llvm_unreachable("Unsupported operation type"); + }); + } + + /** + * @brief Returns the global phase contribution of a rotation gate. + * + * Rotation gates can be factored as U = e^{i * phase} * SU(2), where SU(2) + * is the quaternion-representable part and phase is the global phase. This + * function returns the global phase for each gate type: + * + * - RX, RY, RZ, R -> none (already SU(2), no global phase) + * - P(theta) -> theta / 2 (P = e^{i * theta / 2} * RZ(theta)) + * - U(theta, phi, lambda) -> (phi + lambda) / 2 + * - U2(phi, lambda) -> (phi + lambda) / 2 + * + * @param op The rotation gate to query + * @param constants Pre-created arithmetic constants + * @param loc Source location for created operations + * @param rewriter Pattern rewriter for creating new operations + * @return The global phase as a Value, or std::nullopt for SU(2) gates + */ + static std::optional globalPhaseOf(UnitaryOpInterface op, + const Constants& constants, + Location loc, + PatternRewriter& rewriter) { + return llvm::TypeSwitch>(op.getOperation()) + .Case( + [&](auto) -> std::optional { return std::nullopt; }) + .Case([&](auto) -> std::optional { + return arith::DivFOp::create(rewriter, loc, op.getParameter(0), + constants.two); + }) + .Case([&](auto) -> std::optional { + // phi is at different indexes for UOp and U2Op + auto phiIdx = isa(op.getOperation()) ? 1U : 0U; + auto sum = + arith::AddFOp::create(rewriter, loc, op.getParameter(phiIdx), + op.getParameter(phiIdx + 1)); + return arith::DivFOp::create(rewriter, loc, sum, constants.two); + }) + .Default([](auto) -> std::optional { + llvm_unreachable("Unsupported operation type"); + }); + } + + /** + * @brief Checks if this op is the start of a mergeable chain. + * + * A chain start is a mergeable op whose qubit input does NOT come from + * a chain-compatible predecessor. This ensures the greedy rewriter only + * triggers the rewrite at chain heads, building the maximal chain in one + * shot regardless of worklist order. + * + * @param op The operation to check + * @return True if this op is the start of a chain + */ + static bool isChainStart(UnitaryOpInterface op) { + if (!isMergeable(op.getOperation())) { + return false; + } + auto input = op.getInputQubit(0); + auto* defOp = input.getDefiningOp(); + return defOp == nullptr || + !areQuaternionMergeable(*defOp, *op.getOperation()); + } + + /** + * @brief Collects a chain of consecutive mergeable gates. + * + * Walks forward via single-use SSA edges. Breaks when the next operation is + * not considered as mergeable. + * + * @param start The chain head (must satisfy isChainStart) + * @return The chain of operations in circuit order (first applied to last) + */ + static SmallVector + collectChain(UnitaryOpInterface start) { + SmallVector chain = {start}; + auto current = start; + while (true) { + auto* userOp = *current->getUsers().begin(); + if (!areQuaternionMergeable(*current.getOperation(), *userOp)) { + break; + } + current = chain.emplace_back(cast(userOp)); + } + return chain; + } + + /** + * @brief Computes the Hamilton product of two quaternions (q1 * q2). + * + * For q1 = w1 + x1*i + y1*j + z1*k and q2 = w2 + x2*i + y2*j + z2*k: + * + * q1 * q2 = (w1w2 - x1x2 - y1y2 - z1z2) + * + (w1x2 + x1w2 + y1z2 - z1y2) * i + * + (w1y2 - x1z2 + y1w2 + z1x2) * j + * + (w1z2 + x1y2 - y1x2 + z1w2) * k + * + * @see https://en.wikipedia.org/wiki/Quaternion#Hamilton_product + * @param q1 The first quaternion + * @param q2 The second quaternion + * @param loc Location in the IR + * @param rewriter Pattern rewriter for creating new operations + * @return Product quaternion + */ + static Quaternion hamiltonProduct(Quaternion q1, Quaternion q2, Location loc, + PatternRewriter& rewriter) { + // wRes = w1w2 - x1x2 - y1y2 - z1z2 + auto w1w2 = arith::MulFOp::create(rewriter, loc, q1.w, q2.w); + auto x1x2 = arith::MulFOp::create(rewriter, loc, q1.x, q2.x); + auto y1y2 = arith::MulFOp::create(rewriter, loc, q1.y, q2.y); + auto z1z2 = arith::MulFOp::create(rewriter, loc, q1.z, q2.z); + auto wTemp1 = arith::SubFOp::create(rewriter, loc, w1w2, x1x2); + auto wTemp2 = arith::SubFOp::create(rewriter, loc, wTemp1, y1y2); + auto wRes = arith::SubFOp::create(rewriter, loc, wTemp2, z1z2); + + // xRes = w1x2 + x1w2 + y1z2 - z1y2 + auto w1x2 = arith::MulFOp::create(rewriter, loc, q1.w, q2.x); + auto x1w2 = arith::MulFOp::create(rewriter, loc, q1.x, q2.w); + auto y1z2 = arith::MulFOp::create(rewriter, loc, q1.y, q2.z); + auto z1y2 = arith::MulFOp::create(rewriter, loc, q1.z, q2.y); + auto xTemp1 = arith::AddFOp::create(rewriter, loc, w1x2, x1w2); + auto xTemp2 = arith::AddFOp::create(rewriter, loc, xTemp1, y1z2); + auto xRes = arith::SubFOp::create(rewriter, loc, xTemp2, z1y2); + + // yRes = w1y2 - x1z2 + y1w2 + z1x2 + auto w1y2 = arith::MulFOp::create(rewriter, loc, q1.w, q2.y); + auto x1z2 = arith::MulFOp::create(rewriter, loc, q1.x, q2.z); + auto y1w2 = arith::MulFOp::create(rewriter, loc, q1.y, q2.w); + auto z1x2 = arith::MulFOp::create(rewriter, loc, q1.z, q2.x); + auto yTemp1 = arith::SubFOp::create(rewriter, loc, w1y2, x1z2); + auto yTemp2 = arith::AddFOp::create(rewriter, loc, yTemp1, y1w2); + auto yRes = arith::AddFOp::create(rewriter, loc, yTemp2, z1x2); + + // zRes = w1z2 + x1y2 - y1x2 + z1w2 + auto w1z2 = arith::MulFOp::create(rewriter, loc, q1.w, q2.z); + auto x1y2 = arith::MulFOp::create(rewriter, loc, q1.x, q2.y); + auto y1x2 = arith::MulFOp::create(rewriter, loc, q1.y, q2.x); + auto z1w2 = arith::MulFOp::create(rewriter, loc, q1.z, q2.w); + auto zTemp1 = arith::AddFOp::create(rewriter, loc, w1z2, x1y2); + auto zTemp2 = arith::SubFOp::create(rewriter, loc, zTemp1, y1x2); + auto zRes = arith::AddFOp::create(rewriter, loc, zTemp2, z1w2); + + return {.w = wRes, .x = xRes, .y = yRes, .z = zRes}; + } + + /** + * @brief Extracts ZYZ Euler angles from a unit quaternion. + * + * For unit quaternion q = w + x * i + y * j + z * k, extracts UOp parameters: + * + * - alpha = atan2(z, w) + atan2(-x, y) + * - beta = acos(2 * (w^2 + z^2) - 1) + * - gamma = atan2(z, w) - atan2(-x, y) + * + * Based on Bernardes & Viollet (2022), simplified for unit quaternions and + * proper ZYZ Euler angles (Chapter 3.3): + * https://doi.org/10.1371/journal.pone.0276302 + * + * Reference implementation: + * https://github.com/evbernardes/quaternion_to_euler + * SymPy also implements this paper: + * https://docs.sympy.org/latest/modules/algebras.html#sympy.algebras.Quaternion.to_euler + * + * @note Floating-point errors may accumulate when merging many gates. + * @param q The quaternion to convert + * @param loc Source location for the created operations + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return UOpAngles {theta, phi, lambda} suitable for UOp::create + */ + static UOpAngles anglesFromQuaternion(Quaternion q, Location loc, + const Constants& constants, + PatternRewriter& rewriter) { + // Calculate angle beta (for y-rotation) + // beta = acos(2 * (w^2 + z^2) - 1) + // NOTE: the term (2 * (w^2 + z^2) - 1) is clamped to [-1, 1], + // otherwise acos could produce NaN. + auto ww = arith::MulFOp::create(rewriter, loc, q.w, q.w); + auto zz = arith::MulFOp::create(rewriter, loc, q.z, q.z); + auto bTemp1 = arith::AddFOp::create(rewriter, loc, ww, zz); + auto bTemp2 = arith::MulFOp::create(rewriter, loc, constants.two, bTemp1); + auto bTemp3 = arith::SubFOp::create(rewriter, loc, bTemp2, constants.one); + auto clampedLow = + arith::MaximumFOp::create(rewriter, loc, bTemp3, constants.negOne); + auto clamped = + arith::MinimumFOp::create(rewriter, loc, clampedLow, constants.one); + auto beta = math::AcosOp::create(rewriter, loc, clamped); + + // intermediates to check for gimbal lock (|beta| and |beta - PI|) + auto absBeta = math::AbsFOp::create(rewriter, loc, beta); + auto betaMinusPi = arith::SubFOp::create(rewriter, loc, beta, constants.pi); + auto absBetaMinusPi = math::AbsFOp::create(rewriter, loc, betaMinusPi); + + // safe1 = beta not within boundary eps around 0: + // |beta| >= eps + auto safe1 = arith::CmpFOp::create(rewriter, loc, arith::CmpFPredicate::OGE, + absBeta, constants.eps); + // safe2 = beta not within boundary eps around PI: |beta - PI| >= eps + auto safe2 = arith::CmpFOp::create(rewriter, loc, arith::CmpFPredicate::OGE, + absBetaMinusPi, constants.eps); + // is safe (not in gimbal lock) when both hold (safe1 AND safe2) + auto safe = arith::AndIOp::create(rewriter, loc, safe1, safe2); + + // intermediate angles for z-rotations alpha and gamma + // theta+ = atan2(z, w) + // theta- = atan2(-x, y) + auto xMinus = arith::NegFOp::create(rewriter, loc, q.x); + auto thetaPlus = math::Atan2Op::create(rewriter, loc, q.z, q.w); + auto thetaMinus = math::Atan2Op::create(rewriter, loc, xMinus, q.y); + + // intermediate angles for gimbal lock cases + // twoTheta+ = 2 * theta+ + // twoTheta- = 2 * theta- + auto twoThetaPlus = + arith::MulFOp::create(rewriter, loc, constants.two, thetaPlus); + auto twoThetaMinus = + arith::MulFOp::create(rewriter, loc, constants.two, thetaMinus); + + // Safe Case (no gimbal lock): + // alphaSafe = theta+ + theta- + // gammaSafe = theta+ - theta- + auto alphaSafe = + arith::AddFOp::create(rewriter, loc, thetaPlus, thetaMinus); + auto gammaSafe = + arith::SubFOp::create(rewriter, loc, thetaPlus, thetaMinus); + + // Unsafe Case (gimbal lock): + // when beta = 0 then alpha = 2 * (atan2(z, w)) + // when beta = PI then alpha = 2 * (atan2(-x, y)) + // gamma is set to zero in both cases + auto alphaUnsafe = arith::SelectOp::create(rewriter, loc, safe1, + twoThetaMinus, twoThetaPlus); + + // choose correct alpha and gamma whether safe or not + auto alpha = + arith::SelectOp::create(rewriter, loc, safe, alphaSafe, alphaUnsafe); + auto gamma = + arith::SelectOp::create(rewriter, loc, safe, gammaSafe, constants.zero); + + // normalize alpha and gamma to [-PI, PI] since they are sums/differences + // of atan2 results and can exceed that range + auto alphaNorm = normalizeAngle(alpha, loc, constants, rewriter); + auto gammaNorm = normalizeAngle(gamma, loc, constants, rewriter); + + return {.theta = beta.getResult(), .phi = alphaNorm, .lambda = gammaNorm}; + } + + /** + * @brief Matches and merges a chain of consecutive rotation gates. + * + * Detects the full chain of mergeable operations, folds their quaternions + * via Hamilton product, and emits a single UOp. + * + * @param op The operation to match (only chain heads trigger the rewrite) + * @param rewriter Pattern rewriter for applying transformations + * @return success() if operations were merged, failure() otherwise + */ + LogicalResult matchAndRewrite(UnitaryOpInterface op, + PatternRewriter& rewriter) const override { + if (!isChainStart(op)) { + return failure(); + } + + auto chain = collectChain(op); + if (chain.size() < 2) { + return failure(); + } + + // Emit all helper ops at the chain tail so the merged UOp is placed + // adjacent to the last gate it replaces. + OpBuilder::InsertionGuard guard(rewriter); + rewriter.setInsertionPointAfter(chain.back().getOperation()); + + auto loc = op->getLoc(); + auto constants = createConstants(loc, rewriter); + + // Initialize accumulators from the first operation + auto qAccum = quaternionFromRotation(chain.front(), constants, rewriter); + auto phaseAccum = globalPhaseOf(chain.front(), constants, loc, rewriter); + + // Fold remaining operations via Hamilton product + for (auto chainOp : llvm::drop_begin(chain)) { + auto qi = quaternionFromRotation(chainOp, constants, rewriter); + qAccum = hamiltonProduct(qi, qAccum, loc, rewriter); + + if (auto phase = globalPhaseOf(chainOp, constants, loc, rewriter)) { + phaseAccum = phaseAccum ? Value(arith::AddFOp::create( + rewriter, loc, *phaseAccum, *phase)) + : phase; + } + + // Bypass each tail operation + rewriter.replaceOp(chainOp, chainOp.getInputQubit(0)); + } + + // Extract Euler angles from merged quaternion + auto [theta, phi, lambda] = + anglesFromQuaternion(qAccum, loc, constants, rewriter); + + // Emit global phase correction: + // The synthesized UOp carries an intrinsic phase + // outPhase = (phi + lambda) / 2 that must always be compensated. + // correction = totalInputPhase - outPhase + auto phiPlusLambda = arith::AddFOp::create(rewriter, loc, phi, lambda); + auto outPhase = + arith::DivFOp::create(rewriter, loc, phiPlusLambda, constants.two); + auto inputPhase = phaseAccum.value_or(constants.zero); + auto correction = + arith::SubFOp::create(rewriter, loc, inputPhase, outPhase); + GPhaseOp::create(rewriter, loc, correction.getResult()); + + // Replace the head operation with the merged UOp + rewriter.replaceOpWithNewOp( + chain.front(), chain.front().getInputQubit(0), theta, phi, lambda); + + return success(); + } +}; + +/** + * @brief Pass that merges consecutive rotation gates using quaternion + * multiplication. + */ +struct MergeSingleQubitRotationGates final + : impl::MergeSingleQubitRotationGatesBase { + using impl::MergeSingleQubitRotationGatesBase< + MergeSingleQubitRotationGates>::MergeSingleQubitRotationGatesBase; + +protected: + void runOnOperation() override { + auto op = getOperation(); + auto* ctx = &getContext(); + + RewritePatternSet patterns(ctx); + patterns.add(patterns.getContext()); + + if (failed(applyPatternsGreedily(op, std::move(patterns)))) { + signalPassFailure(); + } + } +}; + +} // namespace + +} // namespace mlir::qco diff --git a/mlir/tools/mqt-cc/mqt-cc.cpp b/mlir/tools/mqt-cc/mqt-cc.cpp index dd52818b1d..a016d8062f 100644 --- a/mlir/tools/mqt-cc/mqt-cc.cpp +++ b/mlir/tools/mqt-cc/mqt-cc.cpp @@ -73,6 +73,11 @@ static cl::opt cl::desc("Print IR after each compiler stage"), cl::init(false)); +static cl::opt disableMergeSingleQubitRotationGates( + "disable-merge-single-qubit-rotation-gates", + cl::desc("Disable quaternion-based single-qubit rotation gate merging"), + cl::init(false)); + /** * @brief Load and parse a .qasm file */ @@ -169,6 +174,8 @@ int main(int argc, char** argv) { config.enableTiming = enableTiming; config.enableStatistics = enableStatistics; config.printIRAfterAllStages = printIRAfterAllStages; + config.disableMergeSingleQubitRotationGates = + disableMergeSingleQubitRotationGates; // Run the compilation pipeline CompilationRecord record; diff --git a/mlir/unittests/Compiler/test_compiler_pipeline.cpp b/mlir/unittests/Compiler/test_compiler_pipeline.cpp index 9cde9043a7..0549b1f4ab 100644 --- a/mlir/unittests/Compiler/test_compiler_pipeline.cpp +++ b/mlir/unittests/Compiler/test_compiler_pipeline.cpp @@ -117,9 +117,12 @@ class CompilerPipelineTest } static void runPipeline(const mlir::ModuleOp module, const bool convertToQIR, + const bool disableMergeSingleQubitRotationGates, mlir::CompilationRecord& record) { mlir::QuantumCompilerConfig config; config.convertToQIR = convertToQIR; + config.disableMergeSingleQubitRotationGates = + disableMergeSingleQubitRotationGates; config.recordIntermediates = true; config.printIRAfterAllStages = true; @@ -163,7 +166,7 @@ TEST_P(CompilerPipelineTest, EndToEndPipeline) { EXPECT_TRUE(mlir::verify(*module).succeeded()); mlir::CompilationRecord record; - runPipeline(module.get(), testCase.convertToQIR, record); + runPipeline(module.get(), testCase.convertToQIR, false, record); ASSERT_TRUE(testCase.qcReferenceBuilder); auto qcReference = buildQCReference(testCase.qcReferenceBuilder); @@ -189,6 +192,33 @@ TEST_P(CompilerPipelineTest, EndToEndPipeline) { } } +/** + * @brief Test: Rotation merging pass is invoked during the optimization stage + * + * @details + * The merged U gate parameters are computed via floating-point arithmetic + * that is not bit-identical across platforms, so we cannot use + * verifyAllStages with hardcoded expected values. Instead, we run the + * pipeline once with the pass enabled and compare afterQCOCanon against + * afterOptimization to verify the pass transformed the IR. + * Correctness of the pass is tested in a dedicated test. + */ +TEST_F(CompilerPipelineTest, RotationGateMergingPass) { + auto module = mlir::qc::QCProgramBuilder::build( + context.get(), [&](mlir::qc::QCProgramBuilder& b) { + auto q = b.allocQubit(); + b.rz(1.0, q); + b.rx(1.0, q); + }); + ASSERT_TRUE(module); + + mlir::CompilationRecord record; + runPipeline(module.get(), false, false, record); + + // The outputs must differ, proving the pass ran and transformed the IR + EXPECT_NE(record.afterQCOCanon, record.afterOptimization); +} + INSTANTIATE_TEST_SUITE_P( QuantumComputationPipelineProgramsTest, CompilerPipelineTest, testing::Values( diff --git a/mlir/unittests/Dialect/QCO/Transforms/CMakeLists.txt b/mlir/unittests/Dialect/QCO/Transforms/CMakeLists.txt index 30ddc4dc38..9f9b03449d 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/CMakeLists.txt +++ b/mlir/unittests/Dialect/QCO/Transforms/CMakeLists.txt @@ -7,3 +7,4 @@ # Licensed under the MIT License add_subdirectory(Mapping) +add_subdirectory(Optimizations) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Mapping/CMakeLists.txt b/mlir/unittests/Dialect/QCO/Transforms/Mapping/CMakeLists.txt index 1ced00dc34..405159075a 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Mapping/CMakeLists.txt +++ b/mlir/unittests/Dialect/QCO/Transforms/Mapping/CMakeLists.txt @@ -11,8 +11,8 @@ add_executable(${target_name} test_mapping.cpp) target_link_libraries( ${target_name} - PRIVATE MLIRParser - GTest::gtest_main + PRIVATE GTest::gtest_main + MLIRParser MLIRQCProgramBuilder MLIRQCOProgramBuilder MLIRQCOUtils diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt new file mode 100644 index 0000000000..73606c2efb --- /dev/null +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt @@ -0,0 +1,26 @@ +# 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 + +set(target_name mqt-core-mlir-unittest-optimizations) +add_executable(${target_name} test_qco_merge_single_qubit_rotation.cpp) + +target_link_libraries( + ${target_name} + PRIVATE GTest::gtest_main + MLIRQCOProgramBuilder + MLIRQCOTransforms + MLIRQCOUtils + MLIRParser + MLIRIR + MLIRPass + MLIRSupport + LLVMSupport) + +mqt_mlir_configure_unittest_target(${target_name}) + +gtest_discover_tests(${target_name} PROPERTIES LABELS mqt-mlir-unittests DISCOVERY_TIMEOUT 60) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/compute_expected_merge_single_qubit_rotation.py b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/compute_expected_merge_single_qubit_rotation.py new file mode 100644 index 0000000000..16f8818cda --- /dev/null +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/compute_expected_merge_single_qubit_rotation.py @@ -0,0 +1,208 @@ +# 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 + +"""Compute expected U gate parameters and global phase for the merge-single-qubit-rotation pass. + +Reference script for test_qco_merge_single_qubit_rotation.cpp. +Uses SymPy quaternion algebra to produce ground-truth values. +""" + +import math + +from sympy import N, Quaternion, cos, pi, sin + + +def r_gate(theta: float, phi: float) -> Quaternion: + """Return the SU(2) quaternion for an R(theta, phi) gate.""" + return Quaternion(cos(theta / 2), sin(theta / 2) * cos(phi), sin(theta / 2) * sin(phi), 0) + + +def u2_gate(phi: float, lam: float) -> Quaternion: + """Return the SU(2) quaternion for a U2(phi, lambda) gate.""" + return Quaternion.from_euler([phi, pi / 2, lam], "ZYZ") + + +def normalize_angle(a: float) -> float: + """Normalize angle to [-pi, pi], matching the pass's normalizeAngle. + + Returns: + Angle in the range [-pi, pi]. + """ + two_pi = 2 * math.pi + return a - math.floor((a + math.pi) / two_pi) * two_pi + + +def angles_from_quaternion(w: float, x: float, y: float, z: float) -> tuple[float, float, float]: + """ZYZ Euler angles from quaternion, matching anglesFromQuaternion in the pass. + + Returns: + Tuple (theta, phi, lambda) in ZYZ convention. + """ + eps = 1e-12 + + # Clamp before acos to guard against floating-point drift outside [-1, 1] + arg = 2 * (w * w + z * z) - 1 + arg = max(-1.0, min(1.0, arg)) + beta = math.acos(arg) + + abs_beta = abs(beta) + abs_beta_minus_pi = abs(beta - math.pi) + + safe1 = abs_beta >= eps # not near 0 + safe2 = abs_beta_minus_pi >= eps # not near pi + safe = safe1 and safe2 + + theta_plus = math.atan2(z, w) + theta_minus = math.atan2(-x, y) + + if safe: + alpha = theta_plus + theta_minus + gamma = theta_plus - theta_minus + elif not safe1: + # beta near 0 + alpha = 2 * theta_plus + gamma = 0.0 + else: + # beta near pi + alpha = 2 * theta_minus + gamma = 0.0 + + alpha = normalize_angle(alpha) + gamma = normalize_angle(gamma) + + # U gate convention: theta=beta, phi=alpha, lambda=gamma + return beta, alpha, gamma + + +def global_phase(gate_type: str, *angles: float) -> float: + """Return the global phase contribution of a gate. + + U = e^{i*phase} * SU(2), this returns 'phase'. + + Returns: + Global phase in radians. + + Raises: + ValueError: If gate_type is not a recognized gate. + """ + if gate_type in {"RX", "RY", "RZ", "R"}: + return 0.0 + if gate_type == "P": + return angles[0] / 2 + if gate_type == "U": + # U(theta, phi, lambda): phase = (phi + lambda) / 2 + _theta, phi, lam = angles + return (phi + lam) / 2 + if gate_type == "U2": + # U2(phi, lambda): phase = (phi + lambda) / 2 + phi, lam = angles + return (phi + lam) / 2 + msg = f"Unknown gate type: {gate_type!r}" + raise ValueError(msg) + + +def output_phase(phi: float, lam: float) -> float: + """Return the intrinsic phase of the synthesized U(theta, phi, lambda). + + Returns: + Phase in radians. + """ + return (phi + lam) / 2 + + +def gphase_correction(input_phase: float, phi: float, lam: float) -> float: + """Return the GPhaseOp correction = total_input_phase - output_UOp_phase. + + Returns: + Phase correction in radians. + """ + return input_phase - output_phase(phi, lam) + + +# ---- Helper to compute merge + gphase for a chain ---- +def compute_merge(chain: list[tuple]) -> tuple[float, float, float, float]: + """Merge a chain of gates into a single U gate with global phase. + + chain: list of (gate_type, quaternion, *angles). + + Uses our own Euler extraction that matches the C++ pass exactly: + no quaternion sign normalization, same atan2/acos/clamp logic, + same gimbal-lock handling, same angle normalization. + + Returns: + Tuple (theta, phi, lambda, gphase) all as floats. + """ + _, q0, *a0 = chain[0] + q = q0 + total_input_phase = global_phase(chain[0][0], *a0) + + for entry in chain[1:]: + gt, qi, *ai = entry + q = qi.mul(q) # Hamilton product in circuit order + total_input_phase += global_phase(gt, *ai) + + # Extract Euler angles matching the pass (no sign normalization) + w, x, y, z = float(N(q.a)), float(N(q.b)), float(N(q.c)), float(N(q.d)) + theta, phi, lam = angles_from_quaternion(w, x, y, z) + + corr = gphase_correction(total_input_phase, phi, lam) + + return theta, phi, lam, float(N(corr)) + + +# ---- Build gates ---- +rx = Quaternion.from_euler([1, 0, 0], "xyz") +ry = Quaternion.from_euler([0, 1, 0], "xyz") +rz = Quaternion.from_euler([0, 0, 1], "xyz") +mx = Quaternion.from_euler([-1, 0, 0], "xyz") +my = Quaternion.from_euler([0, -1, 0], "xyz") +mz = Quaternion.from_euler([0, 0, -1], "xyz") +px = Quaternion.from_euler([pi, 0, 0], "xyz") +py = Quaternion.from_euler([0, pi, 0], "xyz") +pz = Quaternion.from_euler([0, 0, pi], "xyz") +smallx = Quaternion.from_euler([0.001, 0, 0], "xyz") +smally = Quaternion.from_euler([0, 0.001, 0], "xyz") + +# P gate has same SU(2) quaternion as RZ +p1 = Quaternion.from_euler([0, 0, 1], "xyz") # P(1) same rotation as RZ(1) + +u1 = Quaternion.from_euler([2, 1, 3], "ZYZ") # U(1,2,3) +u2 = Quaternion.from_euler([5, 4, 6], "ZYZ") # U(4,5,6) + +u2_12 = u2_gate(1, 2) +u2_34 = u2_gate(3, 4) + +r12 = r_gate(1, 2) +r34 = r_gate(3, 4) +r11 = r_gate(1, 1) + +cases = [ + ("RX+RX", [("RX", rx), ("RX", rx)]), + ("RX+RY", [("RX", rx), ("RY", ry)]), + ("RX+RZ", [("RX", rx), ("RZ", rz)]), + ("RY+RX", [("RY", ry), ("RX", rx)]), + ("RY+RY", [("RY", ry), ("RY", ry)]), + ("RY+RZ", [("RY", ry), ("RZ", rz)]), + ("RZ+RX", [("RZ", rz), ("RX", rx)]), + ("RZ+RY", [("RZ", rz), ("RY", ry)]), + ("RZ+RZ", [("RZ", rz), ("RZ", rz)]), + ("U+U", [("U", u1, 1.0, 2.0, 3.0), ("U", u2, 4.0, 5.0, 6.0)]), + ("P+RX", [("P", p1, 1.0), ("RX", rx)]), + ("R+R", [("R", r12, 1.0, 2.0), ("R", r34, 3.0, 4.0)]), + ("U2+U2", [("U2", u2_12, 1.0, 2.0), ("U2", u2_34, 3.0, 4.0)]), + ("RZ+RY+RX pi", [("RZ", pz), ("RY", py), ("RX", px)]), + ("RY+RZ+RZ-+RY-", [("RY", ry), ("RZ", rz), ("RZ", mz), ("RY", my)]), + ("small RX+RY", [("RX", smallx), ("RY", smally)]), + ("RX(pi)+RY(pi)", [("RX", px), ("RY", py)]), + ("R+R same", [("R", r11, 1.0, 1.0), ("R", r11, 1.0, 1.0)]), +] + +if __name__ == "__main__": + for name, chain in cases: + theta, phi, lam, gphase = compute_merge(chain) + print(f"{name}: U({theta}, {phi}, {lam}) gphase={gphase}") diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp new file mode 100644 index 0000000000..5ab06957f0 --- /dev/null +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp @@ -0,0 +1,810 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" +#include "mlir/Dialect/QCO/IR/QCODialect.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 +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace { + +using namespace mlir; +using namespace mlir::qco; + +/// A constant for the value of \f$\pi\f$. +constexpr double PI = std::numbers::pi; + +class MergeSingleQubitRotationGatesTest : public ::testing::Test { +protected: + MLIRContext context; + QCOProgramBuilder builder; + OwningOpRef module; + + enum class GateType : std::uint8_t { RX, RY, RZ, P, R, U2, U }; + /** + * @brief Struct to easily construct a rotation gate inline. + * opName uses the getOperationName() mnemonic. + */ + struct RotationGate { + GateType type; + llvm::SmallVector angles; + }; + + MergeSingleQubitRotationGatesTest() : builder(&context) {} + + void SetUp() override { + context.loadDialect(); + context.loadDialect(); + context.loadDialect(); + context.loadDialect(); + + builder.initialize(); + } + + /** + * @brief Counts the amount of operations the current module/circuit contains. + */ + template int countOps() { + int count = 0; + module->walk([&](OpTy) { ++count; }); + return count; + } + + /** + * @brief Extract constant floating point value from a mlir::Value + */ + static std::optional toDouble(mlir::Value v) { + if (auto constOp = v.getDefiningOp()) { + if (auto floatAttr = + mlir::dyn_cast(constOp.getValue())) { + return floatAttr.getValueAsDouble(); + } + } + return std::nullopt; + } + + /** + * @brief Find the first occurrence of a u-gate in the current module and get + * the numeric value of its parameters. This assumes that parameters are + * constant and can be extracted. + */ + std::optional> getUGateParams() { + UOp uOp = nullptr; + module->walk([&](UOp op) { + uOp = op; + // stop after finding first UOp + return mlir::WalkResult::interrupt(); + }); + + if (!uOp) { + return std::nullopt; + } + + auto theta = toDouble(uOp.getTheta()); + auto phi = toDouble(uOp.getPhi()); + auto lambda = toDouble(uOp.getLambda()); + + if (!theta || !phi || !lambda) { + return std::nullopt; + } + + return std::make_tuple(*theta, *phi, *lambda); + } + + /** + * @brief Gets the first u-gate of a module and tests whether its angle + * parameters are equal to the expected ones. + */ + void expectUGateParams(double expectedTheta, double expectedPhi, + double expectedLambda, double tolerance = 1e-8) { + auto params = getUGateParams(); + ASSERT_TRUE(params.has_value()); + + auto [theta, phi, lambda] = *params; + EXPECT_NEAR(theta, expectedTheta, tolerance); + EXPECT_NEAR(phi, expectedPhi, tolerance); + EXPECT_NEAR(lambda, expectedLambda, tolerance); + } + + /** + * @brief Find the first occurrence of a gphase op in the current module and + * get the numeric value of its parameter. + */ + std::optional getGPhaseParam() { + GPhaseOp gOp = nullptr; + module->walk([&](GPhaseOp op) { + gOp = op; + return mlir::WalkResult::interrupt(); + }); + + if (!gOp) { + return std::nullopt; + } + + return toDouble(gOp.getParameter(0)); + } + + /** + * @brief Gets the first gphase op of a module and tests whether its angle + * parameter is equal to the expected one. + */ + void expectGPhaseParam(double expected, double tolerance = 1e-8) { + auto param = getGPhaseParam(); + ASSERT_TRUE(param.has_value()); + EXPECT_NEAR(*param, expected, tolerance); + } + + Value buildRotations(llvm::ArrayRef rotations, Value& q) { + auto qubit = q; + + for (const auto& gate : rotations) { + switch (gate.type) { + case GateType::RX: + assert(gate.angles.size() == 1 && "RXOp requires 1 angle parameter"); + qubit = builder.rx(gate.angles[0], qubit); + break; + case GateType::RY: + assert(gate.angles.size() == 1 && "RYOp requires 1 angle parameter"); + qubit = builder.ry(gate.angles[0], qubit); + break; + case GateType::RZ: + assert(gate.angles.size() == 1 && "RZOp requires 1 angle parameter"); + qubit = builder.rz(gate.angles[0], qubit); + break; + case GateType::P: + assert(gate.angles.size() == 1 && "POp requires 1 angle parameter"); + qubit = builder.p(gate.angles[0], qubit); + break; + case GateType::R: + assert(gate.angles.size() == 2 && "ROp requires 2 angle parameters"); + qubit = builder.r(gate.angles[0], gate.angles[1], qubit); + break; + case GateType::U2: + assert(gate.angles.size() == 2 && "U2Op requires 2 angle parameters"); + qubit = builder.u2(gate.angles[0], gate.angles[1], qubit); + break; + case GateType::U: + assert(gate.angles.size() == 3 && "UOp requires 3 angle parameters"); + qubit = + builder.u(gate.angles[0], gate.angles[1], gate.angles[2], qubit); + break; + } + } + + return qubit; + } + + /** + * @brief Takes a list of rotation gates (rx, ry, rz and u) and uses the + * builder api to build a small quantum circuit, where a qubit is fed through + * all rotations in the list. + */ + LogicalResult testGateMerge(llvm::ArrayRef rotations) { + auto q = builder.allocQubitRegister(1); + + buildRotations(rotations, q[0]); + + module = builder.finalize(); + return runMergePass(module.get()); + } + + /** + * @brief Adds the mergeRotationGates Pass to the current context and runs it. + */ + static LogicalResult runMergePass(ModuleOp module) { + PassManager pm(module.getContext()); + pm.addPass(qco::createMergeSingleQubitRotationGates()); + return pm.run(module); + } +}; + +} // namespace + +// Note: All expected values are computed using the reference script +// compute_expected_merge_single_qubit_rotation.py in this directory, which uses +// SymPy's quaternion algebra: +// https://docs.sympy.org/latest/modules/algebras.html#module-sympy.algebras.Quaternion + +// ################################################## +// # Two Gate Merging Tests +// ################################################## + +/** + * @brief Test: RX->RX should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRXRXGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RX, .angles = {1.}}, + {.type = GateType::RX, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: RX->RY should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRXRYGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RX, .angles = {1.}}, + {.type = GateType::RY, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1.27455578230629, -1.07542903757622, 0.495367289218673); + expectGPhaseParam(0.290030874178775); +} + +/** + * @brief Test: RX->RZ should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRXRZGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RX, .angles = {1.}}, + {.type = GateType::RZ, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1., -0.570796326794897, 1.57079632679490); + expectGPhaseParam(-0.5); +} + +/** + * @brief Test: RY->RX should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRYRXGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RY, .angles = {1.}}, + {.type = GateType::RX, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1.27455578230629, -0.495367289218673, 1.07542903757622); + expectGPhaseParam(-0.290030874178775); +} + +/** + * @brief Test: RY->RY should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRYRYGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RY, .angles = {1.}}, + {.type = GateType::RY, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: RY->RZ should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRYRZGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RY, .angles = {1.}}, + {.type = GateType::RZ, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1., 1., 0.); + expectGPhaseParam(-0.5); +} + +/** + * @brief Test: RZ->RX should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRZRXGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RZ, .angles = {1.}}, + {.type = GateType::RX, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1., -1.57079632679490, 2.57079632679490); + expectGPhaseParam(-0.5); +} + +/** + * @brief Test: RZ->RY should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRZRYGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RZ, .angles = {1.}}, + {.type = GateType::RY, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1., 0., 1.); + expectGPhaseParam(-0.5); +} + +/** + * @brief Test: RZ->RZ should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRZRZGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RZ, .angles = {1.}}, + {.type = GateType::RZ, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: U->U should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeUUGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U, .angles = {1., 2., 3.}}, + {.type = GateType::U, .angles = {4., 5., 6.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); + expectUGateParams(2.03289042623884, 0.663830775701153, 0.849231441867857); + expectGPhaseParam(7.243468891215494); +} + +/** + * @brief Test: U->RX should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeURXGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U, .angles = {1., 2., 3.}}, + {.type = GateType::RX, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: U->RY should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeURYGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U, .angles = {1., 2., 3.}}, + {.type = GateType::RY, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: U->RZ should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeURZGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U, .angles = {1., 2., 3.}}, + {.type = GateType::RZ, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: RX->U should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRXUGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RX, .angles = {1.}}, + {.type = GateType::U, .angles = {1., 2., 3.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: RY->U should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRYUGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RY, .angles = {1.}}, + {.type = GateType::U, .angles = {1., 2., 3.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: RZ->U should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRZUGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RZ, .angles = {1.}}, + {.type = GateType::U, .angles = {1., 2., 3.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); +} +/** + * @brief Test: P->RX should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergePRXGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::P, .angles = {1.}}, + {.type = GateType::RX, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1., -1.57079632679490, 2.57079632679490); + expectGPhaseParam(1.11022302462516e-16); +} + +/** + * @brief Test: P->RY should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergePRYGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::P, .angles = {1.}}, + {.type = GateType::RY, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: P->U should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergePUGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::P, .angles = {1.}}, + {.type = GateType::U, .angles = {1., 2., 3.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: R->RX should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRRXGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::R, .angles = {1., 1.}}, + {.type = GateType::RX, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: P->P should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergePPGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::P, .angles = {1.}}, + {.type = GateType::P, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: R->R should merge into a single U gate (same multi-parameter + * type always uses quaternion merge) + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRRGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::R, .angles = {1., 2.}}, + {.type = GateType::R, .angles = {3., 4.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + expectUGateParams(2.07770669385131, 1.36334275733332, 2.85969871348886); + expectGPhaseParam(-2.1115207354110845); +} + +/** + * @brief Test: U2->U should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeU2UGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U2, .angles = {1., 2.}}, + {.type = GateType::U, .angles = {1., 2., 3.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: U2->U2 should merge into a single U gate (same multi-parameter + * type always uses quaternion merge) + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeU2U2Gates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U2, .angles = {1., 2.}}, + {.type = GateType::U2, .angles = {3., 4.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1.85840734641021, 1.42920367320511, 0.429203673205103); + expectGPhaseParam(4.070796326794897); +} + +// ################################################## +// # Not Merging Tests +// ################################################## + +/** + * @brief Test: single RX should not convert to U + */ +TEST_F(MergeSingleQubitRotationGatesTest, noMergeSingleRXGate) { + ASSERT_TRUE( + testGateMerge({{.type = GateType::RX, .angles = {1.}}}).succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); +} + +/** + * @brief Test: single RY should not convert to U + */ +TEST_F(MergeSingleQubitRotationGatesTest, noMergeSingleRYGate) { + ASSERT_TRUE( + testGateMerge({{.type = GateType::RY, .angles = {1.}}}).succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); +} + +/** + * @brief Test: single RZ should not convert to U + */ +TEST_F(MergeSingleQubitRotationGatesTest, noMergeSingleRZGate) { + ASSERT_TRUE( + testGateMerge({{.type = GateType::RZ, .angles = {1.}}}).succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); +} + +/** + * @brief Test: Gates on different qubits should not merge + */ +TEST_F(MergeSingleQubitRotationGatesTest, dontMergeGatesFromDifferentQubits) { + auto q = builder.allocQubitRegister(2); + + builder.rx(1.0, q[0]); + builder.ry(1.0, q[1]); + module = builder.finalize(); + + ASSERT_TRUE(runMergePass(module.get()).succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); +} + +/** + * @brief Test: Non-consecutive gates should not merge + */ +TEST_F(MergeSingleQubitRotationGatesTest, dontMergeNonConsecutiveGates) { + auto q = builder.allocQubitRegister(1); + + auto q1 = builder.rx(1.0, q[0]); + auto q2 = builder.h(q1); + builder.ry(1.0, q2); + + module = builder.finalize(); + + ASSERT_TRUE(runMergePass(module.get()).succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); +} + +// ################################################## +// # Greedy Merging Tests +// ################################################## + +/** + * @brief Test: Many gates should greedily merge into one U + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeManyGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U, .angles = {1., 2., 3.}}, + {.type = GateType::RX, .angles = {1.}}, + {.type = GateType::RY, .angles = {2.}}, + {.type = GateType::RZ, .angles = {3.}}, + {.type = GateType::U, .angles = {4., 5., 6.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: Many gates with one unmergeable in between should merge into two + * U with the unmergeable in between. + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeManyWithUnmergeable) { + auto reg = builder.allocQubitRegister(1); + auto q = reg[0]; + q = buildRotations({{.type = GateType::U, .angles = {1., 2., 3.}}, + {.type = GateType::RX, .angles = {1.}}, + {.type = GateType::RY, .angles = {2.}}, + {.type = GateType::RZ, .angles = {3.}}}, + q); + q = builder.h(q); + q = buildRotations({{.type = GateType::RZ, .angles = {4.}}, + {.type = GateType::RY, .angles = {5.}}, + {.type = GateType::RX, .angles = {6.}}, + {.type = GateType::U, .angles = {4., 5., 6.}}}, + q); + + module = builder.finalize(); + + ASSERT_TRUE(runMergePass(module.get()).succeeded()); + EXPECT_EQ(countOps(), 2); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 2); +} + +// ################################################## +// # Special Cases Tests +// ################################################## + +/** + * @brief Test: Consecutive gates with another gate in between should merge + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeConsecutiveWithGateInBetween) { + auto q = builder.allocQubitRegister(2); + + auto q1 = builder.rx(1.0, q[0]); + builder.h(q[1]); + builder.ry(1.0, q1); + + module = builder.finalize(); + + ASSERT_TRUE(runMergePass(module.get()).succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); +} + +// ################################################## +// # Numerical Correctness +// ################################################## + +/** + * @brief Test: RZ(PI)->RY(PI)->RX(PI) should merge into U(0, 0, 0) + */ +TEST_F(MergeSingleQubitRotationGatesTest, numericalRotationIdentity) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RZ, .angles = {PI}}, + {.type = GateType::RY, .angles = {PI}}, + {.type = GateType::RX, .angles = {PI}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(0., 0., 0.); + expectGPhaseParam(0.); +} + +/** + * @brief Test: RY(1)->RZ(1)->RZ(-1)->RY(-1) should merge into U(0, 0, 0) + */ +TEST_F(MergeSingleQubitRotationGatesTest, numericalRotationIdentity2) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RY, .angles = {1}}, + {.type = GateType::RZ, .angles = {1}}, + {.type = GateType::RZ, .angles = {-1}}, + {.type = GateType::RY, .angles = {-1}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(0., 0., 0.); + expectGPhaseParam(0.); +} + +/** + * @brief Test: RX(0.001)->RY(0.001) should merge into U(0.00141421344452194, + * -0.785398413397490, 0.785397913397407) + */ +TEST_F(MergeSingleQubitRotationGatesTest, numericalSmallAngles) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RX, .angles = {0.001}}, + {.type = GateType::RY, .angles = {0.001}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(0.00141421344452194, -0.785398413397490, 0.785397913397407); + expectGPhaseParam(2.50000041668308e-7); +} + +/** + * @brief Test: RX(PI)->RY(PI) should merge into U(0, -PI, 0.) + */ +TEST_F(MergeSingleQubitRotationGatesTest, numericalGimbalLock) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RX, .angles = {PI}}, + {.type = GateType::RY, .angles = {PI}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(0., -PI, 0.); + expectGPhaseParam(1.57079632679490); +} + +/** + * @brief Test: R(1,1)->R(1,1) (same axis) should merge into U(2.00000000000000, + * -0.570796326794897, 0.570796326794897) + */ +TEST_F(MergeSingleQubitRotationGatesTest, numericalAccuracyRRSameAxis) { + ASSERT_TRUE(testGateMerge({{.type = GateType::R, .angles = {1., 1.}}, + {.type = GateType::R, .angles = {1., 1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(2., -0.570796326794897, 0.570796326794897); + expectGPhaseParam(0.0); +} + +/** + * @brief Test: U(0, -2.0360075460227076, 0)->U(0, 4.157656961105587, 0) should + * not produce NaN. These specific numbers would produce NaN if acos parameter + * would not be clamped to [-1, 1] + */ +TEST_F(MergeSingleQubitRotationGatesTest, numericalAcosClampingPreventsNaN) { + ASSERT_TRUE(testGateMerge( + {{.type = GateType::U, .angles = {0, -2.0360075460227076, 0}}, + {.type = GateType::U, .angles = {0, 4.157656961105587, 0}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); + + auto params = getUGateParams(); + ASSERT_TRUE(params.has_value()); + + auto [theta, phi, lambda] = *params; + EXPECT_FALSE(std::isnan(theta)); + EXPECT_FALSE(std::isnan(phi)); + EXPECT_FALSE(std::isnan(lambda)); + + auto gphase = getGPhaseParam(); + ASSERT_TRUE(gphase.has_value()); + EXPECT_FALSE(std::isnan(*gphase)); +} diff --git a/pyproject.toml b/pyproject.toml index 586456c41f..762f3aca0b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -233,6 +233,7 @@ known-first-party = ["mqt.core"] "test/python/**" = ["T20", "INP001"] "docs/**" = ["T20", "INP001"] "noxfile.py" = ["T20", "TID251", "PLC0415"] +"mlir/unittests/**/*.py" = ["INP001", "T201"] "*.pyi" = ["D418", "E501", "PYI021"] "*.ipynb" = [ "D", # pydocstyle From 06d42cac82de0b42c13e9d7fc927646971dde084 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 21 Apr 2026 12:14:23 +0200 Subject: [PATCH 14/29] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Update=20QDMI=20to?= =?UTF-8?q?=20v1.3.0=20(#1652)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR updates QDMI to the latest stable version (v1.3.0). After this has been back ported to `v3.x`, a new minor release for MQT Core will be created. ## Checklist - [x] The pull request only contains commits that are focused and relevant to this change. - [x] I have added appropriate tests that cover the new/changed functionality. - [x] I have updated the documentation to reflect these changes. - [x] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [x] I have added migration instructions to the upgrade guide (if needed). - [x] The changes follow the project's style guidelines and introduce no new warnings. - [x] The changes are fully tested and pass the CI checks. - [x] I have reviewed my own code changes. **If PR contains AI-assisted content:** - [x] I have disclosed the use of AI tools in the PR description as per our [AI Usage Guidelines](https://github.com/munich-quantum-toolkit/core/blob/main/docs/ai_usage.md). - [x] AI-assisted commits include an `Assisted-by: [Model Name] via [Tool Name]` footer. - [x] I confirm that I have personally reviewed and understood all AI-generated content, and accept full responsibility for it. Signed-off-by: Lukas Burgholzer --- CHANGELOG.md | 2 ++ cmake/ExternalDependencies.cmake | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b1dfddcc6..b6ce5b0476 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Changed +- ⬆️ Update QDMI to v1.3.0 ([#1652]) ([**@burgholzer**]) - 📦 Switch to component-based installation for the MQT Core Python package ([#1596]) ([**@burgholzer**]) - ⬆️ Update QDMI to latest version from stable `v1.2.x` branch ([#1593]) ([**@burgholzer**]) - ⬆️ Update `clang-tidy` to version 22 ([#1564]) ([**@denialhaag**], [**@burgholzer**]) @@ -337,6 +338,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool +[#1652]: https://github.com/munich-quantum-toolkit/core/pull/1652 [#1637]: https://github.com/munich-quantum-toolkit/core/pull/1637 [#1635]: https://github.com/munich-quantum-toolkit/core/pull/1635 [#1627]: https://github.com/munich-quantum-toolkit/core/pull/1627 diff --git a/cmake/ExternalDependencies.cmake b/cmake/ExternalDependencies.cmake index 2d17ff04d5..9f06253a62 100644 --- a/cmake/ExternalDependencies.cmake +++ b/cmake/ExternalDependencies.cmake @@ -98,13 +98,13 @@ if(BUILD_MQT_CORE_TESTS) endif() # cmake-format: off -set(QDMI_VERSION 1.2.2 +set(QDMI_VERSION 1.3.0 CACHE STRING "QDMI version") -set(QDMI_REV "5bcf32f57158beea34d2839a41d218ed46a41516" # v1.2.x +set(QDMI_REV "0f7e08c58b72800d1022a01cfb618af67b9a9c30" # v1.3.0 CACHE STRING "QDMI identifier (tag, branch or commit hash)") set(QDMI_REPO_OWNER "Munich-Quantum-Software-Stack" CACHE STRING "QDMI repository owner (change when using a fork)") -cmake_dependent_option(QDMI_INSTALL "Install QDMI library" ON "MQT_CORE_INSTALL" OFF) +cmake_dependent_option(INSTALL_QDMI "Install QDMI library" ON "MQT_CORE_INSTALL" OFF) # cmake-format: on FetchContent_Declare( qdmi From 608f7905193f6506c8c2b0b2a0edd35b6d51cc2a Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 21 Apr 2026 17:33:53 -0400 Subject: [PATCH 15/29] =?UTF-8?q?=F0=9F=93=9D=20Update=20changelog=20and?= =?UTF-8?q?=20upgrade=20guide=20after=20release=20of=20`v3.5.0`=20(#1656)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR updates the changelog and upgrade guide after the release of `v3.5.0`. ## Checklist - [x] The pull request only contains commits that are focused and relevant to this change. - [x] ~~I have added appropriate tests that cover the new/changed functionality.~~ - [x] ~~I have updated the documentation to reflect these changes.~~ - [x] ~~I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.~~ - [x] ~~I have added migration instructions to the upgrade guide (if needed).~~ - [x] The changes follow the project's style guidelines and introduce no new warnings. - [x] The changes are fully tested and pass the CI checks. - [x] I have reviewed my own code changes. --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 31 +++++++++++++++++++++++-------- UPGRADING.md | 13 ++++++++++++- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9543f3040b..f993e71948 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -164,7 +164,7 @@ jobs: build-project: true files-changed-only: true setup-python: true - install-pkgs: "nanobind==2.11.0" + install-pkgs: "nanobind==2.12.0" cpp-linter-extra-args: "-std=c++20" setup-mlir: true llvm-version: 22.1.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index b6ce5b0476..cdc630c4a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,6 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Added - ✨ Add a `merge-single-qubit-rotation-gates` pass for merging consecutive rotation gates using quaternions ([#1407]) ([**@J4MMlE**]) -- ✨ Add support for multi-controlled gates to ZX package ([#1380]) ([**@keefehuang**]) -- ✨ Add Sampler and Estimator Primitives to the QDMI-Qiskit Interface ([#1507]) ([**@marcelwa**]) - ✨ Add conversions between `jeff` and QCO ([#1479], [#1548], [#1565], [#1637]) ([**@denialhaag**]) - ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588]) ([**@MatthiasReumann**]) - ✨ Add initial infrastructure for new QC and QCO MLIR dialects @@ -22,11 +20,6 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Changed -- ⬆️ Update QDMI to v1.3.0 ([#1652]) ([**@burgholzer**]) -- 📦 Switch to component-based installation for the MQT Core Python package ([#1596]) ([**@burgholzer**]) -- ⬆️ Update QDMI to latest version from stable `v1.2.x` branch ([#1593]) ([**@burgholzer**]) -- ⬆️ Update `clang-tidy` to version 22 ([#1564]) ([**@denialhaag**], [**@burgholzer**]) -- 👷 Build on `macos-26`/`macos-26-intel` by default and `macos-15`/`macos-15-intel` for extensive tests ([#1571]) ([**@denialhaag**]) - ⬆️ Require LLVM 22.1 for C++ library builds ([#1549]) ([**@burgholzer**], [**@denialhaag**]) - 📦 Build MLIR by default for C++ library builds ([#1356]) ([**@burgholzer**], [**@denialhaag**]) @@ -35,6 +28,24 @@ This project adheres to [Semantic Versioning], with the exception that minor rel - 🔥 Remove the density matrix support from the MQT Core DD package ([#1466]) ([**@burgholzer**]) - 🔥 Remove `datastructures` (`ds`) (sub)library from MQT Core ([#1458]) ([**@burgholzer**]) +## [3.5.0] - 2026-04-21 + +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#350)._ + +### Added + +- ✨ Add support for multi-controlled gates to ZX package ([#1380]) ([**@keefehuang**]) +- ✨ Add Sampler and Estimator primitives to the QDMI-Qiskit interface ([#1507]) ([**@marcelwa**]) + +### Changed + +- ⬆️ Update `nanobind` to version 2.12.0 ([#1528]) +- ⬆️ Update QDMI to `v1.3.0` ([#1652]) ([**@burgholzer**]) +- 📦 Switch to component-based installation for the MQT Core Python package ([#1596]) ([**@burgholzer**]) +- ⬆️ Update QDMI to latest version from stable `v1.2.x` branch ([#1593]) ([**@burgholzer**]) +- ⬆️ Update `clang-tidy` to version 22 ([#1564]) ([**@denialhaag**], [**@burgholzer**]) +- 👷 Build on `macos-26`/`macos-26-intel` by default and `macos-15`/`macos-15-intel` for extensive tests ([#1571]) ([**@denialhaag**]) + ## [3.4.1] - 2026-02-01 ### Changed @@ -53,6 +64,8 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ## [3.4.0] - 2026-01-08 +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#340)._ + ### Added - ✨ Return device handle from `add_dynamic_device_library` for direct backend creation ([#1381]) ([**@marcelwa**]) @@ -321,7 +334,8 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool -[unreleased]: https://github.com/munich-quantum-toolkit/core/compare/v3.4.1...HEAD +[unreleased]: https://github.com/munich-quantum-toolkit/core/compare/v3.5.0...HEAD +[3.5.0]: https://github.com/munich-quantum-toolkit/core/releases/tag/v3.5.0 [3.4.1]: https://github.com/munich-quantum-toolkit/core/releases/tag/v3.4.1 [3.4.0]: https://github.com/munich-quantum-toolkit/core/releases/tag/v3.4.0 [3.3.3]: https://github.com/munich-quantum-toolkit/core/releases/tag/v3.3.3 @@ -369,6 +383,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [#1547]: https://github.com/munich-quantum-toolkit/core/pull/1547 [#1542]: https://github.com/munich-quantum-toolkit/core/pull/1542 [#1537]: https://github.com/munich-quantum-toolkit/core/pull/1537 +[#1528]: https://github.com/munich-quantum-toolkit/core/pull/1528 [#1521]: https://github.com/munich-quantum-toolkit/core/pull/1521 [#1513]: https://github.com/munich-quantum-toolkit/core/pull/1513 [#1510]: https://github.com/munich-quantum-toolkit/core/pull/1510 diff --git a/UPGRADING.md b/UPGRADING.md index cb92968af9..f30be35649 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -37,6 +37,16 @@ The `datastructures` (sub)library has been removed from the MQT Core repository. Its functionality has only ever been used in [MQT QMAP] since its inception. As a consequence, the code shall be moved to [MQT QMAP] once QMAP adopts an MQT Core version that includes this change. +## [3.5.0] + +The shared library ABI version (`SOVERSION`) is increased from `3.4` to `3.5`. +Thus, consuming libraries need to update their wheel repair configuration for `cibuildwheel` to ensure the `mqt-core` libraries are properly skipped in the wheel repair step. + +### `nanobind` updated to version 2.12.0 + +This release updates the `nanobind` dependency to version 2.12.0, which includes an ABI bump. +Any existing code that uses the `mqt-core` Python bindings will need to be recompiled with the new `nanobind` version. + ## [3.4.0] ### Python wheels @@ -217,7 +227,8 @@ It also requires the `uv` library version 0.5.20 or higher. -[unreleased]: https://github.com/munich-quantum-toolkit/core/compare/v3.4.0...HEAD +[unreleased]: https://github.com/munich-quantum-toolkit/core/compare/v3.5.0...HEAD +[3.5.0]: https://github.com/munich-quantum-toolkit/core/compare/v3.4.0...v3.5.0 [3.4.0]: https://github.com/munich-quantum-toolkit/core/compare/v3.3.0...v3.4.0 [3.3.0]: https://github.com/munich-quantum-toolkit/core/compare/v3.2.0...v3.3.0 [3.2.0]: https://github.com/munich-quantum-toolkit/core/compare/v3.1.0...v3.2.0 From 0cc85024a9cf2a4929373bf343f373f09a870688 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Tue, 21 Apr 2026 22:56:45 -0400 Subject: [PATCH 16/29] =?UTF-8?q?=F0=9F=94=A7=20Reconfigure=20`prek`=20pri?= =?UTF-8?q?orities=20(#1657)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR reconfigures the `prek` priorities to ensure that no two hooks write at the same time. ## Checklist - [x] The pull request only contains commits that are focused and relevant to this change. - [x] ~~I have added appropriate tests that cover the new/changed functionality.~~ - [x] ~~I have updated the documentation to reflect these changes.~~ - [x] ~~I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.~~ - [x] ~~I have added migration instructions to the upgrade guide (if needed).~~ - [x] The changes follow the project's style guidelines and introduce no new warnings. - [x] The changes are fully tested and pass the CI checks. - [x] I have reviewed my own code changes. --- .license-tools-config.json | 2 +- .pre-commit-config.yaml | 114 ++++++++++++++++++------------------- 2 files changed, 55 insertions(+), 61 deletions(-) diff --git a/.license-tools-config.json b/.license-tools-config.json index 7012f899ba..09e2044147 100644 --- a/.license-tools-config.json +++ b/.license-tools-config.json @@ -35,6 +35,6 @@ "uv\\.lock", "py\\.typed", ".*build.*", - "LICENSE" + "(^|/)LICENSE$" ] } diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b6c3ace3a7..34ea1c086d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,8 +15,6 @@ ci: skip: [ty-check] repos: - # Priority 0: Fast validation and independent fixers - ## Standard hooks - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 @@ -26,14 +24,7 @@ repos: - id: end-of-file-fixer priority: 1 - id: trailing-whitespace - priority: 1 - - ## Check the pyproject.toml file - - repo: https://github.com/henryiii/validate-pyproject-schema-store - rev: 2026.04.11 - hooks: - - id: validate-pyproject - priority: 0 + priority: 2 ## Check JSON schemata - repo: https://github.com/python-jsonschema/check-jsonschema @@ -44,7 +35,29 @@ repos: - id: check-readthedocs priority: 0 - ## Catch common capitalization mistakes + ## Check best practices for scientific Python code + - repo: https://github.com/scientific-python/cookie + rev: 2026.04.04 + hooks: + - id: sp-repo-review + additional_dependencies: ["repo-review[cli]"] + priority: 0 + + ## Check pyproject.toml file + - repo: https://github.com/henryiii/validate-pyproject-schema-store + rev: 2026.04.11 + hooks: + - id: validate-pyproject + priority: 0 + + ## Ensure uv.lock is up to date + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.11.6 + hooks: + - id: uv-lock + priority: 0 + + ## Check for common capitalization mistakes - repo: local hooks: - id: disallow-caps @@ -54,36 +67,21 @@ repos: exclude: ^(\.pre-commit-config\.yaml)$ priority: 0 - ## Check for spelling + ## Check for typos - repo: https://github.com/adhtruong/mirrors-typos rev: v1.45.0 hooks: - id: typos - priority: 0 - - ## Check best practices for scientific Python code - - repo: https://github.com/scientific-python/cookie - rev: 2026.04.04 - hooks: - - id: sp-repo-review - additional_dependencies: ["repo-review[cli]"] - priority: 0 + priority: 3 - ## Check for license headers + ## Check license headers - repo: https://github.com/emzeat/mz-lictools rev: v2.9.0 hooks: - id: license-tools - priority: 0 - - ## Ensure uv lock file is up-to-date - - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.11.6 - hooks: - - id: uv-lock - priority: 0 + priority: 4 - ## Tidy up BibTeX files + ## Format BibTeX files with bibtex-tidy - repo: https://github.com/FlamingTempura/bibtex-tidy rev: v1.14.0 hooks: @@ -100,24 +98,17 @@ repos: "--trailing-commas", "--remove-empty-fields", ] - priority: 0 - - # Priority 1: Second-pass fixers + priority: 5 - ## Format C++ files with clang-format - - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v22.1.3 + ## Format configuration files with prettier + - repo: https://github.com/rbubley/mirrors-prettier + rev: v3.8.2 hooks: - - id: clang-format - types_or: [c++, c, cuda] - priority: 1 - - id: clang-format - name: clang-format (TableGen) - types_or: [file] - files: \.td$ - priority: 1 + - id: prettier + types_or: [yaml, markdown, html, css, scss, javascript, json, json5] + priority: 5 - ## Format the CMakeLists.txt files + ## Format CMake files with cmake-format - repo: https://github.com/cheshirekow/cmake-format-precommit rev: v0.6.13 hooks: @@ -125,30 +116,33 @@ repos: additional_dependencies: [pyyaml] types: [file] files: (\.cmake|CMakeLists.txt)(.in)?$ - priority: 1 + priority: 5 - ## Format configuration files with prettier - - repo: https://github.com/rbubley/mirrors-prettier - rev: v3.8.2 + ## Format C++ files with clang-format + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v22.1.3 hooks: - - id: prettier - types_or: [yaml, markdown, html, css, scss, javascript, json, json5] - priority: 1 + - id: clang-format + types_or: [c++, c, cuda] + priority: 5 + - id: clang-format + name: clang-format (TableGen) + types_or: [file] + files: \.td$ + priority: 5 - ## Python linting using ruff + ## Format and lint Python files with ruff - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.15.10 hooks: - id: ruff-format types_or: [python, pyi, jupyter, markdown] - priority: 1 + priority: 6 - id: ruff-check require_serial: true - priority: 2 + priority: 7 - # Priority 2+: Final checks and fixers - - ## Static type checking using ty (needs to run after lockfile update/ruff format, and ruff lint) + ## Check Python types with ty - repo: local hooks: - id: ty-check @@ -159,4 +153,4 @@ repos: types_or: [python, pyi, jupyter] exclude: ^(docs/|mlir/|eval/) pass_filenames: false - priority: 3 + priority: 8 From 2c584157ad6f303e137dd14157c132b9f6905002 Mon Sep 17 00:00:00 2001 From: J4MMlE <113241498+J4MMlE@users.noreply.github.com> Date: Mon, 20 Apr 2026 23:47:30 +0200 Subject: [PATCH 17/29] =?UTF-8?q?=E2=9C=A8=20Add=20rotation-gate=20merging?= =?UTF-8?q?=20using=20quaternions=20(#1407)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR extends the rotation merging pass in the QCO ~~MQTOpt~~ dialect to support quaternion-based gate fusion. The existing rotation merge pass only merges consecutive rotation gates of the **same type** (e.g., `rx + rx` or `ry + ry`) by adding their angles. This PR introduces quaternion-based merging, which can merge rotation gates of different types (currently only single qubit gates `rx`, `ry`, `rz`, `r`, `p`, `u2`, `u`). Quaternions are widely used to represent rotations in three-dimensional space and naturally map to qubit gate rotations around the Bloch sphere. The implementation: 1. Converts rotation gates to quaternion representation 2. Multiplies quaternions using the Hamilton product 3. Converts the resulting quaternion back to a `u` gate. (This could also be done differently in the future, and directly decompose to the correct base gates by using the decomposition from #1182) This optimization is enabled by default but can be **disabled** by adding this flag to the compiler invocation: ```bash mqt-cc --disable-merge-single-qubit-rotation-gates ``` Closes #1029 - [x] The pull request only contains commits that are focused and relevant to this change. - [x] I have added appropriate tests that cover the new/changed functionality. - [x] I have updated the documentation to reflect these changes. - [x] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [x] ~~I have added migration instructions to the upgrade guide (if needed).~~ - [x] The changes follow the project's style guidelines and introduce no new warnings. - [x] The changes are fully tested and pass the CI checks. - [x] I have reviewed my own code changes. --------- Signed-off-by: burgholzer Signed-off-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Co-authored-by: Matthias Reumann Co-authored-by: burgholzer Co-authored-by: Daniel Haag <121057143+denialhaag@users.noreply.github.com> --- CHANGELOG.md | 164 +++++++++++++++++++++++++-------------------------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdc630c4a1..937aa8ffbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,12 +21,12 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Changed - ⬆️ Require LLVM 22.1 for C++ library builds ([#1549]) ([**@burgholzer**], [**@denialhaag**]) -- 📦 Build MLIR by default for C++ library builds ([#1356]) ([**@burgholzer**], [**@denialhaag**]) +- � Build MLIR by default for C++ library builds ([#1356]) ([**@burgholzer**], [**@denialhaag**]) ### Removed -- 🔥 Remove the density matrix support from the MQT Core DD package ([#1466]) ([**@burgholzer**]) -- 🔥 Remove `datastructures` (`ds`) (sub)library from MQT Core ([#1458]) ([**@burgholzer**]) +- � Remove the density matrix support from the MQT Core DD package ([#1466]) ([**@burgholzer**]) +- � Remove `datastructures` (`ds`) (sub)library from MQT Core ([#1458]) ([**@burgholzer**]) ## [3.5.0] - 2026-04-21 @@ -41,10 +41,10 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#350)._ - ⬆️ Update `nanobind` to version 2.12.0 ([#1528]) - ⬆️ Update QDMI to `v1.3.0` ([#1652]) ([**@burgholzer**]) -- 📦 Switch to component-based installation for the MQT Core Python package ([#1596]) ([**@burgholzer**]) +- � Switch to component-based installation for the MQT Core Python package ([#1596]) ([**@burgholzer**]) - ⬆️ Update QDMI to latest version from stable `v1.2.x` branch ([#1593]) ([**@burgholzer**]) - ⬆️ Update `clang-tidy` to version 22 ([#1564]) ([**@denialhaag**], [**@burgholzer**]) -- 👷 Build on `macos-26`/`macos-26-intel` by default and `macos-15`/`macos-15-intel` for extensive tests ([#1571]) ([**@denialhaag**]) +- � Build on `macos-26`/`macos-26-intel` by default and `macos-15`/`macos-15-intel` for extensive tests ([#1571]) ([**@denialhaag**]) ## [3.4.1] - 2026-02-01 @@ -56,11 +56,11 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#350)._ - ⬆️ Update `spdlog` to version 1.17.0 ([#1453]) ([**@burgholzer**]) - ♻️ Use `llc` instead of random `clang` for compiling QIR test circuits to improve robustness and handle opaque pointers correctly across LLVM versions ([#1447]) ([**@burgholzer**]) - ♻️ Extract singleton pattern into reusable template base class for QDMI devices and driver ([#1444]) ([**@ystade**], [**@burgholzer**]) -- 🚚 Reorganize QDMI code structure by moving devices into dedicated subdirectories and separating driver and common utilities ([#1444]) ([**@ystade**]) +- � Reorganize QDMI code structure by moving devices into dedicated subdirectories and separating driver and common utilities ([#1444]) ([**@ystade**]) ### Removed -- 🔥 No longer actively type check Python code with `mypy` and solely rely on `ty` ([#1437]) ([**@burgholzer**]) +- � No longer actively type check Python code with `mypy` and solely rely on `ty` ([#1437]) ([**@burgholzer**]) ## [3.4.0] - 2026-01-08 @@ -81,44 +81,44 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#340)._ ### Changed -- 📦🏁 Build Windows x86 wheels on `windows-2025` runner for newer compiler ([#1415]) ([**@burgholzer**]) -- 👷 Build on `macos-15`/`windows-2025` by default and `macos-14`/`windows-2022` for extensive tests ([#1414]) ([**@burgholzer**]) -- 📦🍎 Build macOS arm64 wheels on macos-15 runner for newer compiler ([#1413]) ([**@burgholzer**]) +- �� Build Windows x86 wheels on `windows-2025` runner for newer compiler ([#1415]) ([**@burgholzer**]) +- � Build on `macos-15`/`windows-2025` by default and `macos-14`/`windows-2022` for extensive tests ([#1414]) ([**@burgholzer**]) +- �� Build macOS arm64 wheels on macos-15 runner for newer compiler ([#1413]) ([**@burgholzer**]) - ⚡ Improve uv build caching by removing unconditional `reinstall-package` and configuring dedicated `cache-keys` ([#1412]) ([**@burgholzer**]) -- 👨‍💻📦 Build `spdlog` and QDMI generators as shared libraries in Python package builds ([#1411], [#1403]) ([**@burgholzer**]) -- ♻️🏁 Remove Windows-specific restrictions for dynamic QDMI device library handling ([#1406]) ([**@burgholzer**]) +- �‍�� Build `spdlog` and QDMI generators as shared libraries in Python package builds ([#1411], [#1403]) ([**@burgholzer**]) +- ♻️� Remove Windows-specific restrictions for dynamic QDMI device library handling ([#1406]) ([**@burgholzer**]) - ♻️ Migrate Python bindings from `pybind11` to `nanobind` ([#1383]) ([**@denialhaag**], [**@burgholzer**]) -- 📦️ Provide Stable ABI wheels for Python 3.12+ ([#1383]) ([**@burgholzer**], [**@denialhaag**]) -- 🚚 Create dedicated `mqt.core.na` submodule to closely follow the structure of other submodules ([#1383]) ([**@burgholzer**]) +- �️ Provide Stable ABI wheels for Python 3.12+ ([#1383]) ([**@burgholzer**], [**@denialhaag**]) +- � Create dedicated `mqt.core.na` submodule to closely follow the structure of other submodules ([#1383]) ([**@burgholzer**]) - ✨ Add common definitions and utilities for QDMI ([#1355]) ([**@burgholzer**]) -- 🚚 Move `NA` QDMI device in its right place next to other QDMI devices ([#1355]) ([**@burgholzer**]) +- � Move `NA` QDMI device in its right place next to other QDMI devices ([#1355]) ([**@burgholzer**]) - ♻️ Allow repeated loading of QDMI device library with potentially different session configurations ([#1355]) ([**@burgholzer**]) - ♻️ Enable thread-safe reference counting for QDMI devices singletons ([#1355]) ([**@burgholzer**]) - ♻️ Refactor `FoMaC` singleton to instantiable `Session` class with configurable authentication parameters ([#1355]) ([**@marcelwa**]) -- 👷 Stop testing on `ubuntu-22.04` and `ubuntu-22.04-arm` runners ([#1359]) ([**@denialhaag**], [**@burgholzer**]) -- 👷 Stop testing with `clang-19` and start testing with `clang-21` ([#1359]) ([**@denialhaag**], [**@burgholzer**]) -- 👷 Fix macOS tests with Homebrew Clang via new `munich-quantum-toolkit/workflows` version ([#1359]) ([**@denialhaag**], [**@burgholzer**]) -- 👷 Re-enable macOS tests with GCC by disabling module scanning ([#1359]) ([**@denialhaag**], [**@burgholzer**]) +- � Stop testing on `ubuntu-22.04` and `ubuntu-22.04-arm` runners ([#1359]) ([**@denialhaag**], [**@burgholzer**]) +- � Stop testing with `clang-19` and start testing with `clang-21` ([#1359]) ([**@denialhaag**], [**@burgholzer**]) +- � Fix macOS tests with Homebrew Clang via new `munich-quantum-toolkit/workflows` version ([#1359]) ([**@denialhaag**], [**@burgholzer**]) +- � Re-enable macOS tests with GCC by disabling module scanning ([#1359]) ([**@denialhaag**], [**@burgholzer**]) - ♻️ Group circuit operations into scheduling units for MLIR routing ([#1301]) ([**@MatthiasReumann**]) -- 👷 Use `munich-quantum-software/setup-mlir` to set up MLIR ([#1294]) ([**@denialhaag**]) +- � Use `munich-quantum-software/setup-mlir` to set up MLIR ([#1294]) ([**@denialhaag**]) - ♻️ Preserve tuple structure and improve site type clarity of the MQT NA Default QDMI Device ([#1299]) ([**@marcelwa**]) - ♻️ Move DD package evaluation module to standalone script ([#1327]) ([**@burgholzer**]) - ⬆️ Bump QDMI version to 1.2.0 ([#1243]) ([**@marcelwa**], [**@burgholzer**]) ### Fixed -- 🔧 Install all available QDMI device targets in Python package builds ([#1403]) ([**@burgholzer**]) -- 🐛 Fix operation validation in Qiskit backend to handle device-specific gate naming conventions ([#1384]) ([**@marcelwa**]) -- 🐛 Fix conditional branch handling when importing MLIR from `QuantumComputation` ([#1378]) ([**@lirem101**]) -- 🐛 Fix custom QDMI property and parameter handling in SC and NA devices ([#1355]) ([**@burgholzer**]) -- 🚨 Fix argument naming of `QuantumComputation` and `CompoundOperation` dunder methods for properly implementing the `MutableSequence` protocol ([#1338]) ([**@burgholzer**]) -- 🐛 Fix memory management in dynamic QDMI device by making it explicit ([#1336]) ([**@ystade**]) +- � Install all available QDMI device targets in Python package builds ([#1403]) ([**@burgholzer**]) +- � Fix operation validation in Qiskit backend to handle device-specific gate naming conventions ([#1384]) ([**@marcelwa**]) +- � Fix conditional branch handling when importing MLIR from `QuantumComputation` ([#1378]) ([**@lirem101**]) +- � Fix custom QDMI property and parameter handling in SC and NA devices ([#1355]) ([**@burgholzer**]) +- � Fix argument naming of `QuantumComputation` and `CompoundOperation` dunder methods for properly implementing the `MutableSequence` protocol ([#1338]) ([**@burgholzer**]) +- � Fix memory management in dynamic QDMI device by making it explicit ([#1336]) ([**@ystade**]) ### Removed -- 🔥 Remove wheel builds for Python 3.13t ([#1371]) ([**@burgholzer**]) -- 🔥 Remove the `evaluation` extra from the MQT Core Python package ([#1327]) ([**@burgholzer**]) -- 🔥 Remove the `mqt-core-dd-compare` entry point from the MQT Core Python package ([#1327]) ([**@burgholzer**]) +- � Remove wheel builds for Python 3.13t ([#1371]) ([**@burgholzer**]) +- � Remove the `evaluation` extra from the MQT Core Python package ([#1327]) ([**@burgholzer**]) +- � Remove the `mqt-core-dd-compare` entry point from the MQT Core Python package ([#1327]) ([**@burgholzer**]) ## [3.3.3] - 2025-11-10 @@ -128,7 +128,7 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#340)._ ### Fixed -- 🐛 Revert change to `opTypeFromString()` signature made in [#1283] ([#1300]) ([**@denialhaag**]) +- � Revert change to `opTypeFromString()` signature made in [#1283] ([#1300]) ([**@denialhaag**]) ## [3.3.2] - 2025-11-04 @@ -142,14 +142,14 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#340)._ ### Fixed -- 🐛 Fix edge-case in validation of `NAComputation` ([#1276]) ([**@ystade**]) -- 🐛 Allow integer QASM version declarations ([#1269]) ([**@denialhaag**]) +- � Fix edge-case in validation of `NAComputation` ([#1276]) ([**@ystade**]) +- � Allow integer QASM version declarations ([#1269]) ([**@denialhaag**]) ## [3.3.1] - 2025-10-14 ### Fixed -- 🐛 Ensure `spdlog` dependency can be found from `mqt-core` install ([#1263]) ([**@burgholzer**]) +- � Ensure `spdlog` dependency can be found from `mqt-core` install ([#1263]) ([**@burgholzer**]) ## [3.3.0] - 2025-10-13 @@ -157,7 +157,7 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#330)._ ### Added -- 👷 Enable testing on Python 3.14 ([#1246]) ([**@denialhaag**]) +- � Enable testing on Python 3.14 ([#1246]) ([**@denialhaag**]) - ✨ Add dedicated `PlacementPass` to MLIR transpilation routines ([#1232]) ([**@MatthiasReumann**]) - ✨ Add an NA-specific FoMaC implementation ([#1223], [#1236]) ([**@ystade**], [**@burgholzer**]) - ✨ Enable import of BarrierOp into MQTRef ([#1224]) ([**@denialhaag**]) @@ -165,14 +165,14 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#330)._ - ✨ Add QIR runtime using DD-based simulation ([#1210]) ([**@ystade**], [**@burgholzer**]) - ✨ Add SWAP reconstruction patterns to the newly-named `SwapReconstructionAndElision` MLIR pass ([#1207]) ([**@taminob**], [**@burgholzer**]) - ✨ Add two-way conversions between MQTRef and QIR ([#1091]) ([**@li-mingbao**]) -- 🚸 Define custom assembly formats for MLIR operations ([#1209]) ([**@denialhaag**]) +- � Define custom assembly formats for MLIR operations ([#1209]) ([**@denialhaag**]) - ✨ Add support for translating `IfElseOperation`s to the `MQTRef` MLIR dialect ([#1164]) ([**@denialhaag**], [**@burgholzer**]) - ✨ Add MQT's implementation of a generic FoMaC with Python bindings ([#1150], [#1186], [#1223]) ([**@ystade**]) - ✨ Add new MLIR pass `ElidePermutations` for SWAP gate elimination ([#1151]) ([**@taminob**]) - ✨ Add new pattern to MLIR pass `GateElimination` for identity gate removal ([#1140]) ([**@taminob**]) - ✨ Add Clifford block collection pass to `CircuitOptimizer` module ([#885]) ([**jannikpflieger**], [**@burgholzer**]) - ✨ Add `isControlled()` method to the `UnitaryInterface` MLIR class ([#1157]) ([**@taminob**], [**@burgholzer**]) -- 📝 Integrate generated MLIR documentation ([#1147]) ([**@denialhaag**], [**@burgholzer**]) +- � Integrate generated MLIR documentation ([#1147]) ([**@denialhaag**], [**@burgholzer**]) - ✨ Add `IfElseOperation` to C++ library and Python package to support Qiskit's `IfElseOp` ([#1117]) ([**@denialhaag**], [**@burgholzer**], [**@lavanya-m-k**]) - ✨ Add `allocQubit` and `deallocQubit` operations for dynamically working with single qubits to the MLIR dialects ([#1139]) ([**@DRovara**], [**@burgholzer**]) - ✨ Add `qubit` operation for static qubit addressing to the MLIR dialects ([#1098], [#1116]) ([**@MatthiasReumann**]) @@ -184,41 +184,41 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#330)._ ### Changed - ♻️ Replace custom `AllocOp`, `DeallocOp`, `ExtractOp`, and `InsertOp` with MLIR-native `memref` operations ([#1211]) ([**@denialhaag**]) -- 🚚 Rename MLIR pass `ElidePermutations` to `SwapReconstructionAndElision` ([#1207]) ([**@taminob**]) +- � Rename MLIR pass `ElidePermutations` to `SwapReconstructionAndElision` ([#1207]) ([**@taminob**]) - ⬆️ Require LLVM 21 for building the MLIR library ([#1180]) ([**@denialhaag**]) - ⬆️ Update to version 21 of `clang-tidy` ([#1180]) ([**@denialhaag**]) -- 🚚 Rename MLIR pass `CancelConsecutiveInverses` to `GateElimination` ([#1140]) ([**@taminob**]) -- 🚚 Rename `xxminusyy` to `xx_minus_yy` and `xxplusyy` to `xx_plus_yy` in MLIR dialects ([#1071]) ([**@BertiFlorea**], [**@denialhaag**]) -- 🚸 Add custom assembly format for operations in the MLIR dialects ([#1139]) ([**@burgholzer**]) -- 🚸 Enable `InferTypeOpInterface` in the MLIR dialects to reduce explicit type information ([#1139]) ([**@burgholzer**]) -- 🚚 Rename `check-quantum-opt` test target to `mqt-core-mlir-lit-test` ([#1139]) ([**@burgholzer**]) +- � Rename MLIR pass `CancelConsecutiveInverses` to `GateElimination` ([#1140]) ([**@taminob**]) +- � Rename `xxminusyy` to `xx_minus_yy` and `xxplusyy` to `xx_plus_yy` in MLIR dialects ([#1071]) ([**@BertiFlorea**], [**@denialhaag**]) +- � Add custom assembly format for operations in the MLIR dialects ([#1139]) ([**@burgholzer**]) +- � Enable `InferTypeOpInterface` in the MLIR dialects to reduce explicit type information ([#1139]) ([**@burgholzer**]) +- � Rename `check-quantum-opt` test target to `mqt-core-mlir-lit-test` ([#1139]) ([**@burgholzer**]) - ♻️ Update the `measure` operations in the MLIR dialects to no longer support more than one qubit being measured at once ([#1106]) ([**@DRovara**]) -- 🚚 Rename `XXminusYY` to `XXminusYYOp` and `XXplusYY` to `XXplusYYOp` in MLIR dialects ([#1099]) ([**@denialhaag**]) -- 🚚 Rename `MQTDyn` MLIR dialect to `MQTRef` ([#1098]) ([**@MatthiasReumann**]) +- � Rename `XXminusYY` to `XXminusYYOp` and `XXplusYY` to `XXplusYYOp` in MLIR dialects ([#1099]) ([**@denialhaag**]) +- � Rename `MQTDyn` MLIR dialect to `MQTRef` ([#1098]) ([**@MatthiasReumann**]) ### Removed -- 🔥 Drop support for Python 3.9 ([#1181]) ([**@denialhaag**]) -- 🔥 Remove `ClassicControlledOperation` from C++ library and Python package ([#1117]) ([**@denialhaag**]) +- � Drop support for Python 3.9 ([#1181]) ([**@denialhaag**]) +- � Remove `ClassicControlledOperation` from C++ library and Python package ([#1117]) ([**@denialhaag**]) ### Fixed -- 🐛 Fix CMake installation to make `find_package(mqt-core CONFIG)` succeed ([#1247]) ([**@burgholzer**], [**@denialhaag**]) -- 🏁 Fix stack overflows in OpenQASM layout parsing on Windows for large circuits ([#1235]) ([**@burgholzer**]) +- � Fix CMake installation to make `find_package(mqt-core CONFIG)` succeed ([#1247]) ([**@burgholzer**], [**@denialhaag**]) +- � Fix stack overflows in OpenQASM layout parsing on Windows for large circuits ([#1235]) ([**@burgholzer**]) - ✨ Add missing `StandardOperation` conversions in MLIR roundtrip pass ([#1071]) ([**@BertiFlorea**], [**@denialhaag**]) ## [3.2.1] - 2025-08-01 ### Fixed -- 🐛 Fix usage of `std::accumulate` by changing accumulator parameter from reference to value ([#1089]) ([**@denialhaag**]) -- 🐛 Fix erroneous `contains` check in DD package ([#1088]) ([**@denialhaag**]) +- � Fix usage of `std::accumulate` by changing accumulator parameter from reference to value ([#1089]) ([**@denialhaag**]) +- � Fix erroneous `contains` check in DD package ([#1088]) ([**@denialhaag**]) ## [3.2.0] - 2025-07-31 ### Added -- 🐍 Build Python 3.14 wheels ([#1076]) ([**@denialhaag**]) +- � Build Python 3.14 wheels ([#1076]) ([**@denialhaag**]) - ✨ Add MQT-internal MLIR dialect conversions ([#1001]) ([**@li-mingbao**]) ### Changed @@ -235,17 +235,17 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#310)._ - ✨ Add MLIR pass for merging rotation gates ([#1019]) ([**@denialhaag**]) - ✨ Add functions to generate random vector DDs ([#975]) ([**@MatthiasReumann**]) - ✨ Add function to approximate decision diagrams ([#908]) ([**@MatthiasReumann**]) -- 📦 Add Windows ARM64 wheels ([#926]) ([**@burgholzer**]) -- 📝 Add documentation page for MLIR ([#931]) ([**@ystade**]) +- � Add Windows ARM64 wheels ([#926]) ([**@burgholzer**]) +- � Add documentation page for MLIR ([#931]) ([**@ystade**]) - ✨ Initial implementation of the mqtdyn Dialect ([#900]) ([**@DRovara**], [**@ystade**]) ### Fixed -- 🐛 Fix bug in MLIR roundtrip passes caused by accessing an invalidated iterator after erasure in a loop ([#932]) ([**@flowerthrower**]) -- 🐛 Add missing support for `sxdg` gates in Qiskit circuit import ([#930]) ([**@burgholzer**]) -- 🐛 Fix bug related to initialization of operations with duplicate operands ([#964]) ([**@ystade**]) -- 🐛 Open issue for Qiskit upstream test only when the test is actually failing not when it was cancelled ([#973]) ([**@ystade**]) -- 🐛 Fix parsing of `GPhase` in the `MQTOpt` MLIR dialect ([#1042]) ([**@ystade**], [**@DRovara**]) +- � Fix bug in MLIR roundtrip passes caused by accessing an invalidated iterator after erasure in a loop ([#932]) ([**@flowerthrower**]) +- � Add missing support for `sxdg` gates in Qiskit circuit import ([#930]) ([**@burgholzer**]) +- � Fix bug related to initialization of operations with duplicate operands ([#964]) ([**@ystade**]) +- � Open issue for Qiskit upstream test only when the test is actually failing not when it was cancelled ([#973]) ([**@ystade**]) +- � Fix parsing of `GPhase` in the `MQTOpt` MLIR dialect ([#1042]) ([**@ystade**], [**@DRovara**]) ### Changed @@ -264,18 +264,18 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#310)._ ### Added -- 📝 Add JOSS journal reference and citation information ([#913]) ([**@burgholzer**]) -- 📝 Add new links to Python package metadata ([#911]) ([**@burgholzer**]) +- � Add JOSS journal reference and citation information ([#913]) ([**@burgholzer**]) +- � Add new links to Python package metadata ([#911]) ([**@burgholzer**]) ### Fixed -- 📝 Fix old links in Python package metadata ([#911]) ([**@burgholzer**]) +- � Fix old links in Python package metadata ([#911]) ([**@burgholzer**]) ## [3.0.1] - 2025-04-07 ### Fixed -- 🐛 Fix doxygen build on RtD to include C++ API docs ([#912]) ([**@burgholzer**]) +- � Fix doxygen build on RtD to include C++ API docs ([#912]) ([**@burgholzer**]) ## [3.0.0] - 2025-04-06 @@ -289,15 +289,15 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#300)._ - ✨ Support for Qiskit 2.0+ ([#860]) ([**@burgholzer**]) - ✨ Add initial infrastructure for MLIR within the MQT ([#878], [#879], [#892], [#893], [#895]) ([**@burgholzer**], [**@ystade**], [**@DRovara**], [**@flowerthrower**], [**@BertiFlorea**]) - ✨ Add State Preparation Algorithm ([#543]) ([**@M-J-Hochreiter**]) -- 🚸 Add support for indexed identifiers to OpenQASM 3 parser ([#832]) ([**@burgholzer**]) -- 🚸 Allow indexed registers as operation arguments ([#839]) ([**@burgholzer**]) -- 📝 Add documentation for the DD package ([#831]) ([**@burgholzer**]) -- 📝 Add documentation for the ZX package ([#817]) ([**@pehamTom**]) -- 📝 Add C++ API docs setup ([#817]) ([**@pehamTom**], [**@burgholzer**]) +- � Add support for indexed identifiers to OpenQASM 3 parser ([#832]) ([**@burgholzer**]) +- � Allow indexed registers as operation arguments ([#839]) ([**@burgholzer**]) +- � Add documentation for the DD package ([#831]) ([**@burgholzer**]) +- � Add documentation for the ZX package ([#817]) ([**@pehamTom**]) +- � Add C++ API docs setup ([#817]) ([**@pehamTom**], [**@burgholzer**]) ### Changed -- **Breaking**: 🚚 MQT Core has moved to the [munich-quantum-toolkit] GitHub organization +- **Breaking**: � MQT Core has moved to the [munich-quantum-toolkit] GitHub organization - **Breaking**: ✨ Adopt [PEP 735] dependency groups ([#762]) ([**@burgholzer**]) - **Breaking**: ♻️ Encapsulate the OpenQASM parser in its own library ([#822]) ([**@burgholzer**]) - **Breaking**: ♻️ Replace `Config` template from DD package with constructor argument ([#886]) ([**@burgholzer**]) @@ -308,29 +308,29 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#300)._ - **Breaking**: ♻️ Refactor `NAComputation` class hierarchy ([#846], [#877]) ([**@ystade**]) - **Breaking**: ⬆️ Bump minimum required CMake version to `3.24.0` ([#879]) ([**@burgholzer**]) - **Breaking**: ⬆️ Bump minimum required `uv` version to `0.5.20` ([#802]) ([**@burgholzer**]) -- 📝 Rework existing project documentation ([#789], [#842]) ([**@burgholzer**]) -- 📄 Use [PEP 639] license expressions ([#847]) ([**@burgholzer**]) +- � Rework existing project documentation ([#789], [#842]) ([**@burgholzer**]) +- � Use [PEP 639] license expressions ([#847]) ([**@burgholzer**]) ### Removed -- **Breaking**: 🔥 Remove the `Teleportation` gate from the IR ([#882]) ([**@burgholzer**]) -- **Breaking**: 🔥 Remove parsers for `.real`, `.qc`, `.tfc`, and `GRCS` files ([#822]) ([**@burgholzer**]) -- **Breaking**: 🔥 Remove tensor dump functionality ([#798]) ([**@burgholzer**]) -- **Breaking**: 🔥 Remove `extract_probability_vector` functionality ([#883]) ([**@burgholzer**]) +- **Breaking**: � Remove the `Teleportation` gate from the IR ([#882]) ([**@burgholzer**]) +- **Breaking**: � Remove parsers for `.real`, `.qc`, `.tfc`, and `GRCS` files ([#822]) ([**@burgholzer**]) +- **Breaking**: � Remove tensor dump functionality ([#798]) ([**@burgholzer**]) +- **Breaking**: � Remove `extract_probability_vector` functionality ([#883]) ([**@burgholzer**]) ### Fixed -- 🐛 Fix Qiskit layout import and handling ([#849], [#858]) ([**@burgholzer**]) -- 🐛 Properly handle timing literals in QASM parser ([#724]) ([**@burgholzer**]) -- 🐛 Fix stripping of idle qubits ([#763]) ([**@burgholzer**]) -- 🐛 Fix permutation handling in OpenQASM dump ([#810]) ([**@burgholzer**]) -- 🐛 Fix out-of-bounds error in ZX `EdgeIterator` ([#758]) ([**@burgholzer**]) -- 🐛 Fix endianness in DCX and XX_minus_YY gate matrix definition ([#741]) ([**@burgholzer**]) -- 🐛 Fix needless dummy register in empty circuit construction ([#758]) ([**@burgholzer**]) +- � Fix Qiskit layout import and handling ([#849], [#858]) ([**@burgholzer**]) +- � Properly handle timing literals in QASM parser ([#724]) ([**@burgholzer**]) +- � Fix stripping of idle qubits ([#763]) ([**@burgholzer**]) +- � Fix permutation handling in OpenQASM dump ([#810]) ([**@burgholzer**]) +- � Fix out-of-bounds error in ZX `EdgeIterator` ([#758]) ([**@burgholzer**]) +- � Fix endianness in DCX and XX_minus_YY gate matrix definition ([#741]) ([**@burgholzer**]) +- � Fix needless dummy register in empty circuit construction ([#758]) ([**@burgholzer**]) ## [2.7.0] - 2024-10-08 -_📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-toolkit/core/releases) for previous changelogs._ +_� Refer to the [GitHub Release Notes](https://github.com/munich-quantum-toolkit/core/releases) for previous changelogs._ From 652534fa51699751814f80a5893104dc7fa72efd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 25 Apr 2026 02:23:15 +0000 Subject: [PATCH 18/29] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=A9=B9=20Update?= =?UTF-8?q?=20patch=20updates=20(#1669)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | Pending | |---|---|---|---|---| | [adhtruong/mirrors-typos](https://redirect.github.com/adhtruong/mirrors-typos) | repository | patch | `v1.45.0` → `v1.45.1` | | | [astral-sh/ruff-pre-commit](https://redirect.github.com/astral-sh/ruff-pre-commit) | repository | patch | `v0.15.10` → `v0.15.11` | `v0.15.12` | | [astral-sh/uv-pre-commit](https://redirect.github.com/astral-sh/uv-pre-commit) | repository | patch | `0.11.6` → `0.11.7` | | | [henryiii/validate-pyproject-schema-store](https://redirect.github.com/henryiii/validate-pyproject-schema-store) | repository | patch | `2026.04.11` → `2026.04.17` | `2026.04.24` (+2) | | [rbubley/mirrors-prettier](https://redirect.github.com/rbubley/mirrors-prettier) | repository | patch | `v3.8.2` → `v3.8.3` | | Note: The `pre-commit` manager in Renovate is not supported by the `pre-commit` maintainers or community. Please do not report any problems there, instead [create a Discussion in the Renovate repository](https://redirect.github.com/renovatebot/renovate/discussions/new) if you have any questions. --- ### Release Notes
adhtruong/mirrors-typos (adhtruong/mirrors-typos) ### [`v1.45.1`](https://redirect.github.com/adhtruong/mirrors-typos/compare/v1.45.0...v1.45.1) [Compare Source](https://redirect.github.com/adhtruong/mirrors-typos/compare/v1.45.0...v1.45.1)
astral-sh/ruff-pre-commit (astral-sh/ruff-pre-commit) ### [`v0.15.11`](https://redirect.github.com/astral-sh/ruff-pre-commit/releases/tag/v0.15.11) [Compare Source](https://redirect.github.com/astral-sh/ruff-pre-commit/compare/v0.15.10...v0.15.11) See:
astral-sh/uv-pre-commit (astral-sh/uv-pre-commit) ### [`v0.11.7`](https://redirect.github.com/astral-sh/uv-pre-commit/releases/tag/0.11.7) [Compare Source](https://redirect.github.com/astral-sh/uv-pre-commit/compare/0.11.6...0.11.7) See:
henryiii/validate-pyproject-schema-store (henryiii/validate-pyproject-schema-store) ### [`v2026.04.17`](https://redirect.github.com/henryiii/validate-pyproject-schema-store/compare/2026.04.16...2026.04.17) [Compare Source](https://redirect.github.com/henryiii/validate-pyproject-schema-store/compare/2026.04.16...2026.04.17) ### [`v2026.04.16`](https://redirect.github.com/henryiii/validate-pyproject-schema-store/compare/2026.04.11...2026.04.16) [Compare Source](https://redirect.github.com/henryiii/validate-pyproject-schema-store/compare/2026.04.11...2026.04.16)
rbubley/mirrors-prettier (rbubley/mirrors-prettier) ### [`v3.8.3`](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.8.2...v3.8.3) [Compare Source](https://redirect.github.com/rbubley/mirrors-prettier/compare/v3.8.2...v3.8.3)
--- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "every weekend" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 34ea1c086d..808508ee12 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -45,14 +45,14 @@ repos: ## Check pyproject.toml file - repo: https://github.com/henryiii/validate-pyproject-schema-store - rev: 2026.04.11 + rev: 2026.04.17 hooks: - id: validate-pyproject priority: 0 ## Ensure uv.lock is up to date - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.11.6 + rev: 0.11.7 hooks: - id: uv-lock priority: 0 @@ -69,7 +69,7 @@ repos: ## Check for typos - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.45.0 + rev: v1.45.1 hooks: - id: typos priority: 3 @@ -102,7 +102,7 @@ repos: ## Format configuration files with prettier - repo: https://github.com/rbubley/mirrors-prettier - rev: v3.8.2 + rev: v3.8.3 hooks: - id: prettier types_or: [yaml, markdown, html, css, scss, javascript, json, json5] @@ -133,7 +133,7 @@ repos: ## Format and lint Python files with ruff - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.10 + rev: v0.15.11 hooks: - id: ruff-format types_or: [python, pyi, jupyter, markdown] From 8fc1e7553854b889543613dd5477270629520129 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 25 Apr 2026 08:57:09 +0200 Subject: [PATCH 19/29] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=90=8D=20Update?= =?UTF-8?q?=20dependency=20ty=20to=20v0.0.31=20(#1668)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 2 +- uv.lock | 44 ++++++++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 762f3aca0b..ea92e9e203 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -367,5 +367,5 @@ dev = [ {include-group = "test"}, "lit>=18.1.8", "nox>=2025.11.12", - "ty==0.0.29", + "ty==0.0.31", ] diff --git a/uv.lock b/uv.lock index 3c5abef803..f3067e3d25 100644 --- a/uv.lock +++ b/uv.lock @@ -1703,7 +1703,7 @@ dev = [ { name = "qiskit", extras = ["qasm3-import"], specifier = ">=1.0.0" }, { name = "scikit-build-core", specifier = ">=0.12.2" }, { name = "setuptools-scm", specifier = ">=9.2.2" }, - { name = "ty", specifier = "==0.0.29" }, + { name = "ty", specifier = "==0.0.31" }, ] docs = [ { name = "breathe", specifier = ">=4.36.0" }, @@ -2233,7 +2233,7 @@ name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess" }, + { name = "ptyprocess", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ @@ -3674,26 +3674,26 @@ wheels = [ [[package]] name = "ty" -version = "0.0.29" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/d5/853561de49fae38c519e905b2d8da9c531219608f1fccc47a0fc2c896980/ty-0.0.29.tar.gz", hash = "sha256:e7936cca2f691eeda631876c92809688dbbab68687c3473f526cd83b6a9228d8", size = 5469221, upload-time = "2026-04-05T15:01:21.328Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/03/b7/911f9962115acfa24e3b2ec9d4992dd994c38e8769e1b1d7680bb4d28a51/ty-0.0.29-py3-none-linux_armv6l.whl", hash = "sha256:b8a40955f7660d3eaceb0d964affc81b790c0765e7052921a5f861ff8a471c30", size = 10568206, upload-time = "2026-04-05T15:01:19.165Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c3/fcae2167d4c77a97269f92f11d1b43b03617f81de1283d5d05b43432110c/ty-0.0.29-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6b6849adae15b00bbe2d3c5b078967dcb62eba37d38936b8eeb4c81a82d2e3b8", size = 10442530, upload-time = "2026-04-05T15:01:28.471Z" }, - { url = "https://files.pythonhosted.org/packages/97/33/5a6bfa240cfcb9c36046ae2459fa9ea23238d20130d8656ff5ac4d6c012a/ty-0.0.29-py3-none-macosx_11_0_arm64.whl", hash = "sha256:dcdd9b17209788152f7b7ea815eda07989152325052fe690013537cc7904ce49", size = 9915735, upload-time = "2026-04-05T15:01:10.365Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1e/318f45fae232118e81a6306c30f50de42c509c412128d5bd231eab699ffb/ty-0.0.29-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d8ed4789bae78ffaf94462c0d25589a734cab0366b86f2bbcb1bb90e1a7a169", size = 10419748, upload-time = "2026-04-05T15:01:32.375Z" }, - { url = "https://files.pythonhosted.org/packages/a9/a8/5687872e2ab5a0f7dd4fd8456eac31e9381ad4dc74961f6f29965ad4dd91/ty-0.0.29-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91ec374b8565e0ad0900011c24641ebbef2da51adbd4fb69ff3280c8a7eceb02", size = 10394738, upload-time = "2026-04-05T15:01:06.473Z" }, - { url = "https://files.pythonhosted.org/packages/de/68/015d118097eeb95e6a44c4abce4c0a28b7b9dfb3085b7f0ee48e4f099633/ty-0.0.29-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:298a8d5faa2502d3810bbbb47a030b9455495b9921594206043c785dd61548cf", size = 10910613, upload-time = "2026-04-05T15:01:17.17Z" }, - { url = "https://files.pythonhosted.org/packages/1c/01/47ce3c6c53e0670eadbe80756b167bf80ed6681d1ba57cfde2e8065a13d1/ty-0.0.29-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c8fba1a3524c6109d1e020d92301c79d41bf442fa8d335b9fa366239339cb70", size = 11475750, upload-time = "2026-04-05T15:01:30.461Z" }, - { url = "https://files.pythonhosted.org/packages/c4/cf/e361845b1081c9264ad5b7c963231bab03f2666865a9f2a115c4233f2137/ty-0.0.29-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c48adf88a70d264128c39ee922ed14a947817fced1e93c08c1a89c9244edcde", size = 11190055, upload-time = "2026-04-05T15:01:12.369Z" }, - { url = "https://files.pythonhosted.org/packages/79/12/0fb0857e9a62cb11586e9a712103877bbf717f5fb570d16634408cfdefee/ty-0.0.29-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ce0a7a0e96bc7b42518cd3a1a6a6298ef64ff40ca4614355c1aa807059b5c6f", size = 11020539, upload-time = "2026-04-05T15:01:37.022Z" }, - { url = "https://files.pythonhosted.org/packages/20/36/5a26753802083f80cd125db6c4348ad42b3c982ec36e718e0bf4c18f75e5/ty-0.0.29-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a6ac86a05b4a3731d45365ab97780acc7b8146fa62fccb3cbe94fe6546c67a97", size = 10396399, upload-time = "2026-04-05T15:01:26.167Z" }, - { url = "https://files.pythonhosted.org/packages/00/e6/b4e75b5752239ab3ab400f19faef4dbef81d05aab5d3419fda0c062a3765/ty-0.0.29-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6bbbf53141af0f3150bf288d716263f1a3550054e4b3551ca866d38192ba9891", size = 10421461, upload-time = "2026-04-05T15:01:08.367Z" }, - { url = "https://files.pythonhosted.org/packages/c0/21/1084b5b609f9abed62070ec0b31c283a403832a6310c8bbc208bd45ee1e6/ty-0.0.29-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1c9e06b770c1d0ff5efc51e34312390db31d53fcf3088163f413030b42b74f84", size = 10599187, upload-time = "2026-04-05T15:01:23.52Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a1/ce19a2ca717bbcc1ee11378aba52ef70b6ce5b87245162a729d9fdc2360f/ty-0.0.29-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0307fe37e3f000ef1a4ae230bbaf511508a78d24a5e51b40902a21b09d5e6037", size = 11121198, upload-time = "2026-04-05T15:01:15.22Z" }, - { url = "https://files.pythonhosted.org/packages/6b/6b/f1430b279af704321566ce7ec2725d3d8258c2f815ebd93e474c64cd4543/ty-0.0.29-py3-none-win32.whl", hash = "sha256:7a2a898217960a825f8bc0087e1fdbaf379606175e98f9807187221d53a4a8ed", size = 9995331, upload-time = "2026-04-05T15:01:01.32Z" }, - { url = "https://files.pythonhosted.org/packages/d2/ef/3ef01c17785ff9a69378465c7d0faccd48a07b163554db0995e5d65a5a23/ty-0.0.29-py3-none-win_amd64.whl", hash = "sha256:fc1294200226b91615acbf34e0a9ad81caf98c081e9c6a912a31b0a7b603bc3f", size = 11023644, upload-time = "2026-04-05T15:01:04.432Z" }, - { url = "https://files.pythonhosted.org/packages/2c/55/87280a994d6a2d2647c65e12abbc997ed49835794366153c04c4d9304d76/ty-0.0.29-py3-none-win_arm64.whl", hash = "sha256:f9794bbd1bb3ce13f78c191d0c89ae4c63f52c12b6daa0c6fe220b90d019d12c", size = 10428165, upload-time = "2026-04-05T15:01:34.665Z" }, +version = "0.0.31" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/31/cc/5ea5d3a72216c8c2bf77d83066dd4f3553532d0aacc03d4a8397dd9845e1/ty-0.0.31.tar.gz", hash = "sha256:4a4094292d9671caf3b510c7edf36991acd9c962bb5d97205374ffed9f541c45", size = 5516619, upload-time = "2026-04-15T15:47:59.87Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/10/ea805cbbd75d5d50792551a2b383de8521eeab0c44f38c73e12819ced65e/ty-0.0.31-py3-none-linux_armv6l.whl", hash = "sha256:761651dc17ad7bc0abfc1b04b3f0e84df263ed435d34f29760b3da739ab02d35", size = 10834749, upload-time = "2026-04-15T15:48:14.877Z" }, + { url = "https://files.pythonhosted.org/packages/d9/4c/fabf951850401d24d36b21bced088a366c6827e1c37dab4523afff84c4b2/ty-0.0.31-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c529922395a07231c27488f0290651e05d27d149f7e0aa807678f1f7e9c58a5e", size = 10626012, upload-time = "2026-04-15T15:48:22.554Z" }, + { url = "https://files.pythonhosted.org/packages/04/b0/4a5aff88d2544f19514a59c8f693d63144aa7307fe2ee5df608333ab5460/ty-0.0.31-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5f345df2b87d747859e72c2cbc9be607ea1bbc8bc93dd32fa3d03ea091cb4fee", size = 10075790, upload-time = "2026-04-15T15:47:46.959Z" }, + { url = "https://files.pythonhosted.org/packages/d5/73/9d4dcad12cd4e85274014f2c0510ef93f590b2a1e5148de3a9f276098dad/ty-0.0.31-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4b207eddcfbafd376132689d3435b14efcb531289cb59cd961c6a611133bd54", size = 10590286, upload-time = "2026-04-15T15:48:06.222Z" }, + { url = "https://files.pythonhosted.org/packages/47/45/fe40adde18692359ded174ae7ddbfac056e876eb0f43b65be74fde7f6072/ty-0.0.31-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:663778b220f357067488ce68bfc52335ccbd161549776f70dcbde6bbde82f77a", size = 10623824, upload-time = "2026-04-15T15:48:12.965Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e8/0ffa2e09b548e6daa9ebc368d68b767dc2405ca4cbeadb7ede0e2cb21059/ty-0.0.31-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3506cfe87dfade0fb2960dd4fffd4fd8089003587b3445c0a1a295c9d83764fb", size = 11156864, upload-time = "2026-04-15T15:48:08.473Z" }, + { url = "https://files.pythonhosted.org/packages/08/e9/fd44c2075115d569593ee9473d7e2a38b750fd7e783421c95eb528c15df5/ty-0.0.31-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b3f3d8492f08e81916026354c1d1599e9ddfa1241804141a74d5662fc710085", size = 11696401, upload-time = "2026-04-15T15:48:17.355Z" }, + { url = "https://files.pythonhosted.org/packages/4e/50/35aad8eadf964d23e2a4faa5b38a206aa85c78833c8ce335dddd2c34ba63/ty-0.0.31-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a97de32ee6a619393a4c495e056a1c547de7877510f3152e61345c71d774d2d0", size = 11374903, upload-time = "2026-04-15T15:47:55.893Z" }, + { url = "https://files.pythonhosted.org/packages/c8/37/01eccd25d23f5aaa7f7ff1a87b5b215469f6b202cf689a1812b71c1e7f6b/ty-0.0.31-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c906354ce441e342646582bc9b8f48a676f79f3d061e25de15ff870e015ca14e", size = 11206624, upload-time = "2026-04-15T15:47:51.778Z" }, + { url = "https://files.pythonhosted.org/packages/f4/70/baad2914cb097453f127a221f8addb2b41926098059cd773c75e6a662fc4/ty-0.0.31-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:275bb7c82afcbf89fe2dbef1b2692f2bc98451f1ee2c8eb809ddd91317822388", size = 10575089, upload-time = "2026-04-15T15:47:49.448Z" }, + { url = "https://files.pythonhosted.org/packages/83/12/bae3a7bba2e785eb72ce00f9da70eedcb8c5e8299efecbd16e6e436abd82/ty-0.0.31-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:405da247027c6efd1e264886b6ac4a86ab3a4f09200b02e33630efe85f119e53", size = 10642315, upload-time = "2026-04-15T15:48:19.661Z" }, + { url = "https://files.pythonhosted.org/packages/93/9e/cad04d5d839bc60355cea98c7e09d724ea65f47184def0fae8b90dc54591/ty-0.0.31-py3-none-musllinux_1_2_i686.whl", hash = "sha256:54d9835608eed196853d6643f645c50ce83bcc7fe546cdb3e210c1bcf7c58c09", size = 10834473, upload-time = "2026-04-15T15:48:02.091Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ba/84112d280182d37690d3d2b4018b2667e42bc281585e607015635310016a/ty-0.0.31-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5ee11be9b07e8c0c6b455ff075a0abe4f194de9476f57624db98eec9df618355", size = 11315785, upload-time = "2026-04-15T15:48:10.754Z" }, + { url = "https://files.pythonhosted.org/packages/50/9f/ac42dc223d7e0950e97a1854567a8b3e7fe09ad7375adbf91bfb43290482/ty-0.0.31-py3-none-win32.whl", hash = "sha256:7286587aacf3eef0956062d6492b893b02f82b0f22c5e230008e13ff0d216a8b", size = 10187657, upload-time = "2026-04-15T15:48:04.264Z" }, + { url = "https://files.pythonhosted.org/packages/75/3e/57ba7ea7ecb2f4751644ba91756e2be70e33ef5952c0c41a256a0e4c2437/ty-0.0.31-py3-none-win_amd64.whl", hash = "sha256:81134e25d2a2562ab372f24de8f9bd05034d27d30377a5d7540f259791c6234c", size = 11205258, upload-time = "2026-04-15T15:47:53.759Z" }, + { url = "https://files.pythonhosted.org/packages/88/39/bca669095ccf0a400af941fdf741578d4c2d6719f1b7f10e6dbec10aa862/ty-0.0.31-py3-none-win_arm64.whl", hash = "sha256:e9cb15fad26545c6a608f40f227af3a5513cb376998ca6feddd47ca7d93ffafa", size = 10590392, upload-time = "2026-04-15T15:47:57.968Z" }, ] [[package]] From d6d7de98575216c126de1ff31385defc8ca88e9e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 02:11:09 +0000 Subject: [PATCH 20/29] =?UTF-8?q?=E2=AC=86=EF=B8=8F=F0=9F=94=92=EF=B8=8F?= =?UTF-8?q?=20Lock=20file=20maintenance=20(#1670)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Update | Change | |---|---| | lockFileMaintenance | All locks refreshed | 🔧 This Pull Request updates lock files to use the latest dependency versions. --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - "before 4am on monday" - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/munich-quantum-toolkit/core). Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- uv.lock | 234 +++++++++++++++++++++++++------------------------------- 1 file changed, 105 insertions(+), 129 deletions(-) diff --git a/uv.lock b/uv.lock index f3067e3d25..5e00eb850d 100644 --- a/uv.lock +++ b/uv.lock @@ -133,11 +133,11 @@ wheels = [ [[package]] name = "certifi" -version = "2026.2.25" +version = "2026.4.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, + { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, ] [[package]] @@ -329,14 +329,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.2" +version = "8.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/75/31212c6bf2503fdf920d87fee5d7a86a2e3bcf444984126f13d8e4016804/click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5", size = 302856, upload-time = "2026-04-03T19:14:45.118Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d", size = 108379, upload-time = "2026-04-03T19:14:43.505Z" }, + { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, ] [[package]] @@ -963,11 +963,11 @@ wheels = [ [[package]] name = "idna" -version = "3.11" +version = "3.13" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/cc/762dfb036166873f0059f3b7de4565e1b5bc3d6f28a414c13da27e442f99/idna-3.13.tar.gz", hash = "sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242", size = 194210, upload-time = "2026-04-22T16:42:42.314Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/5d/13/ad7d7ca3808a898b4612b6fe93cde56b53f3034dcde235acb1f0e1df24c6/idna-3.13-py3-none-any.whl", hash = "sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3", size = 68629, upload-time = "2026-04-22T16:42:40.909Z" }, ] [[package]] @@ -1009,8 +1009,7 @@ dependencies = [ { name = "comm" }, { name = "debugpy" }, { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "ipython", version = "9.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "ipython", version = "9.13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyter-client" }, { name = "jupyter-core" }, { name = "matplotlib-inline" }, @@ -1053,34 +1052,7 @@ wheels = [ [[package]] name = "ipython" -version = "9.10.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version == '3.11.*' and sys_platform == 'win32'" }, - { name = "decorator", marker = "python_full_version == '3.11.*'" }, - { name = "ipython-pygments-lexers", marker = "python_full_version == '3.11.*'" }, - { name = "jedi", marker = "python_full_version == '3.11.*'" }, - { name = "matplotlib-inline", marker = "python_full_version == '3.11.*'" }, - { name = "pexpect", marker = "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit", marker = "python_full_version == '3.11.*'" }, - { name = "pygments", marker = "python_full_version == '3.11.*'" }, - { name = "stack-data", marker = "python_full_version == '3.11.*'" }, - { name = "traitlets", marker = "python_full_version == '3.11.*'" }, - { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c5/25/daae0e764047b0a2480c7bbb25d48f4f509b5818636562eeac145d06dfee/ipython-9.10.1.tar.gz", hash = "sha256:e170e9b2a44312484415bdb750492699bf329233b03f2557a9692cce6466ada4", size = 4426663, upload-time = "2026-03-27T09:53:26.244Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/09/ba70f8d662d5671687da55ad2cc0064cf795b15e1eea70907532202e7c97/ipython-9.10.1-py3-none-any.whl", hash = "sha256:82d18ae9fb9164ded080c71ef92a182ee35ee7db2395f67616034bebb020a232", size = 622827, upload-time = "2026-03-27T09:53:24.566Z" }, -] - -[[package]] -name = "ipython" -version = "9.12.0" +version = "9.13.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -1092,22 +1064,27 @@ resolution-markers = [ "python_full_version == '3.12.*' and sys_platform == 'win32'", "python_full_version == '3.12.*' and sys_platform == 'emscripten'", "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, - { name = "decorator", marker = "python_full_version >= '3.12'" }, - { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.12'" }, - { name = "jedi", marker = "python_full_version >= '3.12'" }, - { name = "matplotlib-inline", marker = "python_full_version >= '3.12'" }, - { name = "pexpect", marker = "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit", marker = "python_full_version >= '3.12'" }, - { name = "pygments", marker = "python_full_version >= '3.12'" }, - { name = "stack-data", marker = "python_full_version >= '3.12'" }, - { name = "traitlets", marker = "python_full_version >= '3.12'" }, + { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version >= '3.11'" }, + { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" }, + { name = "jedi", marker = "python_full_version >= '3.11'" }, + { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" }, + { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" }, + { name = "psutil", marker = "python_full_version >= '3.11'" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, + { name = "stack-data", marker = "python_full_version >= '3.11'" }, + { name = "traitlets", marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/73/7114f80a8f9cabdb13c27732dce24af945b2923dcab80723602f7c8bc2d8/ipython-9.12.0.tar.gz", hash = "sha256:01daa83f504b693ba523b5a407246cabde4eb4513285a3c6acaff11a66735ee4", size = 4428879, upload-time = "2026-03-27T09:42:45.312Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/87cda5842cf5c31837c06ddb588e11c3c35d8ece89b7a0108c06b8c9b00a/ipython-9.13.0.tar.gz", hash = "sha256:7e834b6afc99f020e3f05966ced34792f40267d64cb1ea9043886dab0dde5967", size = 4430549, upload-time = "2026-04-24T12:24:55.221Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/22/906c8108974c673ebef6356c506cebb6870d48cedea3c41e949e2dd556bb/ipython-9.12.0-py3-none-any.whl", hash = "sha256:0f2701e8ee86e117e37f50563205d36feaa259d2e08d4a6bc6b6d74b18ce128d", size = 625661, upload-time = "2026-03-27T09:42:42.831Z" }, + { url = "https://files.pythonhosted.org/packages/b9/86/3060e8029b7cc505cce9a0137431dda81d0a3fde93a8f0f50ee0bf37a795/ipython-9.13.0-py3-none-any.whl", hash = "sha256:57f9d4639e20818d328d287c7b549af3d05f12486ea8f2e7f73e52a36ec4d201", size = 627274, upload-time = "2026-04-24T12:24:53.038Z" }, ] [[package]] @@ -1491,7 +1468,7 @@ wheels = [ [[package]] name = "matplotlib" -version = "3.10.8" +version = "3.10.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -1506,62 +1483,62 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", size = 8239828, upload-time = "2025-12-10T22:55:02.313Z" }, - { url = "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", size = 8128050, upload-time = "2025-12-10T22:55:04.997Z" }, - { url = "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", size = 8700452, upload-time = "2025-12-10T22:55:07.47Z" }, - { url = "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", size = 9534928, upload-time = "2025-12-10T22:55:10.566Z" }, - { url = "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", size = 9586377, upload-time = "2025-12-10T22:55:12.362Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", size = 8128127, upload-time = "2025-12-10T22:55:14.436Z" }, - { url = "https://files.pythonhosted.org/packages/f8/86/de7e3a1cdcfc941483af70609edc06b83e7c8a0e0dc9ac325200a3f4d220/matplotlib-3.10.8-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6be43b667360fef5c754dda5d25a32e6307a03c204f3c0fc5468b78fa87b4160", size = 8251215, upload-time = "2025-12-10T22:55:16.175Z" }, - { url = "https://files.pythonhosted.org/packages/fd/14/baad3222f424b19ce6ad243c71de1ad9ec6b2e4eb1e458a48fdc6d120401/matplotlib-3.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2b336e2d91a3d7006864e0990c83b216fcdca64b5a6484912902cef87313d78", size = 8139625, upload-time = "2025-12-10T22:55:17.712Z" }, - { url = "https://files.pythonhosted.org/packages/8f/a0/7024215e95d456de5883e6732e708d8187d9753a21d32f8ddb3befc0c445/matplotlib-3.10.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efb30e3baaea72ce5928e32bab719ab4770099079d66726a62b11b1ef7273be4", size = 8712614, upload-time = "2025-12-10T22:55:20.8Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f4/b8347351da9a5b3f41e26cf547252d861f685c6867d179a7c9d60ad50189/matplotlib-3.10.8-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d56a1efd5bfd61486c8bc968fa18734464556f0fb8e51690f4ac25d85cbbbbc2", size = 9540997, upload-time = "2025-12-10T22:55:23.258Z" }, - { url = "https://files.pythonhosted.org/packages/9e/c0/c7b914e297efe0bc36917bf216b2acb91044b91e930e878ae12981e461e5/matplotlib-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238b7ce5717600615c895050239ec955d91f321c209dd110db988500558e70d6", size = 9596825, upload-time = "2025-12-10T22:55:25.217Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d3/a4bbc01c237ab710a1f22b4da72f4ff6d77eb4c7735ea9811a94ae239067/matplotlib-3.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:18821ace09c763ec93aef5eeff087ee493a24051936d7b9ebcad9662f66501f9", size = 8135090, upload-time = "2025-12-10T22:55:27.162Z" }, - { url = "https://files.pythonhosted.org/packages/89/dd/a0b6588f102beab33ca6f5218b31725216577b2a24172f327eaf6417d5c9/matplotlib-3.10.8-cp311-cp311-win_arm64.whl", hash = "sha256:bab485bcf8b1c7d2060b4fcb6fc368a9e6f4cd754c9c2fea281f4be21df394a2", size = 8012377, upload-time = "2025-12-10T22:55:29.185Z" }, - { url = "https://files.pythonhosted.org/packages/9e/67/f997cdcbb514012eb0d10cd2b4b332667997fb5ebe26b8d41d04962fa0e6/matplotlib-3.10.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:64fcc24778ca0404ce0cb7b6b77ae1f4c7231cdd60e6778f999ee05cbd581b9a", size = 8260453, upload-time = "2025-12-10T22:55:30.709Z" }, - { url = "https://files.pythonhosted.org/packages/7e/65/07d5f5c7f7c994f12c768708bd2e17a4f01a2b0f44a1c9eccad872433e2e/matplotlib-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9a5ca4ac220a0cdd1ba6bcba3608547117d30468fefce49bb26f55c1a3d5c58", size = 8148321, upload-time = "2025-12-10T22:55:33.265Z" }, - { url = "https://files.pythonhosted.org/packages/3e/f3/c5195b1ae57ef85339fd7285dfb603b22c8b4e79114bae5f4f0fcf688677/matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04", size = 8716944, upload-time = "2025-12-10T22:55:34.922Z" }, - { url = "https://files.pythonhosted.org/packages/00/f9/7638f5cc82ec8a7aa005de48622eecc3ed7c9854b96ba15bd76b7fd27574/matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f", size = 9550099, upload-time = "2025-12-10T22:55:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/57/61/78cd5920d35b29fd2a0fe894de8adf672ff52939d2e9b43cb83cd5ce1bc7/matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466", size = 9613040, upload-time = "2025-12-10T22:55:38.715Z" }, - { url = "https://files.pythonhosted.org/packages/30/4e/c10f171b6e2f44d9e3a2b96efa38b1677439d79c99357600a62cc1e9594e/matplotlib-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:dd80ecb295460a5d9d260df63c43f4afbdd832d725a531f008dad1664f458adf", size = 8142717, upload-time = "2025-12-10T22:55:41.103Z" }, - { url = "https://files.pythonhosted.org/packages/f1/76/934db220026b5fef85f45d51a738b91dea7d70207581063cd9bd8fafcf74/matplotlib-3.10.8-cp312-cp312-win_arm64.whl", hash = "sha256:3c624e43ed56313651bc18a47f838b60d7b8032ed348911c54906b130b20071b", size = 8012751, upload-time = "2025-12-10T22:55:42.684Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b9/15fd5541ef4f5b9a17eefd379356cf12175fe577424e7b1d80676516031a/matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6", size = 8261076, upload-time = "2025-12-10T22:55:44.648Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a0/2ba3473c1b66b9c74dc7107c67e9008cb1782edbe896d4c899d39ae9cf78/matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1", size = 8148794, upload-time = "2025-12-10T22:55:46.252Z" }, - { url = "https://files.pythonhosted.org/packages/75/97/a471f1c3eb1fd6f6c24a31a5858f443891d5127e63a7788678d14e249aea/matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486", size = 8718474, upload-time = "2025-12-10T22:55:47.864Z" }, - { url = "https://files.pythonhosted.org/packages/01/be/cd478f4b66f48256f42927d0acbcd63a26a893136456cd079c0cc24fbabf/matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce", size = 9549637, upload-time = "2025-12-10T22:55:50.048Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7c/8dc289776eae5109e268c4fb92baf870678dc048a25d4ac903683b86d5bf/matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6", size = 9613678, upload-time = "2025-12-10T22:55:52.21Z" }, - { url = "https://files.pythonhosted.org/packages/64/40/37612487cc8a437d4dd261b32ca21fe2d79510fe74af74e1f42becb1bdb8/matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149", size = 8142686, upload-time = "2025-12-10T22:55:54.253Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/8d8a8730e968185514680c2a6625943f70269509c3dcfc0dcf7d75928cb8/matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645", size = 8012917, upload-time = "2025-12-10T22:55:56.268Z" }, - { url = "https://files.pythonhosted.org/packages/b5/27/51fe26e1062f298af5ef66343d8ef460e090a27fea73036c76c35821df04/matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077", size = 8305679, upload-time = "2025-12-10T22:55:57.856Z" }, - { url = "https://files.pythonhosted.org/packages/2c/1e/4de865bc591ac8e3062e835f42dd7fe7a93168d519557837f0e37513f629/matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22", size = 8198336, upload-time = "2025-12-10T22:55:59.371Z" }, - { url = "https://files.pythonhosted.org/packages/c6/cb/2f7b6e75fb4dce87ef91f60cac4f6e34f4c145ab036a22318ec837971300/matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39", size = 8731653, upload-time = "2025-12-10T22:56:01.032Z" }, - { url = "https://files.pythonhosted.org/packages/46/b3/bd9c57d6ba670a37ab31fb87ec3e8691b947134b201f881665b28cc039ff/matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565", size = 9561356, upload-time = "2025-12-10T22:56:02.95Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3d/8b94a481456dfc9dfe6e39e93b5ab376e50998cddfd23f4ae3b431708f16/matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a", size = 9614000, upload-time = "2025-12-10T22:56:05.411Z" }, - { url = "https://files.pythonhosted.org/packages/bd/cd/bc06149fe5585ba800b189a6a654a75f1f127e8aab02fd2be10df7fa500c/matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958", size = 8220043, upload-time = "2025-12-10T22:56:07.551Z" }, - { url = "https://files.pythonhosted.org/packages/e3/de/b22cf255abec916562cc04eef457c13e58a1990048de0c0c3604d082355e/matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5", size = 8062075, upload-time = "2025-12-10T22:56:09.178Z" }, - { url = "https://files.pythonhosted.org/packages/3c/43/9c0ff7a2f11615e516c3b058e1e6e8f9614ddeca53faca06da267c48345d/matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f", size = 8262481, upload-time = "2025-12-10T22:56:10.885Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ca/e8ae28649fcdf039fda5ef554b40a95f50592a3c47e6f7270c9561c12b07/matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b", size = 8151473, upload-time = "2025-12-10T22:56:12.377Z" }, - { url = "https://files.pythonhosted.org/packages/f1/6f/009d129ae70b75e88cbe7e503a12a4c0670e08ed748a902c2568909e9eb5/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d", size = 9553896, upload-time = "2025-12-10T22:56:14.432Z" }, - { url = "https://files.pythonhosted.org/packages/f5/26/4221a741eb97967bc1fd5e4c52b9aa5a91b2f4ec05b59f6def4d820f9df9/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008", size = 9824193, upload-time = "2025-12-10T22:56:16.29Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/3abf75f38605772cf48a9daf5821cd4f563472f38b4b828c6fba6fa6d06e/matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c", size = 9615444, upload-time = "2025-12-10T22:56:18.155Z" }, - { url = "https://files.pythonhosted.org/packages/93/a5/de89ac80f10b8dc615807ee1133cd99ac74082581196d4d9590bea10690d/matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11", size = 8272719, upload-time = "2025-12-10T22:56:20.366Z" }, - { url = "https://files.pythonhosted.org/packages/69/ce/b006495c19ccc0a137b48083168a37bd056392dee02f87dba0472f2797fe/matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8", size = 8144205, upload-time = "2025-12-10T22:56:22.239Z" }, - { url = "https://files.pythonhosted.org/packages/68/d9/b31116a3a855bd313c6fcdb7226926d59b041f26061c6c5b1be66a08c826/matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50", size = 8305785, upload-time = "2025-12-10T22:56:24.218Z" }, - { url = "https://files.pythonhosted.org/packages/1e/90/6effe8103f0272685767ba5f094f453784057072f49b393e3ea178fe70a5/matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908", size = 8198361, upload-time = "2025-12-10T22:56:26.787Z" }, - { url = "https://files.pythonhosted.org/packages/d7/65/a73188711bea603615fc0baecca1061429ac16940e2385433cc778a9d8e7/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a", size = 9561357, upload-time = "2025-12-10T22:56:28.953Z" }, - { url = "https://files.pythonhosted.org/packages/f4/3d/b5c5d5d5be8ce63292567f0e2c43dde9953d3ed86ac2de0a72e93c8f07a1/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1", size = 9823610, upload-time = "2025-12-10T22:56:31.455Z" }, - { url = "https://files.pythonhosted.org/packages/4d/4b/e7beb6bbd49f6bae727a12b270a2654d13c397576d25bd6786e47033300f/matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c", size = 9614011, upload-time = "2025-12-10T22:56:33.85Z" }, - { url = "https://files.pythonhosted.org/packages/7c/e6/76f2813d31f032e65f6f797e3f2f6e4aab95b65015924b1c51370395c28a/matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b", size = 8362801, upload-time = "2025-12-10T22:56:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" }, - { url = "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", size = 8237252, upload-time = "2025-12-10T22:56:39.529Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", size = 8124693, upload-time = "2025-12-10T22:56:41.758Z" }, - { url = "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", size = 8702205, upload-time = "2025-12-10T22:56:43.415Z" }, - { url = "https://files.pythonhosted.org/packages/04/30/3afaa31c757f34b7725ab9d2ba8b48b5e89c2019c003e7d0ead143aabc5a/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6da7c2ce169267d0d066adcf63758f0604aa6c3eebf67458930f9d9b79ad1db1", size = 8249198, upload-time = "2025-12-10T22:56:45.584Z" }, - { url = "https://files.pythonhosted.org/packages/48/2f/6334aec331f57485a642a7c8be03cb286f29111ae71c46c38b363230063c/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9153c3292705be9f9c64498a8872118540c3f4123d1a1c840172edf262c8be4a", size = 8136817, upload-time = "2025-12-10T22:56:47.339Z" }, - { url = "https://files.pythonhosted.org/packages/73/e4/6d6f14b2a759c622f191b2d67e9075a3f56aaccb3be4bb9bb6890030d0a0/matplotlib-3.10.8-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ae029229a57cd1e8fe542485f27e7ca7b23aa9e8944ddb4985d0bc444f1eca2", size = 8713867, upload-time = "2025-12-10T22:56:48.954Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233, upload-time = "2026-04-24T00:14:13.539Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/6f/340b04986e67aac6f66c5145ce68bf72c64bed30f92c8913499a6e6b8f99/matplotlib-3.10.9-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77210dce9cb8153dffc967efaae990543392563d5a376d4dd8539bebcb0ed217", size = 8296625, upload-time = "2026-04-24T00:11:43.376Z" }, + { url = "https://files.pythonhosted.org/packages/bb/2f/127081eb83162053ebb9678ceac64220b93a663e0167432566e9c7c82aab/matplotlib-3.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1e7698ac9868428e84d2c967424803b2472ff7167d9d6590d4204ed775343c3b", size = 8188790, upload-time = "2026-04-24T00:11:46.556Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b7/d8bcec2626c35f96972bff656299fef4578113ea6193c8fdad324710410c/matplotlib-3.10.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1aa972116abb4c9d201bf245620b433726cb6856f3bef6a78f776a00f5c92d37", size = 8769389, upload-time = "2026-04-24T00:11:48.959Z" }, + { url = "https://files.pythonhosted.org/packages/12/49/b78e214a527ea732033b7f4d37f7afb504d74ba9d134bd47938230dfb8b1/matplotlib-3.10.9-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae2f11957b27ce53497dd4d7b235c4d4f1faf383dfb39d0c5beb833bff883294", size = 9589657, upload-time = "2026-04-24T00:11:51.915Z" }, + { url = "https://files.pythonhosted.org/packages/5f/15/5246f7b43beae19c74dfee651d58d6cc8112e06f77adb4e88cc04f2e3a23/matplotlib-3.10.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b049278ddce116aaa1c1377ebf58adea909132dfce0281cf7e3a1ea9fc2e2c65", size = 9651983, upload-time = "2026-04-24T00:11:54.766Z" }, + { url = "https://files.pythonhosted.org/packages/75/77/5acecfe672ba0fa1b8c0454f69ce155d1e6fc5852fa7206bf9afaf767121/matplotlib-3.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:82834c3c292d24d3a8aae77cd2d20019de69d692a34a970e4fdb8d33e2ea3dda", size = 8199701, upload-time = "2026-04-24T00:11:58.389Z" }, + { url = "https://files.pythonhosted.org/packages/4c/8c/290f021104741fea63769c31494f5324c0cd249bf536a65a4350767b1f22/matplotlib-3.10.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:68cfdcede415f7c8f5577b03303dd94526cdb6d11036cecdc205e08733b2d2bb", size = 8306860, upload-time = "2026-04-24T00:12:01.207Z" }, + { url = "https://files.pythonhosted.org/packages/51/18/325cd32ece1120d1da51cc4e4294c6580190699490183fc2fe8cb6d61ec5/matplotlib-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfca0129678bd56379db26c52b5d77ed7de314c047492fbdc763aa7501710cfb", size = 8199254, upload-time = "2026-04-24T00:12:04.239Z" }, + { url = "https://files.pythonhosted.org/packages/79/db/e28c1b83e3680740aa78925f5fb2ae4d16207207419ad75ea9fe604f8676/matplotlib-3.10.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e436d155fa8a3399dc62683f8f5d0e2e50d25d0144a73edd73f82eec8f4abfb", size = 8777092, upload-time = "2026-04-24T00:12:06.793Z" }, + { url = "https://files.pythonhosted.org/packages/55/fa/3ce7adfe9ba101748f465211660d9c6374c876b671bdb8c2bb6d347e8b94/matplotlib-3.10.9-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56fc0bd271b00025c6edfdc7c2dcd247372c8e1544971d62e1dc7c17367e8bf9", size = 9595691, upload-time = "2026-04-24T00:12:09.706Z" }, + { url = "https://files.pythonhosted.org/packages/36/c4/6960a76686ed668f2c60f84e9799ba4c0d56abdb36b1577b60c1d061d1ec/matplotlib-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5a6104ed666402ba5106d7f36e0e0cdca4e8d7fa4d39708ca88019e2835a2eb", size = 9659771, upload-time = "2026-04-24T00:12:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0d/271aace3342157c64700c9ff4c59c7b392f3dbab393692e8db6fbe7ab96c/matplotlib-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:d730e984eddf56974c3e72b6129c7ca462ac38dc624338f4b0b23eb23ecba00f", size = 8205112, upload-time = "2026-04-24T00:12:15.773Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ee/cb57ad4754f3e7b9174ce6ce66d9205fb827067e48a9f58ac09d7e7d6b77/matplotlib-3.10.9-cp311-cp311-win_arm64.whl", hash = "sha256:51bf0ddbdc598e060d46c16b5590708f81a1624cefbaaf62f6a81bf9285b8c80", size = 8132310, upload-time = "2026-04-24T00:12:18.645Z" }, + { url = "https://files.pythonhosted.org/packages/35/c6/5581e26c72233ebb2a2a6fed2d24fb7c66b4700120b813f51b0555acf0b6/matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1", size = 8319908, upload-time = "2026-04-24T00:12:21.323Z" }, + { url = "https://files.pythonhosted.org/packages/b7/18/4880dd762e40cd360c1bf06e890c5a97b997e91cb324602b1a19950ad5ce/matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320", size = 8216016, upload-time = "2026-04-24T00:12:23.4Z" }, + { url = "https://files.pythonhosted.org/packages/32/91/d024616abdba99e83120e07a20658976f6a343646710760c4a51df126029/matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285", size = 8789336, upload-time = "2026-04-24T00:12:26.096Z" }, + { url = "https://files.pythonhosted.org/packages/5c/04/030a2f61ef2158f5e4c259487a92ac877732499fb33d871585d89e03c42d/matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2", size = 9604602, upload-time = "2026-04-24T00:12:29.052Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c2/541e4d09d87bb6b5830fc28b4c887a9a8cf4e1c6cee698a8c05552ae2003/matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf", size = 9670966, upload-time = "2026-04-24T00:12:32.131Z" }, + { url = "https://files.pythonhosted.org/packages/04/a1/4571fc46e7702de8d0c2dc54ad1b2f8e29328dea3ee90831181f7353d93c/matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6", size = 8217462, upload-time = "2026-04-24T00:12:35.226Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d0/2269edb12aa30c13c8bcc9382892e39943ce1d28aab4ec296e0381798e81/matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42", size = 8136688, upload-time = "2026-04-24T00:12:37.442Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d3/8d4f6afbecb49fc04e060a57c0fce39ea51cc163a6bd87303ccd698e4fa6/matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f", size = 8320331, upload-time = "2026-04-24T00:12:39.688Z" }, + { url = "https://files.pythonhosted.org/packages/63/d9/9e14bc7564bf92d5ffa801ae5fac819ce74b925dfb55e3ebde61a3bbad3e/matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e", size = 8216461, upload-time = "2026-04-24T00:12:42.494Z" }, + { url = "https://files.pythonhosted.org/packages/8a/17/4402d0d14ccf1dfc70932600b68097fbbf9c898a4871d2cbbe79c7801a32/matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f", size = 8790091, upload-time = "2026-04-24T00:12:44.789Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0b/322aeec06dd9b91411f92028b37d447342770a24392aa4813e317064dad5/matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838", size = 9605027, upload-time = "2026-04-24T00:12:47.583Z" }, + { url = "https://files.pythonhosted.org/packages/74/88/5f13482f55e7b00bcfc09838b093c2456e1379978d2a146844aae05350ad/matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2", size = 9671269, upload-time = "2026-04-24T00:12:50.878Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/0840fd2f93da988ec660b8ad1984abe9f25d2aed22a5e394ff1c68c88307/matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921", size = 8217588, upload-time = "2026-04-24T00:12:53.784Z" }, + { url = "https://files.pythonhosted.org/packages/47/b9/d706d06dd605c49b9f83a2aed8c13e3e5db70697d7a80b7e3d7915de6b17/matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8", size = 8136913, upload-time = "2026-04-24T00:12:56.501Z" }, + { url = "https://files.pythonhosted.org/packages/9b/45/6e32d96978264c8ca8c4b1010adb955a1a49cfaf314e212bbc8908f04a61/matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9", size = 8368019, upload-time = "2026-04-24T00:12:58.896Z" }, + { url = "https://files.pythonhosted.org/packages/86/0a/c8e3d3bba245f0f7fc424937f8ff7ef77291a36af3edb97ccd78aa93d84f/matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4", size = 8264645, upload-time = "2026-04-24T00:13:01.406Z" }, + { url = "https://files.pythonhosted.org/packages/3d/aa/5bf5a14fe4fed73a4209a155606f8096ff797aad89c6c35179026571133e/matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc", size = 8802194, upload-time = "2026-04-24T00:13:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5e/b4be852d6bba6fd15893fadf91ff26ae49cb91aac789e95dde9d342e664f/matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99", size = 9622684, upload-time = "2026-04-24T00:13:06.647Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/ed428c971139112ef730f62770654d609467346d09d4b62617e1afd68a5a/matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d", size = 9680790, upload-time = "2026-04-24T00:13:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/e7/09/052e884aaf2b985c63cb79f715f1d5b6a3eaa7de78f6a52b9dbc077d5b53/matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8", size = 8287571, upload-time = "2026-04-24T00:13:13.087Z" }, + { url = "https://files.pythonhosted.org/packages/f4/38/ae27288e788c35a4250491422f3db7750366fc8c97d6f36fbdecfc1f5518/matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38", size = 8188292, upload-time = "2026-04-24T00:13:15.546Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e6/3bd8afd04949f02eabc1c17115ea5255e19cacd4d06fc5abdde4eeb0052c/matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d", size = 8321276, upload-time = "2026-04-24T00:13:18.318Z" }, + { url = "https://files.pythonhosted.org/packages/41/86/86231232fff41c9f8e4a1a7d7a597d349a02527109c3af7d618366122139/matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f", size = 8218218, upload-time = "2026-04-24T00:13:20.974Z" }, + { url = "https://files.pythonhosted.org/packages/85/8f/becc9722cafc64f5d2eb0b7c1bf5f585271c618a45dbd8fabeb021f898b6/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b", size = 9608145, upload-time = "2026-04-24T00:13:23.228Z" }, + { url = "https://files.pythonhosted.org/packages/32/5d/f7e914f7d9325abff4057cee62c0fa70263683189f774473cbfb534cd13b/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2", size = 9885085, upload-time = "2026-04-24T00:13:25.849Z" }, + { url = "https://files.pythonhosted.org/packages/a5/fd/fa69f2221534e80cc5772ac2b7d222011a2acafc2ec7216d5dd174c864ae/matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716", size = 9672358, upload-time = "2026-04-24T00:13:28.906Z" }, + { url = "https://files.pythonhosted.org/packages/ab/1a/5a4f747a8b271cbb024946d2dd3c913ab5032ba430626f8c3528ada96b4b/matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f", size = 8349970, upload-time = "2026-04-24T00:13:31.904Z" }, + { url = "https://files.pythonhosted.org/packages/64/dc/95d60ecaefe30680a154b52ea96ab4b0dab547f1fd6aa12f5fb655e89cae/matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456", size = 8272785, upload-time = "2026-04-24T00:13:34.511Z" }, + { url = "https://files.pythonhosted.org/packages/70/a0/005d68bc8b8418300ce6591f18586910a8526806e2ab663933d9f20a41e9/matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe", size = 8367999, upload-time = "2026-04-24T00:13:36.962Z" }, + { url = "https://files.pythonhosted.org/packages/22/05/1236cc9290be70b2498af20ca348add76e3fffe7f67b477db5133a84f3ea/matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6", size = 8264543, upload-time = "2026-04-24T00:13:39.851Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c2/071f5a5ff6c5bd63aaaf2f45c811d9bf2ced94bde188d9e1a519e21d0cba/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c", size = 9622800, upload-time = "2026-04-24T00:13:42.296Z" }, + { url = "https://files.pythonhosted.org/packages/95/57/da7d1f10a85624b9e7db68e069dd94e58dc41dbf9463c5921632ecbe3661/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4", size = 9888561, upload-time = "2026-04-24T00:13:45.026Z" }, + { url = "https://files.pythonhosted.org/packages/67/b2/ef8d6bb59b0edb6c16c968b70f548aa13b54348972def5aa6ac85df67145/matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf", size = 9680884, upload-time = "2026-04-24T00:13:48.066Z" }, + { url = "https://files.pythonhosted.org/packages/61/1c/d21bfeb9931881ebe96bcfcff27c7ae4b160ae0ec291a714c42641a56d75/matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39", size = 8432333, upload-time = "2026-04-24T00:13:51.008Z" }, + { url = "https://files.pythonhosted.org/packages/78/23/92493c3e6e1b635ccfff146f7b99e674808787915420373ac399283764c2/matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c", size = 8324785, upload-time = "2026-04-24T00:13:53.633Z" }, + { url = "https://files.pythonhosted.org/packages/2c/2b/0e92ad0ac446633f928a1563db4aa8add407e1924faf0ded5b95b35afb27/matplotlib-3.10.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1872fb212a05b729e649754a72d5da61d03e0554d76e80303b6f83d1d2c0552b", size = 8293058, upload-time = "2026-04-24T00:13:56.339Z" }, + { url = "https://files.pythonhosted.org/packages/4b/23/74682fd369f5299ceda438fea2a0662e6383b85c9383fb9cdfcf04713e07/matplotlib-3.10.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:985f2238880e2e69093f588f5fe2e46771747febf0649f3cf7f7b7480875317f", size = 8186627, upload-time = "2026-04-24T00:13:58.623Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e8/368aab88f3c4cd8992800f31abfe0670c3e47540ba20a97e9fdbcde594b3/matplotlib-3.10.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6640f75af2c6148293caa0a2b39dd806a492dd66c8a8b04035813e33d0fd2585", size = 8764117, upload-time = "2026-04-24T00:14:01.684Z" }, + { url = "https://files.pythonhosted.org/packages/63/e2/9f66ca6a651a52abfe0d4964ce01439ed34f3f1e119de10ff3a07f403043/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:42fb814efabe95c06c1994d8ab5a8385f43a249e23badd3ba931d4308e5bca20", size = 8304420, upload-time = "2026-04-24T00:14:04.57Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e8/467c03568218792906aa87b5e7bb379b605e056ed0c74fe00c051786d925/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f76e640a5268850bfda54b5131b1b1941cc685e42c5fa98ed9f2d64038308cba", size = 8197981, upload-time = "2026-04-24T00:14:07.233Z" }, + { url = "https://files.pythonhosted.org/packages/6f/87/afead29192170917537934c6aff4b008c805fff7b1ccea0c79120d96beda/matplotlib-3.10.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3fc0364dfbe1d07f6d15c5ebd0c5bf89e126916e5a8667dd4a7a6e84c36653d4", size = 8774002, upload-time = "2026-04-24T00:14:09.816Z" }, ] [[package]] @@ -1742,8 +1719,7 @@ dependencies = [ { name = "importlib-metadata" }, { name = "ipykernel" }, { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "ipython", version = "9.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "ipython", version = "9.13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyter-cache" }, { name = "myst-parser", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "myst-parser", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -2065,11 +2041,11 @@ parser = [ [[package]] name = "packaging" -version = "26.1" +version = "26.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] [[package]] @@ -2221,11 +2197,11 @@ wheels = [ [[package]] name = "pathspec" -version = "1.0.4" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/17/9c3094b822982b9f1ea666d8580ce59000f61f87c1663556fb72031ad9ec/pathspec-1.1.0.tar.gz", hash = "sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080", size = 133918, upload-time = "2026-04-23T01:46:22.298Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c9/8eed0486f074e9f1ca7f8ce5ad663e65f12fdab344028d658fa1b03d35e0/pathspec-1.1.0-py3-none-any.whl", hash = "sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42", size = 56264, upload-time = "2026-04-23T01:46:20.606Z" }, ] [[package]] @@ -2233,7 +2209,7 @@ name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, + { name = "ptyprocess" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ @@ -2729,7 +2705,7 @@ wheels = [ [[package]] name = "qiskit" -version = "2.4.0" +version = "2.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, @@ -2741,15 +2717,15 @@ dependencies = [ { name = "stevedore" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/cb/a348ec6595a8a2163fad714692c5b2d82ffb6a7f82e7a64f5a58e0dbc37e/qiskit-2.4.0.tar.gz", hash = "sha256:57a43f869608fd8319444c2c1fbf6674fa36c6346646e41f4b128233693970d6", size = 4080145, upload-time = "2026-04-16T17:21:13.532Z" } +sdist = { url = "https://files.pythonhosted.org/packages/39/82/7a90609d283f8707d46e1a6771e16462922789223817b6e7033490dffcaa/qiskit-2.4.1.tar.gz", hash = "sha256:90203e0241184642fc4a55cf88e20633d412b00d0aa89e3af780648744e508cf", size = 4081678, upload-time = "2026-04-24T20:34:14.13Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/cc/47fa03c0a52d603f978b95bc45c0f99bd436bc172f0727fe0f63f2758ce7/qiskit-2.4.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:a6dfd5875a77736749e851c631ff5a328dc60e863c8b0aafd60fdc745a4c7591", size = 9350416, upload-time = "2026-04-16T19:34:53.371Z" }, - { url = "https://files.pythonhosted.org/packages/f9/37/9744dedced76513cb5517a574e268adaa8c10d127eb8add72a6236b3f51e/qiskit-2.4.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:4ff99307a8fac97962964b02afe22ae7ee4997a49e98538599ff4b44e6659d31", size = 8328830, upload-time = "2026-04-16T17:21:02.609Z" }, - { url = "https://files.pythonhosted.org/packages/f0/70/fbd7634fbd66fe6419741051e00d719398466fc437e4cb148effeb057ce1/qiskit-2.4.0-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5352c66c5affd54dfb2473e99247031aaaf3af6c7cefe0875a0bd302ca31fcc1", size = 8625281, upload-time = "2026-04-16T17:21:05.012Z" }, - { url = "https://files.pythonhosted.org/packages/ee/e1/1167314fb2c0d31f41975201604708ddef83192a9289e05f69a32898804a/qiskit-2.4.0-cp310-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:fd04cfb30b59fd257a1108fd6f337eceafd26039c8e86603bcced9b45137971f", size = 9680387, upload-time = "2026-04-16T19:34:56.044Z" }, - { url = "https://files.pythonhosted.org/packages/87/c4/6e639125851e3d6b709aea0d71b10ffc8fce14af322d2de6112cf57627f7/qiskit-2.4.0-cp310-abi3-manylinux_2_28_s390x.whl", hash = "sha256:446c8d6da61e9e134ba5e30f933240ce9920f668d0e010fc79cc5a22c03e9a78", size = 9186598, upload-time = "2026-04-16T19:34:58.786Z" }, - { url = "https://files.pythonhosted.org/packages/62/55/1e6d61a1c8a83d685d281f5ae15e7c2c62080aed358a9c4b79309aa393d9/qiskit-2.4.0-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cf9020bf79934c7bf490973d99b80464957efc1fa13456d680c42bce23262687", size = 9329265, upload-time = "2026-04-16T17:21:08.193Z" }, - { url = "https://files.pythonhosted.org/packages/94/26/b4d51da621e80a3aa4fccf72502f218773bb2f1bd63f9979e8a7b33ffaaf/qiskit-2.4.0-cp310-abi3-win_amd64.whl", hash = "sha256:0292d0d247f1603c225c3e83897aa8c93bc3010523f337f766750f70a8fff5ba", size = 9111120, upload-time = "2026-04-16T17:21:10.623Z" }, + { url = "https://files.pythonhosted.org/packages/dc/08/f9f3d0e6f51059b0da75aba5e3dee08a4ab85c1518b1efcc56c46db800e7/qiskit-2.4.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:421c4649418d96b8996b798f7984b29e53343ed7c13ebde38dad3af5625ea1c0", size = 9350133, upload-time = "2026-04-24T22:41:34.805Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ad/2bef5daf53bb18b735ec278ee3a98b83fe10167ea156e62fb562f0141404/qiskit-2.4.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:b0d14760caf84b9ce81b9380005947d028e993c8752e7f68a372b533a129a4bc", size = 8328821, upload-time = "2026-04-24T20:34:03.32Z" }, + { url = "https://files.pythonhosted.org/packages/e2/86/ed5929218f7943d33f37229406b42a413c1fd40c1312fe628371cc8ef563/qiskit-2.4.1-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:90ffe85b64d197e42cc90f21dd086bedd0609658e407f9a82b1ce3e3b4145eed", size = 8626333, upload-time = "2026-04-24T20:34:06.009Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c9/a7e3150e327c4328fbd66d42f72b6ecac201c6d75729c62972b2b5964101/qiskit-2.4.1-cp310-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:3314f8fe5742a1cb7bd67f9fe8302647fc78ffe9b20f22b30ee4a6c2d0d6d411", size = 9679264, upload-time = "2026-04-24T22:41:37.878Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f8/c1b00ac0843089771152e650c6143825746c9c85d4dac855207e15c9e26e/qiskit-2.4.1-cp310-abi3-manylinux_2_28_s390x.whl", hash = "sha256:a46572a39c3fbb01561e97e2ac7ec3afe29e96090e8d8040e90c2059ab5b3055", size = 9187719, upload-time = "2026-04-24T22:41:40.925Z" }, + { url = "https://files.pythonhosted.org/packages/90/e8/9f1859218193ed89aef5d54b5385d891e808ff8830667bbc490d85507afd/qiskit-2.4.1-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:bb85390b4d6878ecc28fb54b78b128a24642a42eb3a5a58c4b144ee0730fece7", size = 9319379, upload-time = "2026-04-24T20:34:09.075Z" }, + { url = "https://files.pythonhosted.org/packages/11/6a/c9065d0f74178275963b44e57fd93530ed36089682f918553c3bceb9a8ba/qiskit-2.4.1-cp310-abi3-win_amd64.whl", hash = "sha256:91c8c8b0582a8d0dc46c0d3fd37896b2facfd99c54671ac557da9f643114e5e3", size = 9108769, upload-time = "2026-04-24T20:34:11.888Z" }, ] [package.optional-dependencies] @@ -3707,11 +3683,11 @@ wheels = [ [[package]] name = "tzdata" -version = "2026.1" +version = "2026.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/f5/cd531b2d15a671a40c0f66cf06bc3570a12cd56eef98960068ebbad1bf5a/tzdata-2026.1.tar.gz", hash = "sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98", size = 197639, upload-time = "2026-04-03T11:25:22.002Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/70/d460bd685a170790ec89317e9bd33047988e4bce507b831f5db771e142de/tzdata-2026.1-py2.py3-none-any.whl", hash = "sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9", size = 348952, upload-time = "2026-04-03T11:25:20.313Z" }, + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, ] [[package]] From f9297a8b33663688ab937e84ecb2c5872dc14db3 Mon Sep 17 00:00:00 2001 From: matthias Date: Tue, 28 Apr 2026 09:46:42 +0200 Subject: [PATCH 21/29] =?UTF-8?q?=E2=9C=A8Add=20`walkProgramGraph`=20drive?= =?UTF-8?q?r=20(#1664)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This pull request adds the `walkProgramGraph` driver functionality originally developed in #1600 in an effort to reduce the PR size there. Moreover, #1661 also depends on the driver. - [X] The pull request only contains commits that are focused and relevant to this change. - [X] I have added appropriate tests that cover the new/changed functionality. - [X] I have updated the documentation to reflect these changes. - [x] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [X] I have added migration instructions to the upgrade guide (if needed). - [X] The changes follow the project's style guidelines and introduce no new warnings. - [X] The changes are fully tested and pass the CI checks. - [X] I have reviewed my own code changes. **If PR contains AI-assisted content:** - [X] I have disclosed the use of AI tools in the PR description as per our [AI Usage Guidelines](https://github.com/munich-quantum-toolkit/core/blob/main/docs/ai_usage.md). - [X] AI-assisted commits include an `Assisted-by: [Model Name] via [Tool Name]` footer. - [X] I confirm that I have personally reviewed and understood all AI-generated content, and accept full responsibility for it. --------- Signed-off-by: burgholzer Co-authored-by: Copilot Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: burgholzer --- CHANGELOG.md | 3 +- mlir/include/mlir/Dialect/QCO/Utils/Qubits.h | 66 ++++++ .../mlir/Dialect/QCO/Utils/WireIterator.h | 33 +++ .../QCO/Transforms/Mapping/Mapping.cpp | 3 +- mlir/lib/Dialect/QCO/Utils/Driver.cpp | 219 ------------------ mlir/lib/Dialect/QCO/Utils/Qubits.cpp | 71 ++++++ .../Dialect/QCO/Utils/test_drivers.cpp | 152 ++++++++++-- 7 files changed, 313 insertions(+), 234 deletions(-) create mode 100644 mlir/include/mlir/Dialect/QCO/Utils/Qubits.h delete mode 100644 mlir/lib/Dialect/QCO/Utils/Driver.cpp create mode 100644 mlir/lib/Dialect/QCO/Utils/Qubits.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 937aa8ffbf..934262a36b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel - ✨ Add a `merge-single-qubit-rotation-gates` pass for merging consecutive rotation gates using quaternions ([#1407]) ([**@J4MMlE**]) - ✨ Add conversions between `jeff` and QCO ([#1479], [#1548], [#1565], [#1637]) ([**@denialhaag**]) -- ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588]) ([**@MatthiasReumann**]) +- ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588], [#1664]) ([**@MatthiasReumann**], [**@burgholzer**]) - ✨ Add initial infrastructure for new QC and QCO MLIR dialects ([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**]) @@ -352,6 +352,7 @@ _� Refer to the [GitHub Release Notes](https://github.com/munich-quantum-toolk +[#1664]: https://github.com/munich-quantum-toolkit/core/pull/1664 [#1652]: https://github.com/munich-quantum-toolkit/core/pull/1652 [#1637]: https://github.com/munich-quantum-toolkit/core/pull/1637 [#1635]: https://github.com/munich-quantum-toolkit/core/pull/1635 diff --git a/mlir/include/mlir/Dialect/QCO/Utils/Qubits.h b/mlir/include/mlir/Dialect/QCO/Utils/Qubits.h new file mode 100644 index 0000000000..40cbeb1f68 --- /dev/null +++ b/mlir/include/mlir/Dialect/QCO/Utils/Qubits.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include "mlir/Dialect/QCO/IR/QCODialect.h" + +#include +#include + +#include +#include +#include + +namespace mlir::qco { +class Qubits { + /** + * @brief Specifies the qubit "location" (hardware or program). + */ + enum class QubitLocation : std::uint8_t { Hardware, Program }; + +public: + /** + * @brief Add qubit with automatically assigned dynamic index. + */ + void add(TypedValue q); + + /** + * @brief Add qubit with static index. + */ + void add(TypedValue q, std::size_t hw); + + /** + * @brief Remap the qubit value from prev to next. + */ + void remap(TypedValue prev, TypedValue next); + + /** + * @brief Remove the qubit value. + */ + void remove(TypedValue q); + + /** + * @returns the qubit value assigned to a program index. + */ + [[nodiscard]] TypedValue getProgramQubit(std::size_t index) const; + + /** + * @returns the qubit value assigned to a hardware index. + */ + [[nodiscard]] TypedValue getHardwareQubit(std::size_t index) const; + +private: + DenseMap> programToValue_; + DenseMap> hardwareToValue_; + DenseMap, std::pair> + valueToIndex_; +}; +} // namespace mlir::qco diff --git a/mlir/include/mlir/Dialect/QCO/Utils/WireIterator.h b/mlir/include/mlir/Dialect/QCO/Utils/WireIterator.h index 7ff92276fb..73dcbdc870 100644 --- a/mlir/include/mlir/Dialect/QCO/Utils/WireIterator.h +++ b/mlir/include/mlir/Dialect/QCO/Utils/WireIterator.h @@ -10,8 +10,12 @@ #pragma once +#include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QTensor/IR/QTensorOps.h" + #include +#include #include namespace mlir::qco { @@ -83,4 +87,33 @@ class [[nodiscard]] WireIterator { mlir::Value qubit_; bool isSentinel_; }; + +/** + * @brief Categorizes the current traversal direction. + */ +enum class WireDirection : std::uint8_t { Forward, Backward }; + +template struct WireTraversalTraits {}; + +template <> struct WireTraversalTraits { + /// @returns the forward increment stride size. + static constexpr std::ptrdiff_t stride() { return 1; } + + /// @returns true if the wire iterator can continue forward. + static bool isActive(const WireIterator& it) { + return it != std::default_sentinel; + } +}; + +template <> struct WireTraversalTraits { + /// @returns the backward increment stride size. + static constexpr std::ptrdiff_t stride() { return -1; } + + /// @returns true if the wire iterator can continue backward. + static bool isActive(const WireIterator& it) { + return it.operation() == nullptr + ? false + : !isa(it.operation()); + } +}; } // namespace mlir::qco diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp index 97e55fd835..8604491630 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp @@ -14,6 +14,7 @@ #include "mlir/Dialect/QCO/Transforms/Mapping/Architecture.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Dialect/QCO/Utils/Drivers.h" +#include "mlir/Dialect/QCO/Utils/Qubits.h" #include "mlir/Dialect/QCO/Utils/WireIterator.h" #include @@ -810,7 +811,7 @@ struct MappingPass : impl::MappingPassBase { ArrayRef::iterator anchorIt = anchors.begin(); ArrayRef>::iterator swapIt = swaps.begin(); - walkUnit(funcBody, [&](Operation* op, const Qubits& qubits) { + walkProgram(funcBody, [&](Operation* op, Qubits& qubits) { // Early exit if we've processed all layers. if (anchorIt == anchors.end()) { return WalkResult::interrupt(); diff --git a/mlir/lib/Dialect/QCO/Utils/Driver.cpp b/mlir/lib/Dialect/QCO/Utils/Driver.cpp deleted file mode 100644 index ac0dadbfc2..0000000000 --- a/mlir/lib/Dialect/QCO/Utils/Driver.cpp +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM - * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH - * All rights reserved. - * - * SPDX-License-Identifier: MIT - * - * Licensed under the MIT License - */ - -#include "mlir/Dialect/QCO/IR/QCODialect.h" -#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" -#include "mlir/Dialect/QCO/IR/QCOOps.h" -#include "mlir/Dialect/QCO/Utils/Drivers.h" -#include "mlir/Dialect/QCO/Utils/WireIterator.h" -#include "mlir/Dialect/QTensor/IR/QTensorOps.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace mlir::qco { - -using namespace mlir::qtensor; - -namespace { -enum class CircuitWalkResult : std::uint8_t { Advance, Hold, Fail }; -} // namespace - -void Qubits::add(TypedValue q) { add(q, indexToValue_.size()); } - -void Qubits::add(TypedValue q, std::size_t index) { - indexToValue_.try_emplace(index, q); - valueToIndex_.try_emplace(q, index); -} - -void Qubits::remap(TypedValue prev, TypedValue next, - const WalkDirection& direction) { - if (direction == WalkDirection::Backward) { - std::swap(prev, next); - } - - assert(valueToIndex_.contains(prev)); - const auto index = valueToIndex_.lookup(prev); - - valueToIndex_.erase(prev); - valueToIndex_.try_emplace(next, index); - indexToValue_[index] = next; -} - -void Qubits::remap(UnitaryOpInterface op, const WalkDirection& direction) { - for (const auto& [in, out] : - llvm::zip_equal(op.getInputQubits(), op.getOutputQubits())) { - remap(cast>(in), cast>(out), - direction); - } -} - -void Qubits::remove(TypedValue q) { - assert(valueToIndex_.contains(q)); - const auto index = valueToIndex_.lookup(q); - - valueToIndex_.erase(q); - indexToValue_.erase(index); -} - -TypedValue Qubits::getQubit(std::size_t index) const { - assert(indexToValue_.contains(index)); - return indexToValue_.lookup(index); -} - -std::size_t Qubits::getIndex(TypedValue q) const { - assert(valueToIndex_.contains(q)); - return valueToIndex_.lookup(q); -} - -void walkUnit(Region& region, WalkUnitFn fn) { - Qubits qubits; - for (Operation& curr : region.getOps()) { - if (fn(&curr, qubits).wasInterrupted()) { - break; - }; - - TypeSwitch(&curr) - .Case( - [&](StaticOp op) { qubits.add(op.getQubit(), op.getIndex()); }) - .Case([&](AllocOp op) { qubits.add(op.getResult()); }) - .Case([&](ExtractOp op) { qubits.add(op.getResult()); }) - .Case([&](UnitaryOpInterface op) { - qubits.remap(op, WalkDirection::Forward); - }) - .Case([&](ResetOp op) { - qubits.remap(op.getQubitIn(), op.getQubitOut(), - WalkDirection::Forward); - }) - .Case([&](MeasureOp op) { - qubits.remap(op.getQubitIn(), op.getQubitOut(), - WalkDirection::Forward); - }) - .Case([&](InsertOp op) { qubits.remove(op.getScalar()); }) - .Case([&](SinkOp op) { qubits.remove(op.getQubit()); }); - } -} - -LogicalResult walkCircuitGraph(MutableArrayRef wires, - WalkDirection direction, WalkCircuitGraphFn fn) { - const auto step = direction == WalkDirection::Forward ? 1 : -1; - const auto proceed = [&](const WireIterator& it) { - if (direction == WalkDirection::Forward) { - return it != std::default_sentinel; - } - - if (it.operation() == nullptr) { - return false; - } - - return !isa(it.operation()); - }; - - ReleasedOps released; - - PendingWiresMap pending; - pending.reserve(wires.size()); - - SmallVector curr(wires.size()); - std::iota(curr.begin(), curr.end(), 0UL); - - SmallVector next; - next.reserve(wires.size()); - - while (!curr.empty()) { - for (std::size_t i : curr) { - auto& it = wires[i]; - while (proceed(it)) { - const auto res = - TypeSwitch(it.operation()) - .Case([&](UnitaryOpInterface op) { - // If there are fewer wires than the qubit requires inputs, - // it's impossible to release the operation. Hence, fail. - if (op.getNumQubits() > wires.size()) { - return CircuitWalkResult::Fail; - } - - if (op.getNumQubits() == 1) { - std::ranges::advance(it, step); - return CircuitWalkResult::Advance; - } - - // Insert the unitary to the pending map. - // The caller decides if this op should be released. - const auto [it, inserted] = pending.try_emplace(op); - auto& indices = it->second; - - if (inserted) { - indices.reserve(op.getNumQubits()); - } - - indices.emplace_back(i); - - return CircuitWalkResult::Hold; // Stop at multi-qubit gate. - }) - .Case([&](auto) { - std::ranges::advance(it, step); - return CircuitWalkResult::Advance; - }) - .Default([&](Operation* op) { - const auto name = op->getName().getStringRef(); - report_fatal_error("unknown op encountered: " + name); - return CircuitWalkResult::Fail; - }); - - if (res == CircuitWalkResult::Hold) { - break; - } - - if (res == CircuitWalkResult::Fail) { - return failure(); - } - } - } - - released.clear(); - const auto ready = make_filter_range(pending, IsReady{}); - const auto res = std::invoke(fn, ready, released); - if (res.wasInterrupted() || res.wasSkipped()) { - return failure(); - } - - for (UnitaryOpInterface op : released) { - const auto& mapIt = pending.find(op); - assert(mapIt != pending.end()); - - auto& indices = mapIt->second; - for (std::size_t i : mapIt->second) { - std::ranges::advance(wires[i], step); - next.emplace_back(i); - } - - pending.erase(mapIt); - } - - curr.swap(next); - next.clear(); - } - - return success(); -} -} // namespace mlir::qco diff --git a/mlir/lib/Dialect/QCO/Utils/Qubits.cpp b/mlir/lib/Dialect/QCO/Utils/Qubits.cpp new file mode 100644 index 0000000000..d448a0609d --- /dev/null +++ b/mlir/lib/Dialect/QCO/Utils/Qubits.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/QCO/Utils/Qubits.h" + +#include "mlir/Dialect/QCO/IR/QCODialect.h" + +#include + +#include +#include +#include + +namespace mlir::qco { +void Qubits::add(TypedValue q) { + const auto index = programToValue_.size(); + programToValue_.try_emplace(index, q); + valueToIndex_.try_emplace(q, std::make_pair(QubitLocation::Program, index)); +} + +void Qubits::add(TypedValue q, std::size_t hw) { + hardwareToValue_.try_emplace(hw, q); + valueToIndex_.try_emplace(q, std::make_pair(QubitLocation::Hardware, hw)); +} + +void Qubits::remap(TypedValue prev, TypedValue next) { + assert(valueToIndex_.contains(prev)); + const auto& [location, index] = valueToIndex_.lookup(prev); + + valueToIndex_.erase(prev); + valueToIndex_.try_emplace(next, std::make_pair(location, index)); + + if (location == QubitLocation::Program) { + programToValue_[index] = next; + return; + } + + hardwareToValue_[index] = next; +} + +void Qubits::remove(TypedValue q) { + assert(valueToIndex_.contains(q)); + const auto& [location, index] = valueToIndex_.lookup(q); + + valueToIndex_.erase(q); + + if (location == QubitLocation::Program) { + programToValue_.erase(index); + return; + } + + hardwareToValue_.erase(index); +} + +TypedValue Qubits::getProgramQubit(std::size_t index) const { + assert(programToValue_.contains(index)); + return programToValue_.lookup(index); +} + +TypedValue Qubits::getHardwareQubit(std::size_t index) const { + assert(hardwareToValue_.contains(index)); + return hardwareToValue_.lookup(index); +} +} // namespace mlir::qco \ No newline at end of file diff --git a/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp b/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp index ce6b3805b3..7aa0b1fd6a 100644 --- a/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp +++ b/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp @@ -10,7 +10,10 @@ #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Utils/Drivers.h" +#include "mlir/Dialect/QCO/Utils/Qubits.h" +#include "mlir/Dialect/QCO/Utils/WireIterator.h" #include #include @@ -18,9 +21,11 @@ #include #include #include +#include #include #include +#include using namespace mlir; @@ -40,7 +45,7 @@ class DriversTest : public testing::Test { }; } // namespace -TEST_F(DriversTest, FullWalk) { +TEST_F(DriversTest, ProgramWalk) { qco::QCOProgramBuilder builder(context.get()); builder.initialize(); const auto q00 = builder.allocQubit(); @@ -62,27 +67,148 @@ TEST_F(DriversTest, FullWalk) { builder.sink(q22); builder.sink(q32); - auto module = builder.finalize(); - auto func = *(module->getOps().begin()); + auto mod = builder.finalize(); + auto func = *(mod->getOps().begin()); Value ex0 = nullptr; Value ex1 = nullptr; Value ex2 = nullptr; Value ex3 = nullptr; - qco::walkUnit(func.getBody(), [&](Operation* op, const qco::Qubits& qubits) { - if (op == q03.getDefiningOp()) { - ex0 = qubits.getQubit(0); - ex1 = qubits.getQubit(1); - ex2 = qubits.getQubit(2); - ex3 = qubits.getQubit(3); - return WalkResult::interrupt(); - } - return WalkResult::advance(); - }); + // Walk until the first measurement operation is encountered and stop. + // Since WalkOrder::PreOrder is used here, the state of the qubits is not yet + // updated with the SSA values of the measurement op. + // Consequently, the program qubits point at the outputs of the controlled-Xs. + std::ignore = qco::walkProgram(func.getBody(), + [&](Operation* op, const qco::Qubits& qubits) { + if (op == q03.getDefiningOp()) { + ex0 = qubits.getProgramQubit(0); + ex1 = qubits.getProgramQubit(1); + ex2 = qubits.getProgramQubit(2); + ex3 = qubits.getProgramQubit(3); + return WalkResult::interrupt(); + } + return WalkResult::advance(); + }); ASSERT_EQ(ex0, q02); ASSERT_EQ(ex1, q11); ASSERT_EQ(ex2, q21); ASSERT_EQ(ex3, q31); } + +TEST_F(DriversTest, ProgramGraphWalk) { + qco::QCOProgramBuilder builder(context.get()); + builder.initialize(); + + const auto q00 = builder.allocQubit(); + const auto q10 = builder.allocQubit(); + const auto q20 = builder.allocQubit(); + const auto q30 = builder.allocQubit(); + + const auto q01 = builder.h(q00); + + const auto [q02, q11] = builder.cx(q01, q10); + const auto [q21, q31] = builder.cx(q20, q30); + + const auto q03 = builder.z(q02); + const auto q22 = builder.h(q21); + + const auto [q12, q23] = builder.cx(q11, q22); + + const auto [q04, q13] = builder.cx(q03, q12); + const auto q14 = builder.h(q13); + + builder.measure(q04); + builder.measure(q14); + builder.measure(q23); + builder.measure(q31); + + auto mod = builder.finalize(); + auto func = *(mod->getOps().begin()); + + // Collect wires. + SmallVector wires; + for (qco::AllocOp op : func.getOps()) { + wires.emplace_back(op.getResult()); + } + + // Unit-test supporting datastructure. + SmallVector> readyPerLayer; + + // Forward pass. + auto res = qco::walkProgramGraph( + wires, [&](const qco::ReadyRange& ready, qco::ReleasedOps& released) { + DenseSet layer; + for (const auto& [op, progs] : ready) { + layer.insert(op); + released.emplace_back(op); + } + readyPerLayer.emplace_back(layer); + return WalkResult::advance(); + }); + + ASSERT_TRUE(res.succeeded()); + ASSERT_GE(readyPerLayer.size(), 3); + ASSERT_TRUE(readyPerLayer[0].contains(q02.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[0].contains(q21.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[1].contains(q12.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[2].contains(q04.getDefiningOp())); + + // Backward pass. + readyPerLayer.clear(); + res = qco::walkProgramGraph( + wires, [&](const qco::ReadyRange& ready, qco::ReleasedOps& released) { + DenseSet layer; + for (const auto& [op, progs] : ready) { + layer.insert(op); + released.emplace_back(op); + } + readyPerLayer.emplace_back(layer); + return WalkResult::advance(); + }); + + ASSERT_TRUE(res.succeeded()); + ASSERT_GE(readyPerLayer.size(), 3); + ASSERT_TRUE(readyPerLayer[0].contains(q04.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[1].contains(q12.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[2].contains(q02.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[2].contains(q21.getDefiningOp())); + + // Forward, but instead of releasing all, we use ::skip(). + readyPerLayer.clear(); + res = qco::walkProgramGraph( + wires, [&](const qco::ReadyRange& ready, qco::ReleasedOps&) { + DenseSet layer; + for (const auto& [op, progs] : ready) { + layer.insert(op); + } + readyPerLayer.emplace_back(layer); + + return WalkResult::skip(); + }); + + ASSERT_TRUE(res.succeeded()); + ASSERT_GE(readyPerLayer.size(), 3); + ASSERT_TRUE(readyPerLayer[0].contains(q02.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[0].contains(q21.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[1].contains(q12.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[2].contains(q04.getDefiningOp())); + + // Backward, but stop after first layer. + readyPerLayer.clear(); + res = qco::walkProgramGraph( + wires, [&](const qco::ReadyRange& ready, qco::ReleasedOps& released) { + DenseSet layer; + for (const auto& [op, progs] : ready) { + layer.insert(op); + released.emplace_back(op); + } + readyPerLayer.emplace_back(layer); + return WalkResult::interrupt(); + }); + + ASSERT_TRUE(res.failed()); + ASSERT_EQ(readyPerLayer.size(), 1); + ASSERT_TRUE(readyPerLayer[0].contains(q04.getDefiningOp())); +} From d76d08e47294b9f7b3cbbf6ac4e17d5e71ca9687 Mon Sep 17 00:00:00 2001 From: Lukas Burgholzer Date: Tue, 28 Apr 2026 15:37:14 +0200 Subject: [PATCH 22/29] =?UTF-8?q?=F0=9F=90=9B=20Fix=20malformed=20include?= =?UTF-8?q?=20directories=20in=20exported=20`nlohmann=5Fjson`=20CMake=20ta?= =?UTF-8?q?rgets=20for=20component-based=20installs=20(#1662)=20(#1663)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR fixes the problems observed with the 3.5.0 release while updating in dependent MQT repositories. There was a malformed include directory install command, that did not even turn out to be necessary (through local testing with QCEC, QMAP, and DDSIM). This is essentially the forward port of #1662 and the `v3.5.1` release. ## Checklist - [x] The pull request only contains commits that are focused and relevant to this change. - [x] I have added appropriate tests that cover the new/changed functionality. - [x] I have updated the documentation to reflect these changes. - [x] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [x] I have added migration instructions to the upgrade guide (if needed). - [x] The changes follow the project's style guidelines and introduce no new warnings. - [x] The changes are fully tested and pass the CI checks. - [x] I have reviewed my own code changes. **If PR contains AI-assisted content:** - [x] I have disclosed the use of AI tools in the PR description as per our [AI Usage Guidelines](https://github.com/munich-quantum-toolkit/core/blob/main/docs/ai_usage.md). - [x] AI-assisted commits include an `Assisted-by: [Model Name] via [Tool Name]` footer. - [x] I confirm that I have personally reviewed and understood all AI-generated content, and accept full responsibility for it. --------- Signed-off-by: Lukas Burgholzer --- CHANGELOG.md | 12 +++++++++++- UPGRADING.md | 15 ++++++++++++++- cmake/ExternalDependencies.cmake | 7 +------ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 934262a36b..5741e3dfa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,12 @@ This project adheres to [Semantic Versioning], with the exception that minor rel - � Remove the density matrix support from the MQT Core DD package ([#1466]) ([**@burgholzer**]) - � Remove `datastructures` (`ds`) (sub)library from MQT Core ([#1458]) ([**@burgholzer**]) +## [3.5.1] - 2026-04-23 + +### Fixed + +- 🐛 Fix malformed include directories in exported `nlohmann_json` CMake targets for component-based installs ([#1662]) ([**@burgholzer**]) + ## [3.5.0] - 2026-04-21 _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#350)._ @@ -216,6 +222,8 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#330)._ ## [3.2.0] - 2025-07-31 +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#320)._ + ### Added - � Build Python 3.14 wheels ([#1076]) ([**@denialhaag**]) @@ -334,7 +342,8 @@ _� Refer to the [GitHub Release Notes](https://github.com/munich-quantum-toolk -[unreleased]: https://github.com/munich-quantum-toolkit/core/compare/v3.5.0...HEAD +[unreleased]: https://github.com/munich-quantum-toolkit/core/compare/v3.5.1...HEAD +[3.5.1]: https://github.com/munich-quantum-toolkit/core/releases/tag/v3.5.1 [3.5.0]: https://github.com/munich-quantum-toolkit/core/releases/tag/v3.5.0 [3.4.1]: https://github.com/munich-quantum-toolkit/core/releases/tag/v3.4.1 [3.4.0]: https://github.com/munich-quantum-toolkit/core/releases/tag/v3.4.0 @@ -353,6 +362,7 @@ _� Refer to the [GitHub Release Notes](https://github.com/munich-quantum-toolk [#1664]: https://github.com/munich-quantum-toolkit/core/pull/1664 +[#1662]: https://github.com/munich-quantum-toolkit/core/pull/1662 [#1652]: https://github.com/munich-quantum-toolkit/core/pull/1652 [#1637]: https://github.com/munich-quantum-toolkit/core/pull/1637 [#1635]: https://github.com/munich-quantum-toolkit/core/pull/1635 diff --git a/UPGRADING.md b/UPGRADING.md index f30be35649..b1182b0b35 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -37,6 +37,15 @@ The `datastructures` (sub)library has been removed from the MQT Core repository. Its functionality has only ever been used in [MQT QMAP] since its inception. As a consequence, the code shall be moved to [MQT QMAP] once QMAP adopts an MQT Core version that includes this change. +## [3.5.1] + +No breaking changes. + +### Component-based CMake installs + +Fixed exported `nlohmann_json` CMake metadata so `find_package(mqt-core CONFIG)` no longer propagates an invalid `.../COMPONENT` include directory in component-based installations. +Anyone relying on an installed version of `mqt-core` shall update from `3.5.0` to `3.5.1`. + ## [3.5.0] The shared library ABI version (`SOVERSION`) is increased from `3.4` to `3.5`. @@ -49,6 +58,9 @@ Any existing code that uses the `mqt-core` Python bindings will need to be recom ## [3.4.0] +The shared library ABI version (`SOVERSION`) is increased from `3.3` to `3.4`. +Thus, consuming libraries need to update their wheel repair configuration for `cibuildwheel` to ensure the `mqt-core` libraries are properly skipped in the wheel repair step. + ### Python wheels This release contains two changes to the distributed wheels. @@ -227,7 +239,8 @@ It also requires the `uv` library version 0.5.20 or higher. -[unreleased]: https://github.com/munich-quantum-toolkit/core/compare/v3.5.0...HEAD +[unreleased]: https://github.com/munich-quantum-toolkit/core/compare/v3.5.1...HEAD +[3.5.1]: https://github.com/munich-quantum-toolkit/core/compare/v3.5.0...v3.5.1 [3.5.0]: https://github.com/munich-quantum-toolkit/core/compare/v3.4.0...v3.5.0 [3.4.0]: https://github.com/munich-quantum-toolkit/core/compare/v3.3.0...v3.4.0 [3.3.0]: https://github.com/munich-quantum-toolkit/core/compare/v3.2.0...v3.3.0 diff --git a/cmake/ExternalDependencies.cmake b/cmake/ExternalDependencies.cmake index 9f06253a62..f1d2077336 100644 --- a/cmake/ExternalDependencies.cmake +++ b/cmake/ExternalDependencies.cmake @@ -191,12 +191,7 @@ if(MQT_CORE_JSON_INSTALL AND TARGET nlohmann_json) DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT ${MQT_CORE_TARGET_NAME}_Development) - install( - TARGETS nlohmann_json - EXPORT ${MQT_CORE_JSON_TARGETS_EXPORT_NAME} - INCLUDES - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - COMPONENT ${MQT_CORE_TARGET_NAME}_Development) + install(TARGETS nlohmann_json EXPORT ${MQT_CORE_JSON_TARGETS_EXPORT_NAME}) install( EXPORT ${MQT_CORE_JSON_TARGETS_EXPORT_NAME} From 46742ecdbf6b2d193c2c2bcaa068558135d4413a Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 29 Apr 2026 15:59:13 +0200 Subject: [PATCH 23/29] =?UTF-8?q?=F0=9F=8E=A8=20Clean=20up=20`llvm`=20incl?= =?UTF-8?q?udes=20(#1673)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR cleans up `llvm` includes by relying on [`mlir/Support/LLVM.h`](https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Support/LLVM.h) as much as possible in header files. ## Checklist - [x] The pull request only contains commits that are focused and relevant to this change. - [x] ~~I have added appropriate tests that cover the new/changed functionality.~~ - [x] ~~I have updated the documentation to reflect these changes.~~ - [x] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [x] ~~I have added migration instructions to the upgrade guide (if needed).~~ - [x] The changes follow the project's style guidelines and introduce no new warnings. - [x] The changes are fully tested and pass the CI checks. - [x] I have reviewed my own code changes. --------- Signed-off-by: burgholzer Co-authored-by: burgholzer --- CHANGELOG.md | 3 +- .../Dialect/QC/Builder/QCProgramBuilder.h | 50 +- mlir/include/mlir/Dialect/QC/IR/QCDialect.h | 1 - .../include/mlir/Dialect/QC/IR/QCInterfaces.h | 4 +- mlir/include/mlir/Dialect/QC/IR/QCOps.td | 5 +- .../mlir/Dialect/QC/Transforms/Passes.h | 2 - .../Dialect/QCO/Builder/QCOProgramBuilder.h | 54 +- mlir/include/mlir/Dialect/QCO/IR/QCODialect.h | 2 - .../mlir/Dialect/QCO/IR/QCOInterfaces.h | 4 +- mlir/include/mlir/Dialect/QCO/IR/QCOOps.td | 17 +- mlir/include/mlir/Dialect/QCO/QCOUtils.h | 21 +- .../QCO/Transforms/Mapping/Architecture.h | 15 +- .../mlir/Dialect/QCO/Transforms/Passes.h | 3 - .../mlir/Dialect/QCO/Utils/WireIterator.h | 14 +- .../Dialect/QIR/Builder/QIRProgramBuilder.h | 56 +- .../include/mlir/Dialect/QIR/Utils/QIRUtils.h | 1 - .../mlir/Dialect/QTensor/Transforms/Passes.h | 2 - mlir/include/mlir/Support/Passes.h | 2 +- mlir/include/mlir/Support/PrettyPrinting.h | 29 +- mlir/lib/Compiler/CompilerPipeline.cpp | 5 +- mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp | 41 +- mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp | 27 +- mlir/lib/Conversion/QCOToQC/QCOToQC.cpp | 6 +- mlir/lib/Conversion/QCToQCO/QCToQCO.cpp | 28 +- mlir/lib/Conversion/QCToQIR/QCToQIR.cpp | 8 +- .../Dialect/QC/Builder/QCProgramBuilder.cpp | 44 +- mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp | 21 +- mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp | 22 +- .../QC/Transforms/ShrinkQubitRegisters.cpp | 16 +- .../TranslateQuantumComputationToQC.cpp | 6 +- .../Dialect/QCO/Builder/QCOProgramBuilder.cpp | 137 ++-- mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp | 28 +- mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp | 30 +- .../lib/Dialect/QCO/IR/Operations/ResetOp.cpp | 9 +- .../IR/Operations/StandardGates/BarrierOp.cpp | 6 +- .../QCO/IR/Operations/StandardGates/RXXOp.cpp | 1 - .../QCO/IR/Operations/StandardGates/RYYOp.cpp | 1 - .../QCO/IR/Operations/StandardGates/RZXOp.cpp | 1 - .../QCO/IR/Operations/StandardGates/RZZOp.cpp | 1 - .../Operations/StandardGates/XXMinusYYOp.cpp | 6 +- .../Operations/StandardGates/XXPlusYYOp.cpp | 5 +- mlir/lib/Dialect/QCO/IR/QCOOps.cpp | 4 +- .../Dialect/QCO/IR/QubitManagement/SinkOp.cpp | 5 +- mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp | 38 +- .../QCO/Transforms/Mapping/Architecture.cpp | 5 +- .../QCO/Transforms/Mapping/Mapping.cpp | 3 +- .../MergeSingleQubitRotationGates.cpp | 9 +- mlir/lib/Dialect/QCO/Utils/WireIterator.cpp | 10 +- .../Dialect/QIR/Builder/QIRProgramBuilder.cpp | 21 +- .../lib/Dialect/QIR/Transforms/QIRCleanup.cpp | 19 +- mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp | 3 +- .../Dialect/QTensor/IR/Operations/AllocOp.cpp | 1 - .../QTensor/IR/Operations/ExtractOp.cpp | 1 - .../QTensor/IR/Operations/InsertOp.cpp | 6 +- .../QTensor/Transforms/ShrinkRegisters.cpp | 34 +- mlir/lib/Support/IRVerification.cpp | 125 ++- mlir/lib/Support/Passes.cpp | 7 +- mlir/lib/Support/PrettyPrinting.cpp | 62 +- mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp | 6 +- .../QCO/Transforms/Mapping/test_mapping.cpp | 1 + .../test_qco_merge_single_qubit_rotation.cpp | 20 +- .../Dialect/QCO/Utils/test_drivers.cpp | 2 + mlir/unittests/Dialect/Utils/test_utils.cpp | 11 +- mlir/unittests/programs/qco_programs.cpp | 741 ++++++++---------- 64 files changed, 862 insertions(+), 1006 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5741e3dfa4..20500bb816 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel - ✨ Add conversions between `jeff` and QCO ([#1479], [#1548], [#1565], [#1637]) ([**@denialhaag**]) - ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588], [#1664]) ([**@MatthiasReumann**], [**@burgholzer**]) - ✨ Add initial infrastructure for new QC and QCO MLIR dialects - ([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635]) + ([#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], [#1673]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**]) ### Changed @@ -361,6 +361,7 @@ _� Refer to the [GitHub Release Notes](https://github.com/munich-quantum-toolk +[#1673]: https://github.com/munich-quantum-toolkit/core/pull/1673 [#1664]: https://github.com/munich-quantum-toolkit/core/pull/1664 [#1662]: https://github.com/munich-quantum-toolkit/core/pull/1662 [#1652]: https://github.com/munich-quantum-toolkit/core/pull/1652 diff --git a/mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h b/mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h index 65d07ebe73..1cb1ad0394 100644 --- a/mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h +++ b/mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h @@ -10,22 +10,24 @@ #pragma once -#include -#include -#include #include -#include -#include #include #include -#include #include #include #include #include -namespace mlir::qc { +namespace mlir { + +// Forward declarations +class MLIRContext; +class ModuleOp; +class Operation; +class ValueRange; + +namespace qc { /** * @brief Builder API for constructing quantum programs in the QC dialect @@ -113,12 +115,7 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder { * @param index The index of the qubit to access * @return The specified qubit value */ - Value operator[](size_t index) const { - if (index >= qubits.size()) { - llvm::report_fatal_error("Qubit index out of bounds"); - } - return qubits[index]; - } + Value operator[](size_t index) const; /** * @brief Conversion to the backing MemRef value @@ -201,16 +198,7 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder { * @param index The index of the bit to access (must be less than size) * @return A Bit structure representing the specified bit */ - Bit operator[](const int64_t index) const { - if (index < 0 || index >= size) { - const std::string msg = "Bit index " + std::to_string(index) + - " out of bounds for register '" + name + - "' of size " + std::to_string(size); - llvm::reportFatalUsageError(msg.c_str()); - } - return { - .registerName = name, .registerSize = size, .registerIndex = index}; - } + Bit operator[](const int64_t index) const; }; /** @@ -891,8 +879,7 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder { * } : !qc.qubit * ``` */ - QCProgramBuilder& ctrl(ValueRange controls, - const llvm::function_ref& body); + QCProgramBuilder& ctrl(ValueRange controls, const function_ref& body); /** * @brief Apply an inverse (i.e., adjoint) operation. @@ -911,7 +898,7 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder { * } * ``` */ - QCProgramBuilder& inv(const llvm::function_ref& body); + QCProgramBuilder& inv(const function_ref& body); //===--------------------------------------------------------------------===// // Deallocation @@ -965,19 +952,19 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder { */ static OwningOpRef build(MLIRContext* context, - const llvm::function_ref& buildFunc); + const function_ref& buildFunc); private: enum class AllocationMode : uint8_t { Unset, Static, Dynamic }; MLIRContext* ctx{}; - ModuleOp module; + Operation* module; /// Track allocated qubits for automatic deallocation - llvm::DenseSet allocatedQubits; + DenseSet allocatedQubits; /// Track allocated MemRefs for automatic deallocation - llvm::DenseSet allocatedMemrefs; + DenseSet allocatedMemrefs; /// Check if the builder has been finalized void checkFinalized() const; @@ -988,4 +975,5 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder { /// Ensure static and dynamic qubit allocation modes are not mixed. void ensureAllocationMode(AllocationMode requestedMode); }; -} // namespace mlir::qc +} // namespace qc +} // namespace mlir diff --git a/mlir/include/mlir/Dialect/QC/IR/QCDialect.h b/mlir/include/mlir/Dialect/QC/IR/QCDialect.h index 69d11e8471..96fbb7f724 100644 --- a/mlir/include/mlir/Dialect/QC/IR/QCDialect.h +++ b/mlir/include/mlir/Dialect/QC/IR/QCDialect.h @@ -10,7 +10,6 @@ #pragma once -#include #include #define DIALECT_NAME_QC "qc" diff --git a/mlir/include/mlir/Dialect/QC/IR/QCInterfaces.h b/mlir/include/mlir/Dialect/QC/IR/QCInterfaces.h index fce8cd8dc3..94421a6fed 100644 --- a/mlir/include/mlir/Dialect/QC/IR/QCInterfaces.h +++ b/mlir/include/mlir/Dialect/QC/IR/QCInterfaces.h @@ -10,9 +10,7 @@ #pragma once -#include -#include -#include +#include #include diff --git a/mlir/include/mlir/Dialect/QC/IR/QCOps.td b/mlir/include/mlir/Dialect/QC/IR/QCOps.td index 2b15a305ed..8e76c9c3ba 100644 --- a/mlir/include/mlir/Dialect/QC/IR/QCOps.td +++ b/mlir/include/mlir/Dialect/QC/IR/QCOps.td @@ -960,7 +960,7 @@ def CtrlOp }]; let builders = [OpBuilder<(ins "ValueRange":$controls, - "const llvm::function_ref&":$bodyBuilder)>]; + "const function_ref&":$bodyBuilder)>]; let hasCanonicalizer = 1; let hasVerifier = 1; @@ -1002,8 +1002,7 @@ def InvOp : QCOp<"inv", static StringRef getBaseSymbol() { return "inv"; } }]; - let builders = [OpBuilder<(ins - "const llvm::function_ref&":$bodyBuilder)>]; + let builders = [OpBuilder<(ins "const function_ref&":$bodyBuilder)>]; let hasCanonicalizer = 1; let hasVerifier = 1; diff --git a/mlir/include/mlir/Dialect/QC/Transforms/Passes.h b/mlir/include/mlir/Dialect/QC/Transforms/Passes.h index 435be783d7..18c6a600e2 100644 --- a/mlir/include/mlir/Dialect/QC/Transforms/Passes.h +++ b/mlir/include/mlir/Dialect/QC/Transforms/Passes.h @@ -10,8 +10,6 @@ #pragma once -#include "mlir/Dialect/QC/IR/QCDialect.h" - #include #include diff --git a/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h b/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h index 757ffec61e..94b1f85eda 100644 --- a/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h +++ b/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h @@ -10,16 +10,9 @@ #pragma once -#include -#include -#include -#include #include -#include -#include #include #include -#include #include #include @@ -27,7 +20,15 @@ #include #include -namespace mlir::qco { +namespace mlir { + +// Forward declarations +class MLIRContext; +class ModuleOp; +class Operation; +class ValueRange; + +namespace qco { /** * @brief Builder API for constructing quantum programs in the QCO dialect @@ -122,12 +123,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { * @param index The index of the qubit to access * @return The specified qubit value */ - Value& operator[](size_t index) { - if (index >= qubits.size()) { - llvm::reportFatalUsageError("Qubit index out of bounds"); - } - return qubits[index]; - } + Value& operator[](size_t index); /** * @brief Conversion to the backing QTensor value @@ -210,16 +206,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { * @param index The index of the bit to access (must be less than size) * @return A Bit structure representing the specified bit */ - Bit operator[](const int64_t index) const { - if (index < 0 || index >= size) { - const std::string msg = "Bit index " + std::to_string(index) + - " out of bounds for register '" + name + - "' of size " + std::to_string(size); - llvm::reportFatalUsageError(msg.c_str()); - } - return { - .registerName = name, .registerSize = size, .registerIndex = index}; - } + Bit operator[](int64_t index) const; }; /** @@ -1171,7 +1158,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { */ std::pair ctrl(ValueRange controls, ValueRange targets, - llvm::function_ref(ValueRange)> body); + function_ref(ValueRange)> body); /** * @brief Apply an inverse operation @@ -1196,7 +1183,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { * ``` */ ValueRange inv(ValueRange qubits, - llvm::function_ref(ValueRange)> body); + function_ref(ValueRange)> body); //===--------------------------------------------------------------------===// // Deallocation @@ -1263,8 +1250,8 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { */ ValueRange qcoIf(const std::variant& condition, ValueRange qubits, - llvm::function_ref(ValueRange)> thenBody, - llvm::function_ref(ValueRange)> elseBody = nullptr); + function_ref(ValueRange)> thenBody, + function_ref(ValueRange)> elseBody = nullptr); //===--------------------------------------------------------------------===// // Finalization @@ -1294,13 +1281,13 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { */ static OwningOpRef build(MLIRContext* context, - const llvm::function_ref& buildFunc); + const function_ref& buildFunc); private: enum class AllocationMode : uint8_t { Unset, Static, Dynamic }; MLIRContext* ctx{}; - ModuleOp module; + Operation* module; /// Check if the builder has been finalized void checkFinalized() const; @@ -1340,7 +1327,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { /// Only values present in this map are valid for use in operations. /// When an operation consumes a qubit and produces a new one, the old value /// is removed and the new output is added. - llvm::DenseMap validQubits; + DenseMap validQubits; /** * @brief Validate that a tensor value is valid and unconsumed. This also @@ -1370,7 +1357,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { /// Only values present in this map are valid for use in operations. /// When an operation consumes a tensor and produces a new one, the old value /// is removed and the new output is added. - llvm::DenseMap validTensors; + DenseMap validTensors; /// Track whether static or dynamic qubit allocation is used. AllocationMode allocationMode = AllocationMode::Unset; @@ -1378,4 +1365,5 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { /// Ensure static and dynamic qubit allocation modes are not mixed. void ensureAllocationMode(AllocationMode requestedMode); }; -} // namespace mlir::qco +} // namespace qco +} // namespace mlir diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h index a5286162b7..60632679a0 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h @@ -10,8 +10,6 @@ #pragma once -#include -#include #include #define DIALECT_NAME_QCO "qco" diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.h b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.h index df69bd2082..035b0b5268 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.h @@ -11,11 +11,9 @@ #pragma once #include -#include #include #include -#include -#include +#include #include #include diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td index 20c809aaf7..c96dedf718 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td @@ -1132,7 +1132,7 @@ def CtrlOp build($_builder, $_state, controls.getTypes(), targets.getTypes(), controls, targets); }]>, OpBuilder<(ins "ValueRange":$controls, "ValueRange":$targets, - "llvm::function_ref(ValueRange)" + "function_ref(ValueRange)" ">":$bodyBuilder)>]; let hasCanonicalizer = 1; @@ -1202,7 +1202,7 @@ def InvOp build($_builder, $_state, qubits.getTypes(), qubits); }]>, OpBuilder<(ins "ValueRange":$qubits, - "llvm::function_ref(ValueRange)" + "function_ref(ValueRange)" ">":$bodyBuilder)>]; let hasCanonicalizer = 1; @@ -1256,15 +1256,14 @@ def IfOp `{` type($results) `}` }]; - let builders = - [OpBuilder<(ins "Value":$condition, "ValueRange":$qubits), [{ + let builders = [OpBuilder<(ins "Value":$condition, "ValueRange":$qubits), [{ build($_builder, $_state, qubits.getTypes(), condition, qubits); }]>, - OpBuilder<(ins "Value":$condition, "ValueRange":$qubits, - "llvm::function_ref(ValueRange)" - ">":$thenBuilder, - CArg<"llvm::function_ref(ValueRange)>", - "nullptr">:$elseBuilder)>]; + OpBuilder<(ins "Value":$condition, "ValueRange":$qubits, + "function_ref(ValueRange)" + ">":$thenBuilder, + CArg<"function_ref(ValueRange)>", + "nullptr">:$elseBuilder)>]; let extraClassDeclaration = [{ Block *thenBlock() { diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index f888509129..a5fdba0c35 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -10,10 +10,9 @@ #pragma once -#include "mlir/Dialect/Utils/Utils.h" - #include #include +#include namespace mlir::qco { @@ -27,7 +26,7 @@ namespace mlir::qco { * @return LogicalResult Success or failure of the removal. */ template -mlir::LogicalResult +LogicalResult removeInversePairOneTargetZeroParameter(OpType op, PatternRewriter& rewriter) { // Check if the successor is the inverse operation auto nextOp = @@ -52,7 +51,7 @@ removeInversePairOneTargetZeroParameter(OpType op, PatternRewriter& rewriter) { * @return LogicalResult Success or failure of the removal. */ template -mlir::LogicalResult +LogicalResult removeInversePairTwoTargetZeroParameter(OpType op, PatternRewriter& rewriter) { // Check if the successor is the inverse operation auto nextOp = @@ -82,7 +81,7 @@ removeInversePairTwoTargetZeroParameter(OpType op, PatternRewriter& rewriter) { * @return LogicalResult Success or failure of the removal. */ template -mlir::LogicalResult +LogicalResult removeTwoTargetZeroParameterPairWithSwappedTargets(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation @@ -118,8 +117,8 @@ removeTwoTargetZeroParameterPairWithSwappedTargets(OpType op, * @return LogicalResult Success or failure of the merge. */ template -mlir::LogicalResult mergeOneTargetZeroParameter(OpType op, - PatternRewriter& rewriter) { +LogicalResult mergeOneTargetZeroParameter(OpType op, + PatternRewriter& rewriter) { // Check if the successor is the same operation auto nextOp = llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { @@ -148,8 +147,7 @@ mlir::LogicalResult mergeOneTargetZeroParameter(OpType op, * @return LogicalResult Success or failure of the merge. */ template -mlir::LogicalResult mergeOneTargetOneParameter(OpType op, - PatternRewriter& rewriter) { +LogicalResult mergeOneTargetOneParameter(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation auto nextOp = llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { @@ -179,8 +177,7 @@ mlir::LogicalResult mergeOneTargetOneParameter(OpType op, * @return LogicalResult Success or failure of the merge. */ template -mlir::LogicalResult mergeTwoTargetOneParameter(OpType op, - PatternRewriter& rewriter) { +LogicalResult mergeTwoTargetOneParameter(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation auto nextOp = llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { @@ -217,7 +214,7 @@ mlir::LogicalResult mergeTwoTargetOneParameter(OpType op, * @return LogicalResult Success or failure of the merge. */ template -mlir::LogicalResult +LogicalResult mergeTwoTargetOneParameterWithSwappedTargets(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Mapping/Architecture.h b/mlir/include/mlir/Dialect/QCO/Transforms/Mapping/Architecture.h index ca32d645c4..d0207b6921 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Mapping/Architecture.h +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Mapping/Architecture.h @@ -12,8 +12,6 @@ #include #include -#include -#include #include #include @@ -27,15 +25,15 @@ namespace mlir::qco { */ class [[nodiscard]] Architecture { public: - using CouplingSet = mlir::DenseSet>; - using NeighbourVector = mlir::SmallVector>; + using CouplingSet = DenseSet>; + using NeighbourVector = SmallVector>; explicit Architecture(std::string name, std::size_t nqubits, CouplingSet couplingSet) : name_(std::move(name)), nqubits_(nqubits), couplingSet_(std::move(couplingSet)), neighbours_(nqubits), - dist_(nqubits, mlir::SmallVector(nqubits, UINT64_MAX)), - prev_(nqubits, mlir::SmallVector(nqubits, UINT64_MAX)) { + dist_(nqubits, SmallVector(nqubits, UINT64_MAX)), + prev_(nqubits, SmallVector(nqubits, UINT64_MAX)) { floydWarshallWithPathReconstruction(); collectNeighbours(); } @@ -63,8 +61,7 @@ class [[nodiscard]] Architecture { /** * @brief Collect all neighbours of @p u. */ - [[nodiscard]] mlir::SmallVector - neighboursOf(std::size_t u) const; + [[nodiscard]] SmallVector neighboursOf(std::size_t u) const; /** * @brief Return the maximum degree (connectivity) of any qubit in the @@ -73,7 +70,7 @@ class [[nodiscard]] Architecture { [[nodiscard]] std::size_t maxDegree() const; private: - using Matrix = mlir::SmallVector, 0>; + using Matrix = SmallVector, 0>; /** * @brief Find all shortest paths in the coupling map between two qubits. diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.h b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.h index c3589793e6..bcaaeb1fe1 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.h +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.h @@ -10,9 +10,6 @@ #pragma once -#include "mlir/Dialect/QCO/IR/QCODialect.h" - -#include #include #include diff --git a/mlir/include/mlir/Dialect/QCO/Utils/WireIterator.h b/mlir/include/mlir/Dialect/QCO/Utils/WireIterator.h index 73dcbdc870..70ba4b26ff 100644 --- a/mlir/include/mlir/Dialect/QCO/Utils/WireIterator.h +++ b/mlir/include/mlir/Dialect/QCO/Utils/WireIterator.h @@ -30,20 +30,20 @@ class [[nodiscard]] WireIterator { public: using iterator_category = std::bidirectional_iterator_tag; using difference_type = std::ptrdiff_t; - using value_type = mlir::Operation*; + using value_type = Operation*; WireIterator() : op_(nullptr), qubit_(nullptr), isSentinel_(false) {} - explicit WireIterator(mlir::Value qubit) + explicit WireIterator(Value qubit) : op_(qubit.getDefiningOp()), qubit_(qubit), isSentinel_(false) {} /// @returns the operation the iterator points to. - [[nodiscard]] mlir::Operation* operation() const { return op_; } + [[nodiscard]] Operation* operation() const { return op_; } /// @returns the operation the iterator points to. - [[nodiscard]] mlir::Operation* operator*() const { return operation(); } + [[nodiscard]] Operation* operator*() const { return operation(); } /// @returns the qubit the iterator points to. - [[nodiscard]] mlir::Value qubit() const; + [[nodiscard]] Value qubit() const; WireIterator& operator++() { forward(); @@ -83,8 +83,8 @@ class [[nodiscard]] WireIterator { /// @brief Move to the previous operation on the qubit wire. void backward(); - mlir::Operation* op_; - mlir::Value qubit_; + Operation* op_; + Value qubit_; bool isSentinel_; }; diff --git a/mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h b/mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h index 5121cfd06d..97095e47ac 100644 --- a/mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h +++ b/mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h @@ -12,22 +12,14 @@ #include "mlir/Dialect/QIR/Utils/QIRMetadata.h" -#include -#include -#include -#include #include -#include #include -#include #include #include #include -#include #include #include -#include -#include +#include #include #include @@ -35,11 +27,14 @@ #include namespace mlir { +// Forward declarations class Block; +class MLIRContext; +class ModuleOp; class Operation; -} // namespace mlir +class ValueRange; -namespace mlir::qir { +namespace qir { /** * @brief Builder API for constructing QIR (Quantum Intermediate @@ -196,7 +191,7 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { * %q2 = llvm.load %ptr2 : !llvm.ptr -> !llvm.ptr * ``` */ - llvm::SmallVector allocQubitRegister(int64_t size); + SmallVector allocQubitRegister(int64_t size); /** * @brief A small structure representing a single classical bit within a @@ -204,7 +199,7 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { */ struct Bit { /// Name of the register containing this bit - llvm::StringRef registerName; + StringRef registerName; /// Size of the register containing this bit int64_t registerSize{}; /// Index of this bit within the register @@ -225,16 +220,7 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { * @param index The index of the bit to access (must be less than size) * @return A Bit structure representing the specified bit */ - Bit operator[](const int64_t index) const { - if (index < 0 || index >= size) { - const std::string msg = "Bit index " + std::to_string(index) + - " out of bounds for register '" + name + - "' of size " + std::to_string(size); - llvm::reportFatalUsageError(msg.c_str()); - } - return { - .registerName = name, .registerSize = size, .registerIndex = index}; - } + Bit operator[](const int64_t index) const; }; /** @@ -889,13 +875,13 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { */ static OwningOpRef build(MLIRContext* context, - const llvm::function_ref& buildFunc); + const function_ref& buildFunc); private: enum class AllocationMode : uint8_t { Unset, Static, Dynamic }; /// The main module - ModuleOp module; + Operation* module{}; /// The main function Operation* mainFunc{}; @@ -917,22 +903,22 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { Value exitCode; /// Cache static qubit pointers for reuse - llvm::DenseMap staticQubits; + DenseMap staticQubits; /// Set of qubit pointers - llvm::DenseSet qubits; + DenseSet qubits; /// Set of qubit-array pointers - llvm::DenseSet qubitArrays; + DenseSet qubitArrays; /// Map from register name to result-array pointer llvm::StringMap resultArrays; /// Map from (register name, index) to loaded result - llvm::DenseMap, Value> loadedResults; + DenseMap, Value> loadedResults; /// Map from result index to result pointer for non-register results - llvm::DenseMap resultPtrs; + DenseMap resultPtrs; /// Track qubit and result counts for QIR metadata QIRMetadata metadata_; @@ -951,10 +937,9 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { * @param targets Target qubits * @param fnName Name of the QIR function to call */ - void - createCallOp(const llvm::SmallVector>& parameters, - ValueRange controls, const SmallVector& targets, - llvm::StringRef fnName); + void createCallOp(const SmallVector>& parameters, + ValueRange controls, const SmallVector& targets, + StringRef fnName); /** * @brief Generate array-based output recording in the output block @@ -977,4 +962,5 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { void ensureAllocationMode(AllocationMode requestedMode); }; -} // namespace mlir::qir +} // namespace qir +} // namespace mlir diff --git a/mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h b/mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h index 6a28995c83..d594e3df7a 100644 --- a/mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h +++ b/mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h @@ -12,7 +12,6 @@ #include "mlir/Dialect/QIR/Utils/QIRMetadata.h" -#include #include #include #include diff --git a/mlir/include/mlir/Dialect/QTensor/Transforms/Passes.h b/mlir/include/mlir/Dialect/QTensor/Transforms/Passes.h index e33924b5a5..8e53bea5a6 100644 --- a/mlir/include/mlir/Dialect/QTensor/Transforms/Passes.h +++ b/mlir/include/mlir/Dialect/QTensor/Transforms/Passes.h @@ -10,8 +10,6 @@ #pragma once -#include "mlir/Dialect/QTensor/IR/QTensorDialect.h" - #include #include diff --git a/mlir/include/mlir/Support/Passes.h b/mlir/include/mlir/Support/Passes.h index f780be87f9..9a7ec7a2e8 100644 --- a/mlir/include/mlir/Support/Passes.h +++ b/mlir/include/mlir/Support/Passes.h @@ -10,7 +10,7 @@ #pragma once -#include "mlir/Support/LogicalResult.h" +#include namespace mlir { class ModuleOp; diff --git a/mlir/include/mlir/Support/PrettyPrinting.h b/mlir/include/mlir/Support/PrettyPrinting.h index 9dd1fd4fca..06cc0f783b 100644 --- a/mlir/include/mlir/Support/PrettyPrinting.h +++ b/mlir/include/mlir/Support/PrettyPrinting.h @@ -10,10 +10,8 @@ #pragma once -#include -#include -#include #include +#include namespace mlir { @@ -29,7 +27,7 @@ class ModuleOp; * @param str The string to measure * @return The display width in columns */ -int calculateDisplayWidth(llvm::StringRef str); +int calculateDisplayWidth(StringRef str); /** * @brief Wrap a long line into multiple lines that fit within the box @@ -45,30 +43,29 @@ int calculateDisplayWidth(llvm::StringRef str); * @param indent Number of spaces to indent wrapped lines * @param result Output vector to store wrapped lines */ -void wrapLine(llvm::StringRef line, int maxWidth, - llvm::SmallVectorImpl>& result, - int indent = 0); +void wrapLine(StringRef line, int maxWidth, + SmallVectorImpl>& result, int indent = 0); /** * @brief Print top border of a box * * @param os Output stream to write to */ -void printBoxTop(llvm::raw_ostream& os = llvm::errs()); +void printBoxTop(raw_ostream& os = llvm::errs()); /** * @brief Print middle separator of a box * * @param os Output stream to write to */ -void printBoxMiddle(llvm::raw_ostream& os = llvm::errs()); +void printBoxMiddle(raw_ostream& os = llvm::errs()); /** * @brief Print bottom border of a box * * @param os Output stream to write to */ -void printBoxBottom(llvm::raw_ostream& os = llvm::errs()); +void printBoxBottom(raw_ostream& os = llvm::errs()); /** * @brief Print a box line with text and proper padding @@ -80,8 +77,8 @@ void printBoxBottom(llvm::raw_ostream& os = llvm::errs()); * @param indent Number of spaces to indent the text (0 for left-aligned) * @param os Output stream to write to */ -void printBoxLine(llvm::StringRef text, int indent = 0, - llvm::raw_ostream& os = llvm::errs()); +void printBoxLine(StringRef text, int indent = 0, + raw_ostream& os = llvm::errs()); /** * @brief Print multiple lines of text within the box, with line wrapping @@ -94,8 +91,8 @@ void printBoxLine(llvm::StringRef text, int indent = 0, * @param indent Number of spaces to indent the text * @param os Output stream to write to */ -void printBoxText(llvm::StringRef text, int indent = 0, - llvm::raw_ostream& os = llvm::errs()); +void printBoxText(StringRef text, int indent = 0, + raw_ostream& os = llvm::errs()); /** * @brief Pretty print an MLIR module with a header and box formatting @@ -104,7 +101,7 @@ void printBoxText(llvm::StringRef text, int indent = 0, * @param header Optional header text to display above the module * @param os Output stream to write to */ -void printProgram(ModuleOp module, llvm::StringRef header = "", - llvm::raw_ostream& os = llvm::errs()); +void printProgram(ModuleOp module, StringRef header = "", + raw_ostream& os = llvm::errs()); } // namespace mlir diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index 3ca8becf2e..5a4ca8697c 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -17,11 +17,10 @@ #include "mlir/Support/Passes.h" #include "mlir/Support/PrettyPrinting.h" -#include #include #include #include -#include +#include #include @@ -35,7 +34,7 @@ namespace mlir { * @param stageNumber Current stage number * @param totalStages Total number of stages (for progress indication) */ -static void prettyPrintStage(ModuleOp module, const llvm::StringRef stageName, +static void prettyPrintStage(ModuleOp module, const StringRef stageName, const int stageNumber, const int totalStages) { llvm::errs() << "\n"; diff --git a/mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp b/mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp index 425f2a8c35..7cb25e3bbd 100644 --- a/mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp +++ b/mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp @@ -19,8 +19,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -30,9 +29,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -62,10 +59,10 @@ using namespace qco; * returns its results */ template -static void createModified( - JeffOpType& op, ConversionPatternRewriter& rewriter, ValueRange controls, - ValueRange targets, - llvm::function_ref(ValueRange)> lambda) { +static void +createModified(JeffOpType& op, ConversionPatternRewriter& rewriter, + ValueRange controls, ValueRange targets, + function_ref(ValueRange)> lambda) { auto loc = op.getLoc(); if (op.getNumCtrls() != 0) { CtrlOp ctrlOp; @@ -74,12 +71,12 @@ static void createModified( } else { ctrlOp = CtrlOp::create( rewriter, loc, controls, targets, - [&](ValueRange ctrlTargets) -> llvm::SmallVector { + [&](ValueRange ctrlTargets) -> SmallVector { auto invOp = InvOp::create(rewriter, loc, ctrlTargets, lambda); return invOp.getQubitsOut(); }); } - llvm::SmallVector results; + SmallVector results; llvm::append_range(results, ctrlOp.getTargetsOut()); llvm::append_range(results, ctrlOp.getControlsOut()); rewriter.replaceOp(op, results); @@ -122,7 +119,7 @@ createGateFromJeff(JeffOpType& op, ConversionPatternRewriter& rewriter, return success(); } - auto lambda = [&](ValueRange innerTargets) -> llvm::SmallVector { + auto lambda = [&](ValueRange innerTargets) -> SmallVector { auto qcoOp = QCOOpType::create(rewriter, op.getLoc(), innerTargets[TargetIndices]..., parameters[ParamIndices]...); @@ -166,7 +163,7 @@ static void createBarrierOp(jeff::CustomOp& op, jeff::CustomOpAdaptor& adaptor, if (op.getNumCtrls() == 0 && !op.getIsAdjoint()) { rewriter.replaceOpWithNewOp(op, targets); } else { - auto lambda = [&](ValueRange innerTargets) -> llvm::SmallVector { + auto lambda = [&](ValueRange innerTargets) -> SmallVector { auto qcoOp = BarrierOp::create(rewriter, op.getLoc(), innerTargets); return qcoOp.getQubitsOut(); }; @@ -177,8 +174,8 @@ static void createBarrierOp(jeff::CustomOp& op, jeff::CustomOpAdaptor& adaptor, /** * @brief Gets the name of the entry point from the module attributes */ -static llvm::StringRef getEntryPointName(Operation* op) { - auto module = llvm::dyn_cast(op); +static StringRef getEntryPointName(Operation* op) { + auto module = dyn_cast(op); if (!module) { llvm::reportFatalInternalError("Expected a module operation"); } @@ -188,20 +185,20 @@ static llvm::StringRef getEntryPointName(Operation* op) { llvm::reportFatalInternalError( "Module is missing 'jeff.entrypoint' attribute"); } - auto entryPoint = llvm::cast(entryPointAttr).getUInt(); + auto entryPoint = cast(entryPointAttr).getUInt(); auto stringsAttr = module->getAttr("jeff.strings"); if (!stringsAttr) { llvm::reportFatalInternalError( "Module is missing 'jeff.strings' attribute"); } - auto strings = llvm::cast(stringsAttr); + auto strings = cast(stringsAttr); if (entryPoint >= strings.size()) { llvm::reportFatalInternalError("Entry point index is out of bounds"); } - return llvm::cast(strings[entryPoint]).getValue(); + return cast(strings[entryPoint]).getValue(); } /** @@ -211,7 +208,7 @@ static llvm::StringRef getEntryPointName(Operation* op) { * @return LogicalResult Success or failure of the cleanup */ static LogicalResult cleanUp(Operation* op) { - auto module = llvm::dyn_cast(op); + auto module = dyn_cast(op); if (!module) { return failure(); } @@ -250,8 +247,8 @@ struct ConvertJeffQuregAllocOpToQCO final matchAndRewrite(jeff::QuregAllocOp op, OpAdaptor adaptor, ConversionPatternRewriter& rewriter) const override { auto sizeValue = getConstantIntValue(adaptor.getNumQubits()); - auto tensorType = llvm::cast( - getTypeConverter()->convertType(op.getType())); + auto tensorType = + cast(getTypeConverter()->convertType(op.getType())); Value size; if (sizeValue.has_value()) { size = arith::ConstantOp::create(rewriter, op.getLoc(), @@ -523,7 +520,7 @@ struct ConvertJeffGPhaseOpToQCO final : OpConversionPattern { if (op.getNumCtrls() == 0 && !op.getIsAdjoint()) { rewriter.replaceOpWithNewOp(op, op.getRotation()); } else { - auto lambda = [&](ValueRange /*targets*/) -> llvm::SmallVector { + auto lambda = [&](ValueRange /*targets*/) -> SmallVector { GPhaseOp::create(rewriter, op.getLoc(), op.getRotation()); return {}; }; @@ -849,7 +846,7 @@ struct ConvertJeffMainToQCO final : OpConversionPattern { auto* block = &op.getBlocks().front(); auto* returnOp = block->getTerminator(); - if (!llvm::isa(returnOp)) { + if (!isa(returnOp)) { return failure(); } diff --git a/mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp b/mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp index dedf1f051d..dcb112804a 100644 --- a/mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp +++ b/mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -29,11 +29,10 @@ #include #include #include -#include #include -#include #include #include +#include #include #include #include @@ -73,15 +72,15 @@ struct LoweringState { bool inInvOp = false; CtrlOp ctrlOp; InvOp invOp; - llvm::SmallVector controlsIn; - llvm::SmallVector controlsOut; - llvm::SmallVector targetsIn; - llvm::SmallVector targetsOut; + SmallVector controlsIn; + SmallVector controlsOut; + SmallVector targetsIn; + SmallVector targetsOut; [[nodiscard]] bool inModifier() const { return inCtrlOp || inInvOp; } // Module information - llvm::SmallVector strings; + SmallVector strings; std::string entryPointName; /// The qubit allocation mode used in the module @@ -258,7 +257,7 @@ static void createCustomOp(QCOOpType& op, ConversionPatternRewriter& rewriter, template static void createPPROp(QCOOpType& op, ConversionPatternRewriter& rewriter, LoweringState& state, ValueRange targets, - const llvm::SmallVector& pauliGates) { + const SmallVector& pauliGates) { auto pauliGatesAttr = DenseI32ArrayAttr::get(rewriter.getContext(), pauliGates); @@ -286,7 +285,7 @@ static LogicalResult cleanUp(Operation* op, LoweringState& state) { return failure(); } - auto module = llvm::dyn_cast(op); + auto module = dyn_cast(op); if (!module) { return failure(); } @@ -312,7 +311,7 @@ static LogicalResult cleanUp(Operation* op, LoweringState& state) { module->setAttr("jeff.entrypoint", builder.getIntegerAttr(uint16Type, entryPoint)); - llvm::SmallVector stringRefs; + SmallVector stringRefs; stringRefs.reserve(state.strings.size()); for (const auto& str : state.strings) { stringRefs.emplace_back(str); @@ -1019,7 +1018,7 @@ struct ConvertQCOMainToJeff final : StatefulOpConversionPattern { } if (!llvm::any_of(passthrough, [](Attribute attr) { - const auto strAttr = llvm::dyn_cast(attr); + const auto strAttr = dyn_cast(attr); return strAttr && strAttr.getValue() == "entry_point"; })) { return failure(); @@ -1031,7 +1030,7 @@ struct ConvertQCOMainToJeff final : StatefulOpConversionPattern { auto* block = &op.getBlocks().front(); auto* returnOp = block->getTerminator(); - if (!llvm::isa(returnOp)) { + if (!isa(returnOp)) { return failure(); } @@ -1070,7 +1069,7 @@ class QCOToJeffTypeConverter final : public TypeConverter { }); addConversion([ctx](RankedTensorType type) -> Type { - if (llvm::isa(type.getElementType())) { + if (isa(type.getElementType())) { return jeff::QuregType::get(ctx, type.getShape()[0]); } return type; diff --git a/mlir/lib/Conversion/QCOToQC/QCOToQC.cpp b/mlir/lib/Conversion/QCOToQC/QCOToQC.cpp index 88e5e6dec7..1fd0dc1742 100644 --- a/mlir/lib/Conversion/QCOToQC/QCOToQC.cpp +++ b/mlir/lib/Conversion/QCOToQC/QCOToQC.cpp @@ -18,7 +18,6 @@ #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" #include "mlir/Dialect/QTensor/IR/QTensorOps.h" -#include #include #include #include @@ -30,7 +29,6 @@ #include #include #include -#include #include #include @@ -136,7 +134,7 @@ class QCOToQCTypeConverter final : public TypeConverter { }); addConversion([ctx](RankedTensorType type) -> Type { - if (llvm::isa(type.getElementType())) { + if (isa(type.getElementType())) { return MemRefType::get(type.getShape(), qc::QubitType::get(ctx)); } return type; @@ -168,7 +166,7 @@ struct ConvertQTensorAllocOp final return failure(); } auto qubitType = qc::QubitType::get(op.getContext()); - auto tensorType = llvm::cast(op.getResult().getType()); + auto tensorType = cast(op.getResult().getType()); auto memrefType = MemRefType::get(tensorType.getShape(), qubitType); if (tensorType.hasStaticShape()) { diff --git a/mlir/lib/Conversion/QCToQCO/QCToQCO.cpp b/mlir/lib/Conversion/QCToQCO/QCToQCO.cpp index 138005075d..fbfea89794 100644 --- a/mlir/lib/Conversion/QCToQCO/QCToQCO.cpp +++ b/mlir/lib/Conversion/QCToQCO/QCToQCO.cpp @@ -19,10 +19,8 @@ #include "mlir/Dialect/QTensor/IR/QTensorOps.h" #include -#include #include #include -#include #include #include #include @@ -106,7 +104,7 @@ struct LoweringState { /// Latest QCO SSA values for QC qubits that are remapped inside the /// modifier region. - llvm::DenseMap currentQubits; + DenseMap currentQubits; }; /// Per-region map from original QC qubit reference to its latest QCO SSA @@ -114,14 +112,14 @@ struct LoweringState { /// /// @details Keys are `Operation::getParentRegion()` for ops being converted /// (typically a `func.func` body or a modifier region). - llvm::DenseMap> qubitMap; + DenseMap> qubitMap; /// Per-region map from original QC register to its latest QTensor SSA value - llvm::DenseMap> tensorMap; + DenseMap> tensorMap; /// Per-region map from original QC qubit reference to its register /// information - llvm::DenseMap> qubitInfoMap; + DenseMap> qubitInfoMap; /// Stack of active modifier regions SmallVector modifierFrames; @@ -192,8 +190,8 @@ currentModifierFrame(LoweringState& state) { * returns the pair containing the map and a mutable reference to the value in * the map. */ -[[nodiscard]] static std::pair*, Value*> -findRegionLocalMap(llvm::DenseMap>& map, +[[nodiscard]] static std::pair*, Value*> +findRegionLocalMap(DenseMap>& map, Operation* anchor, Value reference) { for (auto* current = anchor->getParentRegion(); current != nullptr; current = current->getParentRegion()) { @@ -355,9 +353,9 @@ struct ConvertFuncReturnOp final : StatefulOpConversionPattern { // Build return values from qubitMap and collect live qubit information. // A qubit from the current scope is considered alive if it is returned from // the function. Otherwise, it is considered dead. - llvm::SmallVector returnValues; + SmallVector returnValues; returnValues.reserve(op.getNumOperands()); - llvm::DenseSet liveQubits; + DenseSet liveQubits; for (auto [qcOperand, adaptorOperand] : llvm::zip_equal(op.getOperands(), adaptor.getOperands())) { if (auto it = map.find(qcOperand); it != map.end()) { @@ -425,7 +423,7 @@ struct ConvertMemRefAllocOp final LogicalResult matchAndRewrite(memref::AllocOp op, OpAdaptor adaptor, ConversionPatternRewriter& rewriter) const override { - if (!llvm::isa(op.getType().getElementType())) { + if (!isa(op.getType().getElementType())) { return failure(); } @@ -476,7 +474,7 @@ struct ConvertMemRefLoadOp final : StatefulOpConversionPattern { matchAndRewrite(memref::LoadOp op, OpAdaptor adaptor, ConversionPatternRewriter& rewriter) const override { auto memref = op.getMemref(); - if (!llvm::isa(memref.getType().getElementType())) { + if (!isa(memref.getType().getElementType())) { return failure(); } @@ -538,7 +536,7 @@ struct ConvertMemRefDeallocOp final matchAndRewrite(memref::DeallocOp op, OpAdaptor /*adaptor*/, ConversionPatternRewriter& rewriter) const override { auto memref = op.getMemref(); - if (!llvm::isa(memref.getType().getElementType())) { + if (!isa(memref.getType().getElementType())) { return failure(); } @@ -1020,8 +1018,8 @@ struct QCToQCO final : impl::QCToQCOBase { target.addDynamicallyLegalDialect([](Operation* op) { auto isQubitMemref = [](Type t) { - auto mt = llvm::dyn_cast(t); - return mt && llvm::isa(mt.getElementType()); + auto mt = dyn_cast(t); + return mt && isa(mt.getElementType()); }; return llvm::none_of(op->getOperandTypes(), isQubitMemref) && llvm::none_of(op->getResultTypes(), isQubitMemref); diff --git a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp index ad081810da..98942d998c 100644 --- a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp +++ b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp @@ -16,11 +16,12 @@ #include "mlir/Dialect/QIR/Utils/QIRMetadata.h" #include "mlir/Dialect/QIR/Utils/QIRUtils.h" +#include #include #include #include +#include #include -#include #include #include #include @@ -31,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -86,7 +86,7 @@ struct LoweringState : QIRMetadata { llvm::StringMap resultArrays; /// Map from (register name, index) to loaded result - llvm::DenseMap, Value> loadedResults; + DenseMap, Value> loadedResults; /// Map from index to result pointer for non-register results DenseMap resultPtrs; @@ -339,7 +339,7 @@ struct QCToQIRTypeConverter final : LLVMTypeConverter { [ctx](QubitType /*type*/) { return LLVM::LLVMPointerType::get(ctx); }); addConversion([ctx](MemRefType type) -> Type { - if (llvm::isa(type.getElementType())) { + if (isa(type.getElementType())) { return LLVM::LLVMPointerType::get(ctx); } return type; diff --git a/mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp b/mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp index d179ab0d29..60218eb7b7 100644 --- a/mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp +++ b/mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp @@ -14,9 +14,7 @@ #include "mlir/Dialect/QC/IR/QCOps.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include -#include #include #include #include @@ -29,7 +27,9 @@ #include #include #include +#include +#include #include #include #include @@ -48,7 +48,7 @@ QCProgramBuilder::QCProgramBuilder(MLIRContext* context) void QCProgramBuilder::initialize() { // Set insertion point to the module body - setInsertionPointToStart(module.getBody()); + setInsertionPointToStart(cast(module).getBody()); // Create main function as entry point auto funcType = getFunctionType({}, {getI64Type()}); @@ -68,6 +68,13 @@ Value QCProgramBuilder::intConstant(const int64_t value) { return arith::ConstantOp::create(*this, getI64IntegerAttr(value)).getResult(); } +Value QCProgramBuilder::QubitRegister::operator[](const size_t index) const { + if (index >= qubits.size()) { + llvm::reportFatalUsageError("Qubit index out of bounds"); + } + return qubits[index]; +} + Value QCProgramBuilder::allocQubit() { checkFinalized(); ensureAllocationMode(AllocationMode::Dynamic); @@ -103,7 +110,7 @@ QCProgramBuilder::allocQubitRegister(const int64_t size) { auto memref = memref::AllocOp::create(*this, memrefType); allocatedMemrefs.insert(memref); - llvm::SmallVector qubits; + SmallVector qubits; qubits.reserve(size); for (int64_t i = 0; i < size; ++i) { auto index = arith::ConstantIndexOp::create(*this, i); @@ -115,6 +122,17 @@ QCProgramBuilder::allocQubitRegister(const int64_t size) { return {.value = memref, .qubits = std::move(qubits)}; } +QCProgramBuilder::Bit +QCProgramBuilder::ClassicalRegister::operator[](const int64_t index) const { + if (index < 0 || index >= size) { + const std::string msg = "Bit index " + std::to_string(index) + + " out of bounds for register '" + name + + "' of size " + std::to_string(size); + llvm::reportFatalUsageError(msg.c_str()); + } + return {.registerName = name, .registerSize = size, .registerIndex = index}; +} + QCProgramBuilder::ClassicalRegister QCProgramBuilder::allocClassicalBitRegister(const int64_t size, std::string name) const { @@ -429,16 +447,14 @@ QCProgramBuilder& QCProgramBuilder::barrier(ValueRange qubits) { // Modifiers //===----------------------------------------------------------------------===// -QCProgramBuilder& -QCProgramBuilder::ctrl(ValueRange controls, - const llvm::function_ref& body) { +QCProgramBuilder& QCProgramBuilder::ctrl(ValueRange controls, + const function_ref& body) { checkFinalized(); CtrlOp::create(*this, controls, body); return *this; } -QCProgramBuilder& -QCProgramBuilder::inv(const llvm::function_ref& body) { +QCProgramBuilder& QCProgramBuilder::inv(const function_ref& body) { checkFinalized(); InvOp::create(*this, body); return *this; @@ -451,7 +467,7 @@ QCProgramBuilder::inv(const llvm::function_ref& body) { QCProgramBuilder& QCProgramBuilder::dealloc(Value qubit) { checkFinalized(); - if (llvm::isa_and_nonnull(qubit.getDefiningOp())) { + if (isa_and_nonnull(qubit.getDefiningOp())) { llvm::reportFatalUsageError( "Register-backed qubits cannot be deallocated manually"); } @@ -506,7 +522,7 @@ OwningOpRef QCProgramBuilder::finalize() { // Ensure that main function exists and insertion point is valid auto* insertionBlock = getInsertionBlock(); func::FuncOp mainFunc = nullptr; - for (auto op : module.getOps()) { + for (auto op : cast(module).getOps()) { if (op.getName() == "main") { mainFunc = op; break; @@ -522,7 +538,7 @@ OwningOpRef QCProgramBuilder::finalize() { } for (auto qubit : allocatedQubits) { - if (!llvm::isa(qubit.getDefiningOp())) { + if (!isa(qubit.getDefiningOp())) { DeallocOp::create(*this, qubit); } } @@ -543,12 +559,12 @@ OwningOpRef QCProgramBuilder::finalize() { ctx = nullptr; // Transfer ownership to the caller - return module; + return cast(module); } OwningOpRef QCProgramBuilder::build( MLIRContext* context, - const llvm::function_ref& buildFunc) { + const function_ref& buildFunc) { QCProgramBuilder builder(context); builder.initialize(); buildFunc(builder); diff --git a/mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp b/mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp index 064107de7b..d457fa9a35 100644 --- a/mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp @@ -11,11 +11,8 @@ #include "mlir/Dialect/QC/IR/QCOps.h" #include -#include -#include #include #include -#include #include #include #include @@ -37,7 +34,7 @@ struct MergeNestedCtrl final : OpRewritePattern { LogicalResult matchAndRewrite(CtrlOp op, PatternRewriter& rewriter) const override { auto* bodyUnitary = op.getBodyUnitary().getOperation(); - auto bodyCtrlOp = llvm::dyn_cast(bodyUnitary); + auto bodyCtrlOp = dyn_cast(bodyUnitary); if (!bodyCtrlOp) { return failure(); } @@ -68,14 +65,14 @@ struct ReduceCtrl final : OpRewritePattern { PatternRewriter& rewriter) const override { auto* bodyUnitary = op.getBodyUnitary().getOperation(); // Inline ops from empty control modifiers, IdOp and BarrierOp - if (op.getNumControls() == 0 || llvm::isa(bodyUnitary)) { + if (op.getNumControls() == 0 || isa(bodyUnitary)) { rewriter.moveOpBefore(bodyUnitary, op); rewriter.replaceOp(op, bodyUnitary->getResults()); return success(); } // The remaining code explicitly handles GPhaseOp and nothing else - auto gPhaseOp = llvm::dyn_cast(bodyUnitary); + auto gPhaseOp = dyn_cast(bodyUnitary); if (!gPhaseOp) { return failure(); } @@ -110,7 +107,7 @@ UnitaryOpInterface CtrlOp::getBodyUnitary() { // also contain constants and arithmetic operations, e.g., created as part of // canonicalization. Thus, the only safe way to access the unitary operation // is to get the second operation from the back of the region. - return llvm::cast(*(++getBody()->rbegin())); + return cast(*(++getBody()->rbegin())); } Value CtrlOp::getQubit(const size_t i) { @@ -133,7 +130,7 @@ Value CtrlOp::getControl(const size_t i) { void CtrlOp::build(OpBuilder& odsBuilder, OperationState& odsState, ValueRange controls, - const llvm::function_ref& bodyBuilder) { + const function_ref& bodyBuilder) { const OpBuilder::InsertionGuard guard(odsBuilder); odsState.addOperands(controls); auto* region = odsState.addRegion(); @@ -149,22 +146,22 @@ LogicalResult CtrlOp::verify() { if (block.getOperations().size() < 2) { return emitOpError("body region must have at least two operations"); } - if (!llvm::isa(block.back())) { + if (!isa(block.back())) { return emitOpError( "last operation in body region must be a yield operation"); } auto iter = ++block.rbegin(); - if (!llvm::isa(*iter)) { + if (!isa(*iter)) { return emitOpError( "second to last operation in body region must be a unitary operation"); } for (auto it = ++iter; it != block.rend(); ++it) { - if (llvm::isa(*it)) { + if (isa(*it)) { return emitOpError("body region may only contain a single unitary op"); } } - llvm::SmallPtrSet uniqueQubits; + SmallPtrSet uniqueQubits; for (const auto& control : getControls()) { if (!uniqueQubits.insert(control).second) { return emitOpError("duplicate control qubit found"); diff --git a/mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp b/mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp index 10f3c1d9df..065fe431be 100644 --- a/mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp @@ -10,9 +10,7 @@ #include "mlir/Dialect/QC/IR/QCOps.h" -#include #include -#include #include #include #include @@ -20,7 +18,6 @@ #include #include #include -#include #include @@ -39,7 +36,7 @@ struct MoveCtrlOutside final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp invOp, PatternRewriter& rewriter) const override { auto bodyUnitary = invOp.getBodyUnitary(); - auto innerCtrlOp = llvm::dyn_cast(bodyUnitary.getOperation()); + auto innerCtrlOp = dyn_cast(bodyUnitary.getOperation()); if (!innerCtrlOp) { return failure(); } @@ -67,8 +64,7 @@ struct InlineSelfAdjoint final : OpRewritePattern { PatternRewriter& rewriter) const override { auto* innerOp = op.getBodyUnitary().getOperation(); - if (!llvm::isa( - innerOp)) { + if (!isa(innerOp)) { return failure(); } @@ -91,7 +87,7 @@ struct ReplaceWithKnownGates final : OpRewritePattern { PatternRewriter& rewriter) const override { auto* innerOp = op.getBodyUnitary().getOperation(); - return llvm::TypeSwitch(innerOp) + return TypeSwitch(innerOp) .Case([&](auto g) { Value negTheta = arith::NegFOp::create(rewriter, op.getLoc(), g.getTheta()); @@ -240,7 +236,7 @@ struct CancelNestedInv final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp invOp, PatternRewriter& rewriter) const override { auto innerUnitary = invOp.getBodyUnitary(); - auto innerInvOp = llvm::dyn_cast(innerUnitary.getOperation()); + auto innerInvOp = dyn_cast(innerUnitary.getOperation()); if (!innerInvOp) { return failure(); } @@ -261,11 +257,11 @@ UnitaryOpInterface InvOp::getBodyUnitary() { // also contain constants and arithmetic operations, e.g., created as part of // canonicalization. Thus, the only safe way to access the unitary operation // is to get the second operation from the back of the region. - return llvm::cast(*(++getBody()->rbegin())); + return cast(*(++getBody()->rbegin())); } void InvOp::build(OpBuilder& odsBuilder, OperationState& odsState, - const llvm::function_ref& bodyBuilder) { + const function_ref& bodyBuilder) { const OpBuilder::InsertionGuard guard(odsBuilder); auto* region = odsState.addRegion(); auto& block = region->emplaceBlock(); @@ -280,17 +276,17 @@ LogicalResult InvOp::verify() { if (block.getOperations().size() < 2) { return emitOpError("body region must have at least two operations"); } - if (!llvm::isa(block.back())) { + if (!isa(block.back())) { return emitOpError( "last operation in body region must be a yield operation"); } auto iter = ++block.rbegin(); - if (!llvm::isa(*iter)) { + if (!isa(*iter)) { return emitOpError( "second to last operation in body region must be a unitary operation"); } for (auto it = ++iter; it != block.rend(); ++it) { - if (llvm::isa(*it)) { + if (isa(*it)) { return emitOpError("body region may only contain a single unitary op"); } } diff --git a/mlir/lib/Dialect/QC/Transforms/ShrinkQubitRegisters.cpp b/mlir/lib/Dialect/QC/Transforms/ShrinkQubitRegisters.cpp index d81a666c5e..583f5ff135 100644 --- a/mlir/lib/Dialect/QC/Transforms/ShrinkQubitRegisters.cpp +++ b/mlir/lib/Dialect/QC/Transforms/ShrinkQubitRegisters.cpp @@ -11,16 +11,14 @@ #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/Transforms/Passes.h" -#include #include #include -#include #include #include #include #include #include -#include +#include #include #include @@ -59,12 +57,12 @@ struct ShrinkQubitRegister final : OpRewritePattern { return failure(); } - auto memRefType = llvm::dyn_cast(op.getMemref().getType()); + auto memRefType = dyn_cast(op.getMemref().getType()); if (!memRefType || memRefType.getRank() != 1 || !memRefType.hasStaticShape()) { return failure(); } - if (!llvm::isa(memRefType.getElementType())) { + if (!isa(memRefType.getElementType())) { return failure(); } if (!memRefType.getLayout().isIdentity()) { @@ -74,15 +72,15 @@ struct ShrinkQubitRegister final : OpRewritePattern { return failure(); } - llvm::SmallVector loadOps; - llvm::SmallVector liveIndices; - llvm::DenseMap newIndexByOldIndex; + SmallVector loadOps; + SmallVector liveIndices; + DenseMap newIndexByOldIndex; for (auto* user : op.getMemref().getUsers()) { if (user == op.getOperation()) { continue; } - auto loadOp = llvm::dyn_cast(user); + auto loadOp = dyn_cast(user); if (!loadOp) { return failure(); } diff --git a/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp b/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp index a8f3912b8b..d3fb4dc99d 100644 --- a/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp +++ b/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp @@ -22,12 +22,10 @@ #include #include #include -#include #include #include #include #include -#include #include #include @@ -53,8 +51,8 @@ struct QregInfo { SmallVector qubits; }; -using BitMemInfo = std::pair; // (register ref, localIdx) +// (register ref, localIdx) +using BitMemInfo = std::pair; using BitIndexVec = SmallVector; } // namespace diff --git a/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp b/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp index becdc42b31..b2be9d8f10 100644 --- a/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp +++ b/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp @@ -17,11 +17,9 @@ #include "mlir/Dialect/Utils/Utils.h" #include -#include #include #include #include -#include #include #include #include @@ -34,7 +32,9 @@ #include #include #include +#include +#include #include #include #include @@ -53,7 +53,7 @@ QCOProgramBuilder::QCOProgramBuilder(MLIRContext* context) void QCOProgramBuilder::initialize() { // Set insertion point to the module body - setInsertionPointToStart(module.getBody()); + setInsertionPointToStart(mlir::cast(module).getBody()); // Create main function as entry point auto funcType = getFunctionType({}, {getI64Type()}); @@ -73,6 +73,13 @@ Value QCOProgramBuilder::intConstant(const int64_t value) { return arith::ConstantOp::create(*this, getI64IntegerAttr(value)).getResult(); } +Value& QCOProgramBuilder::QubitRegister::operator[](const size_t index) { + if (index >= qubits.size()) { + llvm::reportFatalUsageError("Qubit index out of bounds"); + } + return qubits[index]; +} + Value QCOProgramBuilder::allocQubit() { checkFinalized(); ensureAllocationMode(AllocationMode::Dynamic); @@ -109,7 +116,7 @@ QCOProgramBuilder::allocQubitRegister(const int64_t size) { auto qtensor = qtensorAlloc(size); - llvm::SmallVector qubits; + SmallVector qubits; qubits.reserve(size); for (int64_t i = 0; i < size; ++i) { auto [qtensorOut, qubit] = qtensorExtract(qtensor, i); @@ -120,6 +127,17 @@ QCOProgramBuilder::allocQubitRegister(const int64_t size) { return {.value = qtensor, .qubits = std::move(qubits)}; } +QCOProgramBuilder::Bit +QCOProgramBuilder::ClassicalRegister::operator[](const int64_t index) const { + if (index < 0 || index >= size) { + const std::string msg = "Bit index " + std::to_string(index) + + " out of bounds for register '" + name + + "' of size " + std::to_string(size); + llvm::reportFatalUsageError(msg.c_str()); + } + return {.registerName = name, .registerSize = size, .registerIndex = index}; +} + QCOProgramBuilder::ClassicalRegister QCOProgramBuilder::allocClassicalBitRegister(const int64_t size, std::string name) const { @@ -170,11 +188,11 @@ void QCOProgramBuilder::validateTensorValue(Value tensor) const { "Invalid tensor value used (either consumed or not tracked)"); } - auto tensorType = llvm::dyn_cast(tensor.getType()); + auto tensorType = dyn_cast(tensor.getType()); if (!tensorType || tensorType.getRank() != 1) { llvm::reportFatalUsageError("Tensor must be of 1-D RankedTensorType!"); } - if (!llvm::isa(tensorType.getElementType())) { + if (!isa(tensorType.getElementType())) { llvm::reportFatalUsageError("Elements must be of QubitType!"); } } @@ -220,7 +238,7 @@ Value QCOProgramBuilder::qtensorFromElements(ValueRange elements) { } for (auto element : elements) { - if (!llvm::isa(element.getType())) { + if (!isa(element.getType())) { llvm::reportFatalUsageError("Elements must be QubitType!"); } validateQubitValue(element); @@ -339,12 +357,10 @@ Value QCOProgramBuilder::reset(Value qubit) { checkFinalized(); \ auto param = variantToValue(*this, getLoc(), PARAM); \ const auto controlsOut = \ - ctrl(control, {}, \ - [&](ValueRange /*targets*/) -> llvm::SmallVector { \ - OP_NAME(param); \ - return {}; \ - }) \ - .first; \ + ctrl(control, {}, [&](ValueRange /*targets*/) -> SmallVector { \ + OP_NAME(param); \ + return {}; \ + }).first; \ return controlsOut[0]; \ } \ ValueRange QCOProgramBuilder::mc##OP_NAME( \ @@ -352,12 +368,10 @@ Value QCOProgramBuilder::reset(Value qubit) { checkFinalized(); \ auto param = variantToValue(*this, getLoc(), PARAM); \ const auto controlsOut = \ - ctrl(controls, {}, \ - [&](ValueRange /*targets*/) -> llvm::SmallVector { \ - OP_NAME(param); \ - return {}; \ - }) \ - .first; \ + ctrl(controls, {}, [&](ValueRange /*targets*/) -> SmallVector { \ + OP_NAME(param); \ + return {}; \ + }).first; \ return controlsOut; \ } @@ -378,8 +392,8 @@ DEFINE_ZERO_TARGET_ONE_PARAMETER(GPhaseOp, gphase, theta) std::pair QCOProgramBuilder::c##OP_NAME(Value control, \ Value target) { \ checkFinalized(); \ - const auto [controlsOut, targetsOut] = ctrl( \ - control, target, [&](ValueRange targets) -> llvm::SmallVector { \ + const auto [controlsOut, targetsOut] = \ + ctrl(control, target, [&](ValueRange targets) -> SmallVector { \ return {OP_NAME(targets[0])}; \ }); \ return {controlsOut[0], targetsOut[0]}; \ @@ -388,10 +402,9 @@ DEFINE_ZERO_TARGET_ONE_PARAMETER(GPhaseOp, gphase, theta) ValueRange controls, Value target) { \ checkFinalized(); \ const auto [controlsOut, targetsOut] = \ - ctrl(controls, target, \ - [&](ValueRange targets) -> llvm::SmallVector { \ - return {OP_NAME(targets[0])}; \ - }); \ + ctrl(controls, target, [&](ValueRange targets) -> SmallVector { \ + return {OP_NAME(targets[0])}; \ + }); \ return {controlsOut, targetsOut[0]}; \ } @@ -425,8 +438,8 @@ DEFINE_ONE_TARGET_ZERO_PARAMETER(SXdgOp, sxdg) Value target) { \ checkFinalized(); \ auto param = variantToValue(*this, getLoc(), PARAM); \ - const auto [controlsOut, targetsOut] = ctrl( \ - control, target, [&](ValueRange targets) -> llvm::SmallVector { \ + const auto [controlsOut, targetsOut] = \ + ctrl(control, target, [&](ValueRange targets) -> SmallVector { \ return {OP_NAME(param, targets[0])}; \ }); \ return {controlsOut[0], targetsOut[0]}; \ @@ -437,10 +450,9 @@ DEFINE_ONE_TARGET_ZERO_PARAMETER(SXdgOp, sxdg) checkFinalized(); \ auto param = variantToValue(*this, getLoc(), PARAM); \ const auto [controlsOut, targetsOut] = \ - ctrl(controls, target, \ - [&](ValueRange targets) -> llvm::SmallVector { \ - return {OP_NAME(param, targets[0])}; \ - }); \ + ctrl(controls, target, [&](ValueRange targets) -> SmallVector { \ + return {OP_NAME(param, targets[0])}; \ + }); \ return {controlsOut, targetsOut[0]}; \ } @@ -470,8 +482,8 @@ DEFINE_ONE_TARGET_ONE_PARAMETER(POp, p, phi) checkFinalized(); \ auto param1 = variantToValue(*this, getLoc(), PARAM1); \ auto param2 = variantToValue(*this, getLoc(), PARAM2); \ - const auto [controlsOut, targetsOut] = ctrl( \ - control, target, [&](ValueRange targets) -> llvm::SmallVector { \ + const auto [controlsOut, targetsOut] = \ + ctrl(control, target, [&](ValueRange targets) -> SmallVector { \ return {OP_NAME(param1, param2, targets[0])}; \ }); \ return {controlsOut[0], targetsOut[0]}; \ @@ -484,10 +496,9 @@ DEFINE_ONE_TARGET_ONE_PARAMETER(POp, p, phi) auto param1 = variantToValue(*this, getLoc(), PARAM1); \ auto param2 = variantToValue(*this, getLoc(), PARAM2); \ const auto [controlsOut, targetsOut] = \ - ctrl(controls, target, \ - [&](ValueRange targets) -> llvm::SmallVector { \ - return {OP_NAME(param1, param2, targets[0])}; \ - }); \ + ctrl(controls, target, [&](ValueRange targets) -> SmallVector { \ + return {OP_NAME(param1, param2, targets[0])}; \ + }); \ return {controlsOut, targetsOut[0]}; \ } @@ -519,8 +530,8 @@ DEFINE_ONE_TARGET_TWO_PARAMETER(U2Op, u2, phi, lambda) auto param1 = variantToValue(*this, getLoc(), PARAM1); \ auto param2 = variantToValue(*this, getLoc(), PARAM2); \ auto param3 = variantToValue(*this, getLoc(), PARAM3); \ - const auto [controlsOut, targetsOut] = ctrl( \ - control, target, [&](ValueRange targets) -> llvm::SmallVector { \ + const auto [controlsOut, targetsOut] = \ + ctrl(control, target, [&](ValueRange targets) -> SmallVector { \ return {OP_NAME(param1, param2, param3, targets[0])}; \ }); \ return {controlsOut[0], targetsOut[0]}; \ @@ -535,10 +546,9 @@ DEFINE_ONE_TARGET_TWO_PARAMETER(U2Op, u2, phi, lambda) auto param2 = variantToValue(*this, getLoc(), PARAM2); \ auto param3 = variantToValue(*this, getLoc(), PARAM3); \ const auto [controlsOut, targetsOut] = \ - ctrl(controls, target, \ - [&](ValueRange targets) -> llvm::SmallVector { \ - return {OP_NAME(param1, param2, param3, targets[0])}; \ - }); \ + ctrl(controls, target, [&](ValueRange targets) -> SmallVector { \ + return {OP_NAME(param1, param2, param3, targets[0])}; \ + }); \ return {controlsOut, targetsOut[0]}; \ } @@ -564,7 +574,7 @@ DEFINE_ONE_TARGET_THREE_PARAMETER(UOp, u, theta, phi, lambda) checkFinalized(); \ const auto [controlsOut, targetsOut] = \ ctrl(control, {qubit0, qubit1}, \ - [&](ValueRange targets) -> llvm::SmallVector { \ + [&](ValueRange targets) -> SmallVector { \ auto [q0, q1] = OP_NAME(targets[0], targets[1]); \ return {q0, q1}; \ }); \ @@ -576,7 +586,7 @@ DEFINE_ONE_TARGET_THREE_PARAMETER(UOp, u, theta, phi, lambda) checkFinalized(); \ const auto [controlsOut, targetsOut] = \ ctrl(controls, {qubit0, qubit1}, \ - [&](ValueRange targets) -> llvm::SmallVector { \ + [&](ValueRange targets) -> SmallVector { \ auto [q0, q1] = OP_NAME(targets[0], targets[1]); \ return {q0, q1}; \ }); \ @@ -610,7 +620,7 @@ DEFINE_TWO_TARGET_ZERO_PARAMETER(ECROp, ecr) auto param = variantToValue(*this, getLoc(), PARAM); \ const auto [controlsOut, targetsOut] = \ ctrl(control, {qubit0, qubit1}, \ - [&](ValueRange targets) -> llvm::SmallVector { \ + [&](ValueRange targets) -> SmallVector { \ auto [q0, q1] = OP_NAME(param, targets[0], targets[1]); \ return {q0, q1}; \ }); \ @@ -624,7 +634,7 @@ DEFINE_TWO_TARGET_ZERO_PARAMETER(ECROp, ecr) auto param = variantToValue(*this, getLoc(), PARAM); \ const auto [controlsOut, targetsOut] = \ ctrl(controls, {qubit0, qubit1}, \ - [&](ValueRange targets) -> llvm::SmallVector { \ + [&](ValueRange targets) -> SmallVector { \ auto [q0, q1] = OP_NAME(param, targets[0], targets[1]); \ return {q0, q1}; \ }); \ @@ -662,7 +672,7 @@ DEFINE_TWO_TARGET_ONE_PARAMETER(RZZOp, rzz, theta) auto param2 = variantToValue(*this, getLoc(), PARAM2); \ const auto [controlsOut, targetsOut] = \ ctrl(control, {qubit0, qubit1}, \ - [&](ValueRange targets) -> llvm::SmallVector { \ + [&](ValueRange targets) -> SmallVector { \ auto [q0, q1] = \ OP_NAME(param1, param2, targets[0], targets[1]); \ return {q0, q1}; \ @@ -679,7 +689,7 @@ DEFINE_TWO_TARGET_ONE_PARAMETER(RZZOp, rzz, theta) auto param2 = variantToValue(*this, getLoc(), PARAM2); \ const auto [controlsOut, targetsOut] = \ ctrl(controls, {qubit0, qubit1}, \ - [&](ValueRange targets) -> llvm::SmallVector { \ + [&](ValueRange targets) -> SmallVector { \ auto [q0, q1] = \ OP_NAME(param1, param2, targets[0], targets[1]); \ return {q0, q1}; \ @@ -709,9 +719,9 @@ ValueRange QCOProgramBuilder::barrier(ValueRange qubits) { // Modifiers //===----------------------------------------------------------------------===// -std::pair QCOProgramBuilder::ctrl( - ValueRange controls, ValueRange targets, - llvm::function_ref(ValueRange)> body) { +std::pair +QCOProgramBuilder::ctrl(ValueRange controls, ValueRange targets, + function_ref(ValueRange)> body) { checkFinalized(); auto ctrlOp = CtrlOp::create(*this, controls, targets); @@ -746,9 +756,9 @@ std::pair QCOProgramBuilder::ctrl( return {controlsOut, targetsOut}; } -ValueRange QCOProgramBuilder::inv( - ValueRange qubits, - llvm::function_ref(ValueRange)> body) { +ValueRange +QCOProgramBuilder::inv(ValueRange qubits, + function_ref(ValueRange)> body) { checkFinalized(); auto invOp = InvOp::create(*this, qubits); @@ -803,8 +813,8 @@ QCOProgramBuilder& QCOProgramBuilder::sink(Value qubit) { ValueRange QCOProgramBuilder::qcoIf( const std::variant& condition, ValueRange qubits, - llvm::function_ref(ValueRange)> thenBody, - llvm::function_ref(ValueRange)> elseBody) { + function_ref(ValueRange)> thenBody, + function_ref(ValueRange)> elseBody) { checkFinalized(); auto conditionValue = variantToValue(*this, getLoc(), condition); @@ -828,7 +838,7 @@ ValueRange QCOProgramBuilder::qcoIf( const auto thenResult = thenBody(thenBlock.getArguments()); YieldOp::create(*this, thenResult); setInsertionPointToStart(&elseBlock); - llvm::SmallVector elseResult; + SmallVector elseResult; if (elseBody) { elseResult = elseBody(elseBlock.getArguments()); YieldOp::create(*this, elseResult); @@ -902,7 +912,7 @@ OwningOpRef QCOProgramBuilder::finalize() { // Ensure that main function exists and insertion point is valid auto* insertionBlock = getInsertionBlock(); func::FuncOp mainFunc = nullptr; - for (auto op : module.getOps()) { + for (auto op : cast(module).getOps()) { if (op.getName() == "main") { mainFunc = op; break; @@ -917,13 +927,12 @@ OwningOpRef QCOProgramBuilder::finalize() { "Insertion point is not in entry block of main function"); } - llvm::DenseSet validTensorIds; + DenseSet validTensorIds; for (const auto& [tensor, info] : validTensors) { validTensorIds.insert(info.regId); } - llvm::DenseMap>> - qubitsByRegister; + DenseMap>> qubitsByRegister; for (auto [qubit, info] : validQubits) { if (info.regId == -1 || !validTensorIds.contains(info.regId)) { // Automatically deallocate all still-allocated qubits @@ -958,12 +967,12 @@ OwningOpRef QCOProgramBuilder::finalize() { // Invalidate context to prevent use-after-finalize ctx = nullptr; - return module; + return cast(module); } OwningOpRef QCOProgramBuilder::build( MLIRContext* context, - const llvm::function_ref& buildFunc) { + const function_ref& buildFunc) { QCOProgramBuilder builder(context); builder.initialize(); buildFunc(builder); diff --git a/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp b/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp index 96c811eed8..25fc88d084 100644 --- a/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -24,7 +23,6 @@ #include #include #include -#include #include #include @@ -51,8 +49,7 @@ struct MergeNestedCtrl final : OpRewritePattern { return failure(); } - auto bodyCtrlOp = - llvm::dyn_cast(op.getBodyUnitary().getOperation()); + auto bodyCtrlOp = dyn_cast(op.getBodyUnitary().getOperation()); if (!bodyCtrlOp) { return failure(); } @@ -66,7 +63,7 @@ struct MergeNestedCtrl final : OpRewritePattern { rewriter.replaceOpWithNewOp( op, newControls, newTargets, - [&](ValueRange newTargetArgs) -> llvm::SmallVector { + [&](ValueRange newTargetArgs) -> SmallVector { IRMapping mapping; auto* innerBody = bodyCtrlOp.getBody(); for (size_t i = 0; i < bodyCtrlOp.getNumTargets(); ++i) { @@ -92,7 +89,7 @@ struct ReduceCtrl final : OpRewritePattern { PatternRewriter& rewriter) const override { auto* bodyUnitary = op.getBodyUnitary().getOperation(); // Inline ops from empty control modifiers, IdOp and BarrierOp - if (op.getNumControls() == 0 || llvm::isa(bodyUnitary)) { + if (op.getNumControls() == 0 || isa(bodyUnitary)) { rewriter.moveOpBefore(bodyUnitary, op); bodyUnitary->setOperands(0, op.getNumTargets(), op.getTargetsIn()); rewriter.replaceAllUsesWith(op.getControlsOut(), op.getControlsIn()); @@ -103,7 +100,7 @@ struct ReduceCtrl final : OpRewritePattern { } // The remaining code explicitly handles GPhaseOp and nothing else - auto gPhaseOp = llvm::dyn_cast(bodyUnitary); + auto gPhaseOp = dyn_cast(bodyUnitary); if (!gPhaseOp) { return failure(); } @@ -136,7 +133,7 @@ struct ReduceCtrl final : OpRewritePattern { POp::create(rewriter, gPhaseOp.getLoc(), arg, gPhaseOp.getTheta()); // Add the results of the POp to the yield operation - auto yieldOp = llvm::cast(op.getBody()->back()); + auto yieldOp = cast(op.getBody()->back()); yieldOp->setOperands(pOp->getResults()); // erase the GPhaseOp @@ -154,7 +151,7 @@ UnitaryOpInterface CtrlOp::getBodyUnitary() { // also contain constants and arithmetic operations, e.g., created as part of // canonicalization. Thus, the only safe way to access the unitary operation // is to get the second operation from the back of the region. - return llvm::cast(*(++getBody()->rbegin())); + return cast(*(++getBody()->rbegin())); } Value CtrlOp::getInputQubit(const size_t i) { @@ -235,10 +232,9 @@ Value CtrlOp::getOutputForInput(Value input) { llvm::reportFatalUsageError("Given qubit is not an input of the operation"); } -void CtrlOp::build( - OpBuilder& odsBuilder, OperationState& odsState, ValueRange controls, - ValueRange targets, - llvm::function_ref(ValueRange)> bodyBuilder) { +void CtrlOp::build(OpBuilder& odsBuilder, OperationState& odsState, + ValueRange controls, ValueRange targets, + function_ref(ValueRange)> bodyBuilder) { build(odsBuilder, odsState, controls, targets); auto& block = odsState.regions.front()->emplaceBlock(); @@ -270,7 +266,7 @@ LogicalResult CtrlOp::verify() { << i << " does not match target type"; } } - if (!llvm::isa(block.back())) { + if (!isa(block.back())) { return emitOpError( "last operation in body region must be a yield operation"); } @@ -280,12 +276,12 @@ LogicalResult CtrlOp::verify() { << numTargets << " values, but found " << numYieldOperands; } auto iter = ++block.rbegin(); - if (!llvm::isa(*iter)) { + if (!isa(*iter)) { return emitOpError( "second to last operation in body region must be a unitary operation"); } for (auto it = ++iter; it != block.rend(); ++it) { - if (llvm::isa(*it)) { + if (isa(*it)) { return emitOpError("body region may only contain a single unitary op"); } } diff --git a/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp b/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp index 18cc331c5b..d82a64f819 100644 --- a/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp @@ -13,9 +13,7 @@ #include #include -#include #include -#include #include #include #include @@ -25,7 +23,6 @@ #include #include #include -#include #include #include @@ -46,7 +43,7 @@ struct MoveCtrlOutside final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp invOp, PatternRewriter& rewriter) const override { auto bodyUnitary = invOp.getBodyUnitary(); - auto innerCtrlOp = llvm::dyn_cast(bodyUnitary.getOperation()); + auto innerCtrlOp = dyn_cast(bodyUnitary.getOperation()); if (!innerCtrlOp) { return failure(); } @@ -59,10 +56,10 @@ struct MoveCtrlOutside final : OpRewritePattern { rewriter.replaceOpWithNewOp( invOp, controls, targets, - [&](ValueRange newTargetArgs) -> llvm::SmallVector { + [&](ValueRange newTargetArgs) -> SmallVector { return InvOp::create( rewriter, invOp.getLoc(), newTargetArgs, - [&](ValueRange invArgs) -> llvm::SmallVector { + [&](ValueRange invArgs) -> SmallVector { IRMapping mapping; auto* innerBody = innerCtrlOp.getBody(); for (size_t i = 0; i < innerCtrlOp.getNumTargets(); @@ -93,8 +90,7 @@ struct InlineSelfAdjoint final : OpRewritePattern { PatternRewriter& rewriter) const override { auto* innerOp = op.getBodyUnitary().getOperation(); - if (!llvm::isa( - innerOp)) { + if (!isa(innerOp)) { return failure(); } @@ -118,7 +114,7 @@ struct ReplaceWithKnownGates final : OpRewritePattern { PatternRewriter& rewriter) const override { auto* innerOp = op.getBodyUnitary().getOperation(); - return llvm::TypeSwitch(innerOp) + return TypeSwitch(innerOp) .Case([&](auto g) { Value negTheta = arith::NegFOp::create(rewriter, op.getLoc(), g.getTheta()); @@ -263,7 +259,7 @@ struct CancelNestedInv final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp op, PatternRewriter& rewriter) const override { auto* innerUnitary = op.getBodyUnitary().getOperation(); - auto innerInvOp = llvm::dyn_cast(innerUnitary); + auto innerInvOp = dyn_cast(innerUnitary); if (!innerInvOp) { return failure(); } @@ -285,7 +281,7 @@ UnitaryOpInterface InvOp::getBodyUnitary() { // also contain constants and arithmetic operations, e.g., created as part of // canonicalization. Thus, the only safe way to access the unitary operation // is to get the second operation from the back of the region. - return llvm::cast(*(++getBody()->rbegin())); + return cast(*(++getBody()->rbegin())); } Value InvOp::getInputQubit(const size_t i) { @@ -320,9 +316,9 @@ Value InvOp::getOutputForInput(Value input) { llvm::reportFatalUsageError("Given qubit is not an input of the operation"); } -void InvOp::build( - OpBuilder& odsBuilder, OperationState& odsState, ValueRange qubits, - llvm::function_ref(ValueRange)> bodyBuilder) { +void InvOp::build(OpBuilder& odsBuilder, OperationState& odsState, + ValueRange qubits, + function_ref(ValueRange)> bodyBuilder) { build(odsBuilder, odsState, qubits); auto& block = odsState.regions.front()->emplaceBlock(); @@ -354,7 +350,7 @@ LogicalResult InvOp::verify() { << i << " does not match target type"; } } - if (!llvm::isa(block.back())) { + if (!isa(block.back())) { return emitOpError( "last operation in body region must be a yield operation"); } @@ -364,12 +360,12 @@ LogicalResult InvOp::verify() { << numTargets << " values, but found " << numYieldOperands; } auto iter = ++block.rbegin(); - if (!llvm::isa(*iter)) { + if (!isa(*iter)) { return emitOpError( "second to last operation in body region must be a unitary operation"); } for (auto it = ++iter; it != block.rend(); ++it) { - if (llvm::isa(*it)) { + if (isa(*it)) { return emitOpError("body region may only contain a single unitary op"); } } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/ResetOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/ResetOp.cpp index 3db78fbd8b..8832162354 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/ResetOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/ResetOp.cpp @@ -12,13 +12,12 @@ #include "mlir/Dialect/QTensor/IR/QTensorOps.h" #include "mlir/Dialect/QTensor/IR/QTensorUtils.h" -#include #include #include #include #include #include -#include +#include using namespace mlir; using namespace mlir::qco; @@ -40,11 +39,11 @@ static bool originatesFromQTensorAlloc(qtensor::ExtractOp extractOp) { } while (auto* definingOp = current.getDefiningOp()) { - if (llvm::isa(definingOp)) { + if (isa(definingOp)) { return true; } - if (auto nestedExtractOp = llvm::dyn_cast(definingOp)) { + if (auto nestedExtractOp = dyn_cast(definingOp)) { auto nestedExtractIndex = nestedExtractOp.getIndex(); if (!getConstantIntValue(nestedExtractIndex)) { return false; @@ -56,7 +55,7 @@ static bool originatesFromQTensorAlloc(qtensor::ExtractOp extractOp) { continue; } - if (auto insertOp = llvm::dyn_cast(definingOp)) { + if (auto insertOp = dyn_cast(definingOp)) { auto insertIndex = insertOp.getIndex(); if (!getConstantIntValue(insertIndex)) { return false; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/BarrierOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/BarrierOp.cpp index d168624987..0bd50dd3a3 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/BarrierOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/BarrierOp.cpp @@ -10,15 +10,15 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include #include -#include +#include #include #include #include #include #include #include -#include #include @@ -44,7 +44,7 @@ struct MergeSubsequentBarrier final : OpRewritePattern { SmallVector indicesToFill; for (size_t i = 0; i < qubitsIn.size(); ++i) { - if (llvm::isa( + if (isa( *op.getOutputForInput(qubitsIn[i]).getUsers().begin())) { anythingToMerge = true; newQubitsOutMap[i] = qubitsIn[i]; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp index ca26cf0c78..02e3c5d421 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp index 6b9d540062..2996fdd289 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp index be40c3de9a..9f3ecedfe6 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp index 5fb05c3379..8a6793475b 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp index 79dc169ae1..6bd93add60 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp @@ -12,15 +12,12 @@ #include "mlir/Dialect/Utils/Utils.h" #include -#include #include #include -#include #include #include #include #include -#include #include #include @@ -44,8 +41,7 @@ struct MergeSubsequentXXMinusYY final : OpRewritePattern { LogicalResult matchAndRewrite(XXMinusYYOp op, PatternRewriter& rewriter) const override { // Check if the successor is the same operation - auto nextOp = - llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp index 03d8853371..63648cdcf3 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp @@ -12,14 +12,12 @@ #include "mlir/Dialect/Utils/Utils.h" #include -#include #include #include #include #include #include #include -#include #include #include @@ -43,8 +41,7 @@ struct MergeSubsequentXXPlusYY final : OpRewritePattern { LogicalResult matchAndRewrite(XXPlusYYOp op, PatternRewriter& rewriter) const override { // Check if the successor is the same operation - auto nextOp = - llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } diff --git a/mlir/lib/Dialect/QCO/IR/QCOOps.cpp b/mlir/lib/Dialect/QCO/IR/QCOOps.cpp index 36aa66ad54..21beaedf96 100644 --- a/mlir/lib/Dialect/QCO/IR/QCOOps.cpp +++ b/mlir/lib/Dialect/QCO/IR/QCOOps.cpp @@ -14,13 +14,13 @@ #include #include +#include #include #include #include #include #include #include -#include // The following headers are needed for some template instantiations. // IWYU pragma: begin_keep @@ -160,7 +160,7 @@ parseIfOpAliasing(OpAsmParser& parser, Region& thenRegion, Region& elseRegion, } // Remove duplicate operands - llvm::DenseSet seen; + DenseSet seen; llvm::erase_if(operands, [&](const auto& op) { return !seen.insert(op.name).second; }); diff --git a/mlir/lib/Dialect/QCO/IR/QubitManagement/SinkOp.cpp b/mlir/lib/Dialect/QCO/IR/QubitManagement/SinkOp.cpp index de6ee6edc7..ec8b33b833 100644 --- a/mlir/lib/Dialect/QCO/IR/QubitManagement/SinkOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/QubitManagement/SinkOp.cpp @@ -10,10 +10,9 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" -#include #include #include -#include +#include using namespace mlir; using namespace mlir::qco; @@ -30,7 +29,7 @@ struct RemoveAllocSinkPair final : OpRewritePattern { LogicalResult matchAndRewrite(SinkOp op, PatternRewriter& rewriter) const override { auto* defOp = op.getQubit().getDefiningOp(); - if (!llvm::isa_and_nonnull(defOp)) { + if (!isa_and_nonnull(defOp)) { return failure(); } diff --git a/mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp b/mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp index 6aec9b9ed7..220c36e7db 100644 --- a/mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -28,18 +27,16 @@ #include #include #include -#include #include using namespace mlir; using namespace mlir::qco; -void IfOp::build( - OpBuilder& odsBuilder, OperationState& odsState, Value condition, - ValueRange qubits, - llvm::function_ref(ValueRange)> thenBuilder, - llvm::function_ref(ValueRange)> elseBuilder) { +void IfOp::build(OpBuilder& odsBuilder, OperationState& odsState, + Value condition, ValueRange qubits, + function_ref(ValueRange)> thenBuilder, + function_ref(ValueRange)> elseBuilder) { // Build the empty operation build(odsBuilder, odsState, qubits.getTypes(), condition, qubits); @@ -49,22 +46,19 @@ void IfOp::build( const OpBuilder::InsertionGuard guard(odsBuilder); // Add the block arguments and insert the yield operation - thenBlock.addArguments( - qubits.getTypes(), - SmallVector(qubits.size(), odsState.location)); + thenBlock.addArguments(qubits.getTypes(), + SmallVector(qubits.size(), odsState.location)); odsBuilder.setInsertionPointToStart(&thenBlock); - qco::YieldOp::create(odsBuilder, odsState.location, - thenBuilder(thenBlock.getArguments())); - elseBlock.addArguments( - qubits.getTypes(), - SmallVector(qubits.size(), odsState.location)); + YieldOp::create(odsBuilder, odsState.location, + thenBuilder(thenBlock.getArguments())); + elseBlock.addArguments(qubits.getTypes(), + SmallVector(qubits.size(), odsState.location)); odsBuilder.setInsertionPointToStart(&elseBlock); if (elseBuilder) { - qco::YieldOp::create(odsBuilder, odsState.location, - elseBuilder(elseBlock.getArguments())); + YieldOp::create(odsBuilder, odsState.location, + elseBuilder(elseBlock.getArguments())); } else { - qco::YieldOp::create(odsBuilder, odsState.location, - elseBlock.getArguments()); + YieldOp::create(odsBuilder, odsState.location, elseBlock.getArguments()); } } @@ -113,7 +107,7 @@ void IfOp::getEntrySuccessorRegions(ArrayRef operands, void IfOp::getRegionInvocationBounds( ArrayRef operands, SmallVectorImpl& invocationBounds) { - if (auto cond = llvm::dyn_cast_or_null(operands[0])) { + if (auto cond = dyn_cast_or_null(operands[0])) { // If the condition is known, then one region is known to be executed once // and the other zero times. invocationBounds.emplace_back(0, cond.getValue() ? 1 : 0); @@ -206,7 +200,7 @@ struct ConditionPropagation : public OpRewritePattern { } bool changed = false; - mlir::Type i1Ty = rewriter.getI1Type(); + Type i1Ty = rewriter.getI1Type(); // These variables serve to prevent creating duplicate constants // and hold constant true or false values. @@ -257,7 +251,7 @@ LogicalResult IfOp::verify() { const auto numOutputQubits = outputQubits.size(); for (auto type : inputQubits.getTypes()) { - if (!llvm::isa(type)) { + if (!isa(type)) { return emitOpError("Inputs must be qubit type!"); } } diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/Architecture.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/Architecture.cpp index 593c2e6501..0f2cb5876f 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/Architecture.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/Architecture.cpp @@ -10,6 +10,8 @@ #include "mlir/Dialect/QCO/Transforms/Mapping/Architecture.h" +#include +#include #include #include @@ -39,7 +41,8 @@ std::size_t Architecture::distanceBetween(std::size_t u, std::size_t v) const { return dist_[u][v]; } -SmallVector Architecture::neighboursOf(std::size_t u) const { +SmallVector +Architecture::neighboursOf(const std::size_t u) const { return neighbours_[u]; } diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp index 8604491630..73993e2d8b 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp @@ -17,6 +17,8 @@ #include "mlir/Dialect/QCO/Utils/Qubits.h" #include "mlir/Dialect/QCO/Utils/WireIterator.h" +#include +#include #include #include #include @@ -46,7 +48,6 @@ #include #include #include -#include #include #include #include diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp index fd4b44f291..f35ce043ad 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp @@ -13,6 +13,7 @@ #include "mlir/Dialect/QCO/Transforms/Passes.h" #include +#include #include #include #include @@ -93,7 +94,7 @@ struct MergeSingleQubitRotationGatesPattern final * RXOp, RYOp, or RZOp. */ static std::optional getRotationAxis(Operation* op) { - return llvm::TypeSwitch>(op) + return TypeSwitch>(op) .Case([](auto) { return RotationAxis::X; }) .Case([](auto) { return RotationAxis::Y; }) .Case([](auto) { return RotationAxis::Z; }) @@ -325,7 +326,7 @@ struct MergeSingleQubitRotationGatesPattern final } // Multi-parameter gates each need their own conversion - return llvm::TypeSwitch(op.getOperation()) + return TypeSwitch(op.getOperation()) .Case( [&](ROp o) { return quaternionFromROp(o, constants, rewriter); }) .Case( @@ -359,7 +360,7 @@ struct MergeSingleQubitRotationGatesPattern final const Constants& constants, Location loc, PatternRewriter& rewriter) { - return llvm::TypeSwitch>(op.getOperation()) + return TypeSwitch>(op.getOperation()) .Case( [&](auto) -> std::optional { return std::nullopt; }) .Case([&](auto) -> std::optional { @@ -411,7 +412,7 @@ struct MergeSingleQubitRotationGatesPattern final */ static SmallVector collectChain(UnitaryOpInterface start) { - SmallVector chain = {start}; + SmallVector chain = {start}; auto current = start; while (true) { auto* userOp = *current->getUsers().begin(); diff --git a/mlir/lib/Dialect/QCO/Utils/WireIterator.cpp b/mlir/lib/Dialect/QCO/Utils/WireIterator.cpp index c9f60b1eb3..0aa61634d3 100644 --- a/mlir/lib/Dialect/QCO/Utils/WireIterator.cpp +++ b/mlir/lib/Dialect/QCO/Utils/WireIterator.cpp @@ -25,7 +25,7 @@ namespace mlir::qco { mlir::Value WireIterator::qubit() const { // A sink/deallocation doesn't have an OpResult. - if (op_ != nullptr && mlir::isa(op_)) { + if (op_ != nullptr && isa(op_)) { return nullptr; } return qubit_; @@ -42,12 +42,12 @@ void WireIterator::forward() { op_ = *(qubit_.getUsers().begin()); // A sink op defines the end of the qubit wire (dynamic and static). - if (mlir::isa(op_)) { + if (isa(op_)) { isSentinel_ = true; return; } - if (!(mlir::isa(op_))) { + if (!(isa(op_))) { // Find the output from the input qubit SSA value. mlir::TypeSwitch(op_) .Case([&](UnitaryOpInterface op) { @@ -71,14 +71,14 @@ void WireIterator::backward() { // For sinks/deallocations, qubit_ is an OpOperand. Hence, only get the // def-op. - if (mlir::isa(op_)) { + if (isa(op_)) { op_ = qubit_.getDefiningOp(); return; } // Allocations or static definitions define the start of the qubit wire. // Consequently, stop and early exit. - if (mlir::isa(op_)) { + if (isa(op_)) { return; } diff --git a/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp b/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp index bca47a0265..0b3434a705 100644 --- a/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp +++ b/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp @@ -12,10 +12,8 @@ #include "mlir/Dialect/QIR/Utils/QIRUtils.h" -#include #include #include -#include #include #include #include @@ -50,7 +48,7 @@ QIRProgramBuilder::QIRProgramBuilder(MLIRContext* context) void QIRProgramBuilder::initialize() { // Set insertion point to the module body - setInsertionPointToStart(module.getBody()); + setInsertionPointToStart(cast(module).getBody()); // Create main function: () -> i64 auto funcType = LLVM::LLVMFunctionType::get(getI64Type(), {}); @@ -198,6 +196,17 @@ SmallVector QIRProgramBuilder::allocQubitRegister(const int64_t size) { return qubits; } +QIRProgramBuilder::Bit +QIRProgramBuilder::ClassicalRegister::operator[](const int64_t index) const { + if (index < 0 || index >= size) { + const std::string msg = "Bit index " + std::to_string(index) + + " out of bounds for register '" + name + + "' of size " + std::to_string(size); + llvm::reportFatalUsageError(msg.c_str()); + } + return {.registerName = name, .registerSize = size, .registerIndex = index}; +} + QIRProgramBuilder::ClassicalRegister QIRProgramBuilder::allocClassicalBitRegister(const int64_t size, const std::string& name) { @@ -733,17 +742,17 @@ OwningOpRef QIRProgramBuilder::finalize() { LLVM::CallOp::create(*this, dec, ValueRange{size, array}); } - auto mainFuncOp = llvm::cast(mainFunc); + auto mainFuncOp = cast(mainFunc); setQIRAttributes(mainFuncOp, metadata_); isFinalized = true; - return module; + return cast(module); } OwningOpRef QIRProgramBuilder::build( MLIRContext* context, - const llvm::function_ref& buildFunc) { + const function_ref& buildFunc) { QIRProgramBuilder builder(context); builder.initialize(); buildFunc(builder); diff --git a/mlir/lib/Dialect/QIR/Transforms/QIRCleanup.cpp b/mlir/lib/Dialect/QIR/Transforms/QIRCleanup.cpp index f1188ce19a..64e3d1dd13 100644 --- a/mlir/lib/Dialect/QIR/Transforms/QIRCleanup.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/QIRCleanup.cpp @@ -12,8 +12,8 @@ #include "mlir/Dialect/QIR/Utils/QIRUtils.h" #include +#include #include -#include #include #include #include @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -33,20 +32,20 @@ namespace mlir::qir { #include "mlir/Dialect/QIR/Transforms/Passes.h.inc" [[nodiscard]] static StringAttr getMetadataKey(const Attribute attr) { - auto pair = llvm::dyn_cast(attr); + auto pair = dyn_cast(attr); if (!pair || pair.size() != 2) { return {}; } - auto key = llvm::dyn_cast(pair[0]); - if (!key || !llvm::isa(pair[1])) { + auto key = dyn_cast(pair[0]); + if (!key || !isa(pair[1])) { return {}; } return key; } -[[nodiscard]] static llvm::StringRef getCalleeName(LLVM::CallOp callOp) { +[[nodiscard]] static StringRef getCalleeName(LLVM::CallOp callOp) { auto calleeAttr = callOp.getCalleeAttr(); - auto flatRef = llvm::dyn_cast_or_null(calleeAttr); + auto flatRef = dyn_cast_or_null(calleeAttr); if (!flatRef) { return {}; } @@ -105,9 +104,9 @@ static void normalizeQIRMetadata(ModuleOp module) { continue; } if (key.getValue() == "required_num_qubits") { - requiredNumQubitsAttr = llvm::cast(attr); + requiredNumQubitsAttr = cast(attr); } else if (key.getValue() == "required_num_results") { - requiredNumResultsAttr = llvm::cast(attr); + requiredNumResultsAttr = cast(attr); } } @@ -166,7 +165,7 @@ struct RemoveDeadQubitArrayPair final : OpRewritePattern { LLVM::CallOp allocCall = nullptr; for (Operation* user : allocaOp.getResult().getUsers()) { - auto callOp = llvm::dyn_cast(user); + auto callOp = dyn_cast(user); if (!callOp) { return failure(); } diff --git a/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp b/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp index b698c06dc7..04be35f3e6 100644 --- a/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp +++ b/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp @@ -13,7 +13,6 @@ #include "mlir/Dialect/QIR/Utils/QIRMetadata.h" #include -#include #include #include #include @@ -63,7 +62,7 @@ void setQIRAttributes(LLVM::LLVMFuncOp& main, const QIRMetadata& metadata) { } OpBuilder builder(main.getBody()); - llvm::SmallVector attributes; + SmallVector attributes; // Core QIR attributes attributes.emplace_back(builder.getStringAttr("entry_point")); diff --git a/mlir/lib/Dialect/QTensor/IR/Operations/AllocOp.cpp b/mlir/lib/Dialect/QTensor/IR/Operations/AllocOp.cpp index 05978b6f36..61e60eccfb 100644 --- a/mlir/lib/Dialect/QTensor/IR/Operations/AllocOp.cpp +++ b/mlir/lib/Dialect/QTensor/IR/Operations/AllocOp.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include diff --git a/mlir/lib/Dialect/QTensor/IR/Operations/ExtractOp.cpp b/mlir/lib/Dialect/QTensor/IR/Operations/ExtractOp.cpp index a899d3ae41..04d6b834c7 100644 --- a/mlir/lib/Dialect/QTensor/IR/Operations/ExtractOp.cpp +++ b/mlir/lib/Dialect/QTensor/IR/Operations/ExtractOp.cpp @@ -14,7 +14,6 @@ #include #include #include -#include using namespace mlir; using namespace mlir::qtensor; diff --git a/mlir/lib/Dialect/QTensor/IR/Operations/InsertOp.cpp b/mlir/lib/Dialect/QTensor/IR/Operations/InsertOp.cpp index adeac1cb8d..9ec4bd5d07 100644 --- a/mlir/lib/Dialect/QTensor/IR/Operations/InsertOp.cpp +++ b/mlir/lib/Dialect/QTensor/IR/Operations/InsertOp.cpp @@ -11,7 +11,6 @@ #include "mlir/Dialect/QTensor/IR/QTensorOps.h" #include "mlir/Dialect/QTensor/IR/QTensorUtils.h" -#include #include #include #include @@ -19,7 +18,6 @@ #include #include #include -#include using namespace mlir; using namespace mlir::qtensor; @@ -69,7 +67,7 @@ static ExtractOp findMatchingExtractInTensorChain(InsertOp insertOp) { } while (auto* definingOp = current.getDefiningOp()) { - if (auto nestedInsertOp = llvm::dyn_cast(definingOp)) { + if (auto nestedInsertOp = dyn_cast(definingOp)) { auto nestedInsertIndex = nestedInsertOp.getIndex(); if (!getConstantIntValue(nestedInsertIndex)) { return nullptr; @@ -81,7 +79,7 @@ static ExtractOp findMatchingExtractInTensorChain(InsertOp insertOp) { current = nestedInsertOp.getDest(); continue; } - if (auto extractOp = llvm::dyn_cast(definingOp)) { + if (auto extractOp = dyn_cast(definingOp)) { auto extractIndex = extractOp.getIndex(); if (!getConstantIntValue(extractIndex)) { return nullptr; diff --git a/mlir/lib/Dialect/QTensor/Transforms/ShrinkRegisters.cpp b/mlir/lib/Dialect/QTensor/Transforms/ShrinkRegisters.cpp index feb64d6e15..33146caed4 100644 --- a/mlir/lib/Dialect/QTensor/Transforms/ShrinkRegisters.cpp +++ b/mlir/lib/Dialect/QTensor/Transforms/ShrinkRegisters.cpp @@ -11,16 +11,14 @@ #include "mlir/Dialect/QTensor/IR/QTensorOps.h" #include "mlir/Dialect/QTensor/Transforms/Passes.h" -#include #include #include -#include #include #include #include #include #include -#include +#include #include #include @@ -45,7 +43,7 @@ namespace mlir::qtensor { * @brief Mark a single live index. */ [[nodiscard]] static LogicalResult markLiveIndex(const int64_t index, - llvm::BitVector& liveIndices) { + BitVector& liveIndices) { if (index < 0 || std::cmp_greater_equal(index, liveIndices.size())) { return failure(); } @@ -58,21 +56,21 @@ namespace mlir::qtensor { */ [[nodiscard]] static LogicalResult remapTensorOperand(Operation* op, Value from, Value to) { - if (auto extractOp = llvm::dyn_cast(op)) { + if (auto extractOp = dyn_cast(op)) { if (extractOp.getTensor() != from) { return failure(); } extractOp->setOperand(0, to); return success(); } - if (auto insertOp = llvm::dyn_cast(op)) { + if (auto insertOp = dyn_cast(op)) { if (insertOp.getDest() != from) { return failure(); } insertOp->setOperand(1, to); return success(); } - if (auto deallocOp = llvm::dyn_cast(op)) { + if (auto deallocOp = dyn_cast(op)) { if (deallocOp.getTensor() != from) { return failure(); } @@ -85,9 +83,8 @@ namespace mlir::qtensor { /** * @brief Walk alloc->dealloc and collect all touched indices. */ -[[nodiscard]] static LogicalResult collectLiveIndices(AllocOp allocOp, - llvm::BitVector& live, - DeallocOp& deallocOp) { +[[nodiscard]] static LogicalResult +collectLiveIndices(AllocOp allocOp, BitVector& live, DeallocOp& deallocOp) { auto tensor = allocOp.getResult(); while (true) { auto* user = getLinearTensorUser(tensor); @@ -95,7 +92,7 @@ namespace mlir::qtensor { return failure(); } - if (auto currentDealloc = llvm::dyn_cast(user)) { + if (auto currentDealloc = dyn_cast(user)) { if (currentDealloc.getTensor() != tensor) { return failure(); } @@ -103,7 +100,7 @@ namespace mlir::qtensor { return success(); } - if (auto extractOp = llvm::dyn_cast(user)) { + if (auto extractOp = dyn_cast(user)) { if (extractOp.getTensor() != tensor) { return failure(); } @@ -115,7 +112,7 @@ namespace mlir::qtensor { continue; } - if (auto insertOp = llvm::dyn_cast(user)) { + if (auto insertOp = dyn_cast(user)) { if (insertOp.getDest() != tensor) { return failure(); } @@ -147,7 +144,7 @@ struct ShrinkStaticQTensor final : OpRewritePattern { return failure(); } - llvm::BitVector live(static_cast(*oldSize), false); + BitVector live(static_cast(*oldSize), false); DeallocOp oldDeallocOp{}; if (failed(collectLiveIndices(allocOp, live, oldDeallocOp))) { return failure(); @@ -157,8 +154,7 @@ struct ShrinkStaticQTensor final : OpRewritePattern { return failure(); } - llvm::SmallVector newIndexByOldIndex(static_cast(*oldSize), - -1); + SmallVector newIndexByOldIndex(static_cast(*oldSize), -1); int64_t newSize = 0; for (int64_t index = 0; index < *oldSize; ++index) { if (live.test(static_cast(index))) { @@ -184,7 +180,7 @@ struct ShrinkStaticQTensor final : OpRewritePattern { return failure(); } - if (auto deallocOp = llvm::dyn_cast(currentOp)) { + if (auto deallocOp = dyn_cast(currentOp)) { if (deallocOp != oldDeallocOp || deallocOp.getTensor() != oldTensor) { return failure(); } @@ -194,7 +190,7 @@ struct ShrinkStaticQTensor final : OpRewritePattern { break; } - if (auto extractOp = llvm::dyn_cast(currentOp)) { + if (auto extractOp = dyn_cast(currentOp)) { if (extractOp.getTensor() != oldTensor) { return failure(); } @@ -230,7 +226,7 @@ struct ShrinkStaticQTensor final : OpRewritePattern { continue; } - if (auto insertOp = llvm::dyn_cast(currentOp)) { + if (auto insertOp = dyn_cast(currentOp)) { if (insertOp.getDest() != oldTensor) { return failure(); } diff --git a/mlir/lib/Support/IRVerification.cpp b/mlir/lib/Support/IRVerification.cpp index db2799a533..eaac426f0a 100644 --- a/mlir/lib/Support/IRVerification.cpp +++ b/mlir/lib/Support/IRVerification.cpp @@ -12,7 +12,6 @@ #include "mlir/Dialect/QTensor/IR/QTensorUtils.h" -#include #include #include #include @@ -21,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -36,6 +34,7 @@ #include #include #include +#include #include #include @@ -119,9 +118,9 @@ struct StructuralOperationKey { }; /// Map to track value equivalence between two modules. -using ValueEquivalenceMap = llvm::DenseMap; +using ValueEquivalenceMap = DenseMap; -using OperationSet = llvm::DenseSet; +using OperationSet = DenseSet; struct InsertWrite { Value scalar; @@ -131,7 +130,7 @@ struct InsertWrite { struct InsertChainSummary { Value baseTensor; Value finalTensor; - llvm::SmallVector writes; + SmallVector writes; }; } // namespace @@ -154,13 +153,13 @@ static bool areIndexValuesEquivalent(Value lhs, Value rhs, } static bool isQTensorInsertOp(Operation* op) { - return llvm::isa(op); + return isa(op); } static bool isCommutableQTensorInsertDependency(Operation* dependent, Operation* dependency) { - auto dependentInsert = llvm::dyn_cast(dependent); - auto dependencyInsert = llvm::dyn_cast(dependency); + auto dependentInsert = dyn_cast(dependent); + auto dependencyInsert = dyn_cast(dependency); if (!dependentInsert || !dependencyInsert) { return false; } @@ -187,17 +186,16 @@ static Value getInsertChainBaseTensor(Value tensor, const OperationSet& group) { return current; } -static bool -summarizeInsertGroup(llvm::ArrayRef ops, - llvm::SmallVectorImpl& chains) { +static bool summarizeInsertGroup(ArrayRef ops, + SmallVectorImpl& chains) { OperationSet groupOps; for (Operation* op : ops) { groupOps.insert(op); } - llvm::DenseSet consumedInsertResults; + DenseSet consumedInsertResults; for (Operation* op : ops) { - auto insertOp = llvm::cast(op); + auto insertOp = cast(op); if (auto definingInsert = insertOp.getDest().getDefiningOp()) { if (groupOps.contains(definingInsert.getOperation())) { @@ -206,9 +204,9 @@ summarizeInsertGroup(llvm::ArrayRef ops, } } - llvm::DenseMap chainByBaseTensor; + DenseMap chainByBaseTensor; for (Operation* op : ops) { - auto insertOp = llvm::cast(op); + auto insertOp = cast(op); const Value baseTensor = getInsertChainBaseTensor(insertOp.getDest(), groupOps); @@ -242,7 +240,7 @@ summarizeInsertGroup(llvm::ArrayRef ops, } // Reordering writes to the same index is not semantics-preserving. - llvm::SmallVector seenIndices; + SmallVector seenIndices; for (const auto& write : chain.writes) { if (llvm::any_of(seenIndices, [&](Value seenIndex) { return qtensor::areEquivalentIndices(seenIndex, write.index); @@ -257,9 +255,9 @@ summarizeInsertGroup(llvm::ArrayRef ops, } static bool areInsertWritesEquivalentRec(const size_t lhsIdx, - llvm::ArrayRef lhsWrites, - llvm::ArrayRef rhsWrites, - llvm::SmallVectorImpl& rhsUsed, + ArrayRef lhsWrites, + ArrayRef rhsWrites, + SmallVectorImpl& rhsUsed, ValueEquivalenceMap& valueMap) { if (lhsIdx == lhsWrites.size()) { return true; @@ -290,13 +288,13 @@ static bool areInsertWritesEquivalentRec(const size_t lhsIdx, return false; } -static bool areInsertWritesEquivalent(llvm::ArrayRef lhsWrites, - llvm::ArrayRef rhsWrites, +static bool areInsertWritesEquivalent(ArrayRef lhsWrites, + ArrayRef rhsWrites, ValueEquivalenceMap& valueMap) { if (lhsWrites.size() != rhsWrites.size()) { return false; } - llvm::SmallVector rhsUsed(rhsWrites.size(), 0); + SmallVector rhsUsed(rhsWrites.size(), 0); return areInsertWritesEquivalentRec(0, lhsWrites, rhsWrites, rhsUsed, valueMap); } @@ -322,10 +320,11 @@ static bool areInsertChainsEquivalent(const InsertChainSummary& lhsChain, return true; } -static bool areInsertGroupsEquivalentRec( - const size_t lhsChainIdx, llvm::ArrayRef lhsChains, - llvm::ArrayRef rhsChains, - llvm::SmallVectorImpl& rhsChainUsed, ValueEquivalenceMap& valueMap) { +static bool areInsertGroupsEquivalentRec(const size_t lhsChainIdx, + ArrayRef lhsChains, + ArrayRef rhsChains, + SmallVectorImpl& rhsChainUsed, + ValueEquivalenceMap& valueMap) { if (lhsChainIdx == lhsChains.size()) { return true; } @@ -353,15 +352,15 @@ static bool areInsertGroupsEquivalentRec( return false; } -static bool areInsertGroupsEquivalent(llvm::ArrayRef lhsOps, - llvm::ArrayRef rhsOps, +static bool areInsertGroupsEquivalent(ArrayRef lhsOps, + ArrayRef rhsOps, ValueEquivalenceMap& valueMap) { if (lhsOps.size() != rhsOps.size()) { return false; } - llvm::SmallVector lhsChains; - llvm::SmallVector rhsChains; + SmallVector lhsChains; + SmallVector rhsChains; if (!summarizeInsertGroup(lhsOps, lhsChains) || !summarizeInsertGroup(rhsOps, rhsChains)) { return false; @@ -370,7 +369,7 @@ static bool areInsertGroupsEquivalent(llvm::ArrayRef lhsOps, return false; } - llvm::SmallVector rhsChainUsed(rhsChains.size(), 0); + SmallVector rhsChainUsed(rhsChains.size(), 0); return areInsertGroupsEquivalentRec(0, lhsChains, rhsChains, rhsChainUsed, valueMap); } @@ -408,8 +407,8 @@ template <> struct llvm::DenseMapInfo { } }; -static bool areFloatValuesNear(const llvm::APFloat& lhs, - const llvm::APFloat& rhs, const unsigned width) { +static bool areFloatValuesNear(const APFloat& lhs, const APFloat& rhs, + const unsigned width) { if (lhs.isNaN() || rhs.isNaN()) { return lhs.isNaN() && rhs.isNaN(); } @@ -443,8 +442,8 @@ static bool areConstantAttributesEquivalent(const Attribute& lhs, return true; } - if (auto lhsFloat = llvm::dyn_cast(lhs)) { - auto rhsFloat = llvm::dyn_cast(rhs); + if (auto lhsFloat = dyn_cast(lhs)) { + auto rhsFloat = dyn_cast(rhs); if (!rhsFloat) { return false; } @@ -465,8 +464,8 @@ static bool areOperationsEquivalent(Operation* lhs, Operation* rhs, } // Check arith::ConstantOp - if (auto lhsConst = llvm::dyn_cast(lhs)) { - auto rhsConst = llvm::dyn_cast(rhs); + if (auto lhsConst = dyn_cast(lhs)) { + auto rhsConst = dyn_cast(rhs); if (!rhsConst) { return false; } @@ -478,8 +477,8 @@ static bool areOperationsEquivalent(Operation* lhs, Operation* rhs, } // Check LLVM::ConstantOp - if (auto lhsConst = llvm::dyn_cast(lhs)) { - auto rhsConst = llvm::dyn_cast(rhs); + if (auto lhsConst = dyn_cast(lhs)) { + auto rhsConst = dyn_cast(rhs); if (!rhsConst) { return false; } @@ -490,8 +489,8 @@ static bool areOperationsEquivalent(Operation* lhs, Operation* rhs, } // Check LLVM::CallOp - if (auto lhsCall = llvm::dyn_cast(lhs)) { - auto rhsCall = llvm::dyn_cast(rhs); + if (auto lhsCall = dyn_cast(lhs)) { + auto rhsCall = dyn_cast(rhs); if (!rhsCall) { return false; } @@ -567,19 +566,19 @@ static bool hasOrderingConstraints(Operation* op) { // Symbol-defining operations (like function declarations) can be reordered if (op->hasTrait() || - llvm::isa(op)) { + isa(op)) { return false; } // Check for memory effects that enforce ordering - if (auto memInterface = llvm::dyn_cast(op)) { - llvm::SmallVector effects; + if (auto memInterface = dyn_cast(op)) { + SmallVector effects; memInterface.getEffects(effects); bool hasNonAllocFreeEffects = false; for (const auto& effect : effects) { // Allow operations with no effects or pure allocation/free effects - if (!llvm::isa( + if (!isa( effect.getEffect())) { hasNonAllocFreeEffects = true; break; @@ -596,17 +595,14 @@ static bool hasOrderingConstraints(Operation* op) { /// Build a dependence graph for operations. /// Returns a map from each operation to the set of operations it depends on. -llvm::DenseMap< - Operation*, - llvm::DenseSet< - Operation*>> static buildDependenceGraph(llvm::ArrayRef - ops) { - llvm::DenseMap> dependsOn; - llvm::DenseMap valueProducers; +DenseMap> static buildDependenceGraph( + ArrayRef ops) { + DenseMap> dependsOn; + DenseMap valueProducers; // Build value-to-producer map and dependence relationships for (Operation* op : ops) { - dependsOn[op] = llvm::DenseSet(); + dependsOn[op] = DenseSet(); // This operation depends on the producers of its operands for (const auto operand : op->getOperands()) { @@ -626,16 +622,15 @@ llvm::DenseMap< /// Partition operations into groups that can be compared as multisets. /// Operations in the same group are independent and can be reordered. -llvm::SmallVector> static partitionIndependentGroups(llvm::ArrayRef - ops) { - llvm::SmallVector> groups; +SmallVector> static partitionIndependentGroups( + ArrayRef ops) { + SmallVector> groups; if (ops.empty()) { return groups; } auto dependsOn = buildDependenceGraph(ops); - llvm::SmallVector currentGroup; + SmallVector currentGroup; for (auto* op : ops) { bool dependsOnCurrent = false; @@ -682,15 +677,15 @@ llvm::SmallVector lhsOps, - llvm::ArrayRef rhsOps) { +static bool areIndependentGroupsEquivalent(ArrayRef lhsOps, + ArrayRef rhsOps) { if (lhsOps.size() != rhsOps.size()) { return false; } // Build frequency maps for both groups - llvm::DenseMap lhsFrequencyMap; - llvm::DenseMap rhsFrequencyMap; + DenseMap lhsFrequencyMap; + DenseMap rhsFrequencyMap; for (auto* op : lhsOps) { lhsFrequencyMap[StructuralOperationKey(op)]++; @@ -734,8 +729,8 @@ static bool areBlocksEquivalent(Block& lhs, Block& rhs, } // Collect all operations - llvm::SmallVector lhsOps; - llvm::SmallVector rhsOps; + SmallVector lhsOps; + SmallVector rhsOps; for (Operation& op : lhs) { lhsOps.push_back(&op); @@ -784,7 +779,7 @@ static bool areBlocksEquivalent(Block& lhs, Block& rhs, // by trying all permutations (for small groups) or use a greedy approach // Use a simple greedy matching - llvm::DenseSet matchedRhs; + DenseSet matchedRhs; for (Operation* lhsOp : lhsGroup) { bool matched = false; for (Operation* rhsOp : rhsGroup) { diff --git a/mlir/lib/Support/Passes.cpp b/mlir/lib/Support/Passes.cpp index 0369056533..70a5a499d1 100644 --- a/mlir/lib/Support/Passes.cpp +++ b/mlir/lib/Support/Passes.cpp @@ -14,12 +14,11 @@ #include "mlir/Dialect/QIR/Transforms/Passes.h" #include "mlir/Dialect/QTensor/Transforms/Passes.h" -#include #include #include #include #include -#include +#include #include using namespace mlir; @@ -31,8 +30,8 @@ static void addSimplificationPasses(PassManager& pm) { static LogicalResult runWithPassManager(ModuleOp module, - const llvm::function_ref populatePasses, - const llvm::StringRef errorMessage) { + const function_ref populatePasses, + const StringRef errorMessage) { PassManager pm(module.getContext()); populatePasses(pm); if (pm.run(module).failed()) { diff --git a/mlir/lib/Support/PrettyPrinting.cpp b/mlir/lib/Support/PrettyPrinting.cpp index c8323ba848..5ce09f58a7 100644 --- a/mlir/lib/Support/PrettyPrinting.cpp +++ b/mlir/lib/Support/PrettyPrinting.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -29,7 +30,7 @@ constexpr int CONTENT_WIDTH = TOTAL_WIDTH - (2 * BORDER_WIDTH); // Pre-built strings, initialised once on first call. Each UTF-8 "═" is 3 // bytes. BORDER_SEP is the "═" run between box corners; SPACES is used for // padding. -static llvm::StringRef getBorderSep() { +static StringRef getBorderSep() { static const std::string BORDER_SEP = [] { std::string s; s.reserve(static_cast(TOTAL_WIDTH - 2) * 3U); @@ -41,12 +42,12 @@ static llvm::StringRef getBorderSep() { return BORDER_SEP; } -static llvm::StringRef getSpaces() { +static StringRef getSpaces() { static const std::string SPACES(static_cast(CONTENT_WIDTH), ' '); return SPACES; } -int calculateDisplayWidth(llvm::StringRef str) { +int calculateDisplayWidth(StringRef str) { auto displayWidth = 0; for (size_t i = 0; i < str.size();) { if (const unsigned char c = str[i]; (c & 0x80) == 0) { @@ -73,9 +74,8 @@ int calculateDisplayWidth(llvm::StringRef str) { return displayWidth; } -void wrapLine(llvm::StringRef line, const int maxWidth, - llvm::SmallVectorImpl>& result, - const int indent) { +void wrapLine(StringRef line, const int maxWidth, + SmallVectorImpl>& result, const int indent) { if (line.empty()) { result.emplace_back(""); return; @@ -94,7 +94,7 @@ void wrapLine(llvm::StringRef line, const int maxWidth, } // Extract the content without leading whitespace - const llvm::StringRef content = line.substr(line.find_first_not_of(" \t")); + const StringRef content = line.substr(line.find_first_not_of(" \t")); if (content.empty()) { result.emplace_back(line); return; @@ -114,15 +114,15 @@ void wrapLine(llvm::StringRef line, const int maxWidth, return; } - llvm::SmallString<128> currentLine; - llvm::SmallString<64> currentWord; + SmallString<128> currentLine; + SmallString<64> currentWord; auto currentWidth = 0; auto isFirstLine = true; // Helper: build and emit a completed line with proper indent prefix. // `addArrow` appends " →" to signal the line continues. auto flushLine = [&](const bool addArrow, const bool lastLine) { - llvm::SmallString<128> lineWithIndent; + SmallString<128> lineWithIndent; lineWithIndent.append(leadingSpaces, ' '); lineWithIndent += currentLine; if (addArrow && (!isFirstLine || !lastLine)) { @@ -131,7 +131,7 @@ void wrapLine(llvm::StringRef line, const int maxWidth, result.emplace_back(std::move(lineWithIndent)); }; - auto addWord = [&](llvm::StringRef word) -> bool { + auto addWord = [&](StringRef word) -> bool { const int wordWidth = calculateDisplayWidth(word); const int spaceWidth = currentLine.empty() ? 0 : 1; const int effectiveWidth = isFirstLine ? firstLineWidth : contLineWidth; @@ -161,7 +161,7 @@ void wrapLine(llvm::StringRef line, const int maxWidth, } // Start new continuation line currentLine = currentWord; - currentWidth = calculateDisplayWidth(llvm::StringRef(currentWord)); + currentWidth = calculateDisplayWidth(StringRef(currentWord)); isFirstLine = false; } currentWord.clear(); @@ -179,7 +179,7 @@ void wrapLine(llvm::StringRef line, const int maxWidth, flushLine(/*addArrow=*/true, /*lastLine=*/false); } // Add word on new continuation line (no arrow — this is the last line) - llvm::SmallString<128> contLine("↳ "); + SmallString<128> contLine("↳ "); contLine.append(leadingSpaces, ' '); contLine += currentWord; result.emplace_back(std::move(contLine)); @@ -203,30 +203,23 @@ void wrapLine(llvm::StringRef line, const int maxWidth, // Prepend "↳ " to all continuation lines (index >= 1) that don't have it yet for (size_t i = 1; i < result.size(); ++i) { - if (!llvm::StringRef(result[i]).contains("↳")) { - llvm::SmallString<128> newLine("↳ "); - const llvm::StringRef lineRef = result[i]; + if (!StringRef(result[i]).contains("↳")) { + SmallString<128> newLine("↳ "); + const StringRef lineRef = result[i]; newLine += lineRef.substr(leadingSpaces); result[i] = std::move(newLine); } } } -void printBoxTop(llvm::raw_ostream& os) { - os << "╔" << getBorderSep() << "╗\n"; -} +void printBoxTop(raw_ostream& os) { os << "╔" << getBorderSep() << "╗\n"; } -void printBoxMiddle(llvm::raw_ostream& os) { - os << "╠" << getBorderSep() << "╣\n"; -} +void printBoxMiddle(raw_ostream& os) { os << "╠" << getBorderSep() << "╣\n"; } -void printBoxBottom(llvm::raw_ostream& os) { - os << "╚" << getBorderSep() << "╝\n"; -} +void printBoxBottom(raw_ostream& os) { os << "╚" << getBorderSep() << "╝\n"; } // Internal helper: emit one already-wrapped line inside the box with padding. -static void emitBoxedLine(llvm::StringRef line, const int indent, - llvm::raw_ostream& os) { +static void emitBoxedLine(StringRef line, const int indent, raw_ostream& os) { const int displayWidth = calculateDisplayWidth(line); const int padding = CONTENT_WIDTH - indent - displayWidth; @@ -240,8 +233,7 @@ static void emitBoxedLine(llvm::StringRef line, const int indent, os << " ║\n"; } -void printBoxLine(llvm::StringRef text, const int indent, - llvm::raw_ostream& os) { +void printBoxLine(StringRef text, const int indent, raw_ostream& os) { // Trim trailing whitespace before processing const auto trimmedText = text.rtrim(); @@ -253,7 +245,7 @@ void printBoxLine(llvm::StringRef text, const int indent, } // Wrap the line - llvm::SmallVector, 4> wrappedLines; + SmallVector, 4> wrappedLines; wrapLine(trimmedText, CONTENT_WIDTH, wrappedLines, indent); for (const auto& line : wrappedLines) { @@ -261,10 +253,9 @@ void printBoxLine(llvm::StringRef text, const int indent, } } -void printBoxText(llvm::StringRef text, const int indent, - llvm::raw_ostream& os) { +void printBoxText(StringRef text, const int indent, raw_ostream& os) { // Trim trailing newlines from the entire text, then iterate line-by-line - llvm::StringRef remaining = text.rtrim(); + StringRef remaining = text.rtrim(); while (!remaining.empty()) { auto [lineStr, rest] = remaining.split('\n'); @@ -273,14 +264,13 @@ void printBoxText(llvm::StringRef text, const int indent, } } -void printProgram(ModuleOp module, const llvm::StringRef header, - llvm::raw_ostream& os) { +void printProgram(ModuleOp module, const StringRef header, raw_ostream& os) { printBoxTop(os); printBoxLine(header, 0, os); printBoxMiddle(os); // Capture the IR to a string so we can wrap it in box lines. - llvm::SmallString<4096> irString; + SmallString<4096> irString; llvm::raw_svector_ostream irStream(irString); module.print(irStream); diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp index 6ad3d1438e..a7c42660f5 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp @@ -19,12 +19,12 @@ #include "qco_programs.h" #include -#include #include #include #include #include #include +#include #include #include @@ -125,9 +125,9 @@ TEST_F(QCOTest, DirectIfBuilder) { auto measureOp = MeasureOp::create(builder, q1); auto ifOp = IfOp::create(builder, measureOp.getResult(), measureOp.getQubitOut(), - [&](ValueRange qubits) -> llvm::SmallVector { + [&](ValueRange qubits) -> SmallVector { auto innerQubit = XOp::create(builder, qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); auto r2 = qtensor::InsertOp::create(builder, ifOp.getResult(0), extractOp.getOutTensor(), c0); diff --git a/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp b/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp index 70a943c536..a8106c3604 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp @@ -19,6 +19,7 @@ #include "mlir/Dialect/QCO/Transforms/Passes.h" #include +#include #include #include #include diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp index 5ab06957f0..af398ea2b4 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp @@ -14,7 +14,6 @@ #include "mlir/Dialect/QCO/Transforms/Passes.h" #include -#include #include #include #include @@ -55,7 +54,7 @@ class MergeSingleQubitRotationGatesTest : public ::testing::Test { */ struct RotationGate { GateType type; - llvm::SmallVector angles; + SmallVector angles; }; MergeSingleQubitRotationGatesTest() : builder(&context) {} @@ -79,12 +78,11 @@ class MergeSingleQubitRotationGatesTest : public ::testing::Test { } /** - * @brief Extract constant floating point value from a mlir::Value + * @brief Extract constant floating point value from a Value */ - static std::optional toDouble(mlir::Value v) { - if (auto constOp = v.getDefiningOp()) { - if (auto floatAttr = - mlir::dyn_cast(constOp.getValue())) { + static std::optional toDouble(Value v) { + if (auto constOp = v.getDefiningOp()) { + if (auto floatAttr = dyn_cast(constOp.getValue())) { return floatAttr.getValueAsDouble(); } } @@ -101,7 +99,7 @@ class MergeSingleQubitRotationGatesTest : public ::testing::Test { module->walk([&](UOp op) { uOp = op; // stop after finding first UOp - return mlir::WalkResult::interrupt(); + return WalkResult::interrupt(); }); if (!uOp) { @@ -142,7 +140,7 @@ class MergeSingleQubitRotationGatesTest : public ::testing::Test { GPhaseOp gOp = nullptr; module->walk([&](GPhaseOp op) { gOp = op; - return mlir::WalkResult::interrupt(); + return WalkResult::interrupt(); }); if (!gOp) { @@ -162,7 +160,7 @@ class MergeSingleQubitRotationGatesTest : public ::testing::Test { EXPECT_NEAR(*param, expected, tolerance); } - Value buildRotations(llvm::ArrayRef rotations, Value& q) { + Value buildRotations(ArrayRef rotations, Value& q) { auto qubit = q; for (const auto& gate : rotations) { @@ -207,7 +205,7 @@ class MergeSingleQubitRotationGatesTest : public ::testing::Test { * builder api to build a small quantum circuit, where a qubit is fed through * all rotations in the list. */ - LogicalResult testGateMerge(llvm::ArrayRef rotations) { + LogicalResult testGateMerge(ArrayRef rotations) { auto q = builder.allocQubitRegister(1); buildRotations(rotations, q[0]); diff --git a/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp b/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp index 7aa0b1fd6a..616e08d66d 100644 --- a/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp +++ b/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp @@ -16,6 +16,8 @@ #include "mlir/Dialect/QCO/Utils/WireIterator.h" #include +#include +#include #include #include #include diff --git a/mlir/unittests/Dialect/Utils/test_utils.cpp b/mlir/unittests/Dialect/Utils/test_utils.cpp index 36bd137673..5ed1a21638 100644 --- a/mlir/unittests/Dialect/Utils/test_utils.cpp +++ b/mlir/unittests/Dialect/Utils/test_utils.cpp @@ -11,9 +11,7 @@ #include "mlir/Dialect/Utils/Utils.h" #include -#include #include -#include #include #include #include @@ -22,6 +20,7 @@ #include #include #include +#include #include #include @@ -96,7 +95,7 @@ TEST_F(UtilsTest, valueToDoubleCastFromMaxUnsignedInteger) { auto op = arith::ConstantOp::create( *builder, builder->getIntegerAttr(builder->getIntegerType(bitCount, false), - llvm::APInt::getMaxValue(bitCount))); + APInt::getMaxValue(bitCount))); ASSERT_TRUE(op); const auto stdValue = utils::valueToDouble(op.getResult()); @@ -126,11 +125,11 @@ TEST_F(UtilsTest, valueToDoubleFoldedConstant) { auto op = createAddition(1.5, 2.0); ASSERT_TRUE(op); - llvm::SmallVector tmp; - llvm::SmallVector newConstants; + SmallVector tmp; + SmallVector newConstants; ASSERT_TRUE(builder->tryFold(op, tmp, &newConstants).succeeded()); ASSERT_EQ(newConstants.size(), 1); - auto cst = llvm::dyn_cast(newConstants[0]); + auto cst = dyn_cast(newConstants[0]); ASSERT_TRUE(cst); const auto stdValue = utils::valueToDouble(cst.getResult()); ASSERT_TRUE(stdValue.has_value()); diff --git a/mlir/unittests/programs/qco_programs.cpp b/mlir/unittests/programs/qco_programs.cpp index 88dca42b54..ff05a9f075 100644 --- a/mlir/unittests/programs/qco_programs.cpp +++ b/mlir/unittests/programs/qco_programs.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -66,7 +67,7 @@ void staticQubitsWithCtrl(QCOProgramBuilder& b) { void staticQubitsWithInv(QCOProgramBuilder& b) { auto q0 = b.staticQubit(0); - q0 = b.inv({q0}, [&](auto targets) -> llvm::SmallVector { + q0 = b.inv({q0}, [&](auto targets) -> SmallVector { return {b.t(targets[0])}; })[0]; } @@ -175,19 +176,18 @@ void multipleControlledGlobalPhase(QCOProgramBuilder& b) { } void inverseGlobalPhase(QCOProgramBuilder& b) { - b.inv({}, [&](mlir::ValueRange /*qubits*/) { + b.inv({}, [&](ValueRange /*qubits*/) { b.gphase(-0.123); - return llvm::SmallVector{}; + return SmallVector{}; }); } void inverseMultipleControlledGlobalPhase(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { - llvm::SmallVector controls{qubits[0], qubits[1], qubits[2]}; + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { + SmallVector controls{qubits[0], qubits[1], qubits[2]}; auto controlsOut = b.mcgphase(-0.123, controls); - return llvm::SmallVector(controlsOut.begin(), - controlsOut.end()); + return SmallVector(controlsOut.begin(), controlsOut.end()); }); } @@ -208,13 +208,13 @@ void multipleControlledIdentity(QCOProgramBuilder& b) { void nestedControlledIdentity(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.id(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.id(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -225,18 +225,17 @@ void trivialControlledIdentity(QCOProgramBuilder& b) { void inverseIdentity(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.id(qubits[0])}; - }); + b.inv({q[0]}, + [&](ValueRange qubits) { return SmallVector{b.id(qubits[0])}; }); } void inverseMultipleControlledIdentity(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcid({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -257,13 +256,13 @@ void multipleControlledX(QCOProgramBuilder& b) { void nestedControlledX(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.x(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.x(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -274,18 +273,16 @@ void trivialControlledX(QCOProgramBuilder& b) { void inverseX(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.x(qubits[0])}; - }); + b.inv({q[0]}, [&](ValueRange qubits) { return SmallVector{b.x(qubits[0])}; }); } void inverseMultipleControlledX(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcx({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -312,13 +309,13 @@ void multipleControlledY(QCOProgramBuilder& b) { void nestedControlledY(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.y(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.y(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -329,18 +326,16 @@ void trivialControlledY(QCOProgramBuilder& b) { void inverseY(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.y(qubits[0])}; - }); + b.inv({q[0]}, [&](ValueRange qubits) { return SmallVector{b.y(qubits[0])}; }); } void inverseMultipleControlledY(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcy({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -367,13 +362,13 @@ void multipleControlledZ(QCOProgramBuilder& b) { void nestedControlledZ(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.z(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.z(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -384,18 +379,16 @@ void trivialControlledZ(QCOProgramBuilder& b) { void inverseZ(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.z(qubits[0])}; - }); + b.inv({q[0]}, [&](ValueRange qubits) { return SmallVector{b.z(qubits[0])}; }); } void inverseMultipleControlledZ(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcz({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -422,13 +415,13 @@ void multipleControlledH(QCOProgramBuilder& b) { void nestedControlledH(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.h(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.h(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -439,18 +432,16 @@ void trivialControlledH(QCOProgramBuilder& b) { void inverseH(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.h(qubits[0])}; - }); + b.inv({q[0]}, [&](ValueRange qubits) { return SmallVector{b.h(qubits[0])}; }); } void inverseMultipleControlledH(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mch({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -482,13 +473,13 @@ void multipleControlledS(QCOProgramBuilder& b) { void nestedControlledS(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.s(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.s(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -499,18 +490,16 @@ void trivialControlledS(QCOProgramBuilder& b) { void inverseS(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.s(qubits[0])}; - }); + b.inv({q[0]}, [&](ValueRange qubits) { return SmallVector{b.s(qubits[0])}; }); } void inverseMultipleControlledS(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcs({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -543,13 +532,13 @@ void multipleControlledSdg(QCOProgramBuilder& b) { void nestedControlledSdg(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.sdg(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.sdg(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -560,18 +549,17 @@ void trivialControlledSdg(QCOProgramBuilder& b) { void inverseSdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.sdg(qubits[0])}; - }); + b.inv({q[0]}, + [&](ValueRange qubits) { return SmallVector{b.sdg(qubits[0])}; }); } void inverseMultipleControlledSdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcsdg({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -604,13 +592,13 @@ void multipleControlledT(QCOProgramBuilder& b) { void nestedControlledT(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.t(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.t(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -621,18 +609,16 @@ void trivialControlledT(QCOProgramBuilder& b) { void inverseT(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.t(qubits[0])}; - }); + b.inv({q[0]}, [&](ValueRange qubits) { return SmallVector{b.t(qubits[0])}; }); } void inverseMultipleControlledT(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mct({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -665,13 +651,13 @@ void multipleControlledTdg(QCOProgramBuilder& b) { void nestedControlledTdg(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.tdg(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.tdg(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -682,18 +668,17 @@ void trivialControlledTdg(QCOProgramBuilder& b) { void inverseTdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.tdg(qubits[0])}; - }); + b.inv({q[0]}, + [&](ValueRange qubits) { return SmallVector{b.tdg(qubits[0])}; }); } void inverseMultipleControlledTdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mctdg({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -726,13 +711,13 @@ void multipleControlledSx(QCOProgramBuilder& b) { void nestedControlledSx(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.sx(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.sx(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -743,18 +728,17 @@ void trivialControlledSx(QCOProgramBuilder& b) { void inverseSx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.sx(qubits[0])}; - }); + b.inv({q[0]}, + [&](ValueRange qubits) { return SmallVector{b.sx(qubits[0])}; }); } void inverseMultipleControlledSx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcsx({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -787,13 +771,13 @@ void multipleControlledSxdg(QCOProgramBuilder& b) { void nestedControlledSxdg(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.sxdg(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.sxdg(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -804,18 +788,17 @@ void trivialControlledSxdg(QCOProgramBuilder& b) { void inverseSxdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.sxdg(qubits[0])}; - }); + b.inv({q[0]}, + [&](ValueRange qubits) { return SmallVector{b.sxdg(qubits[0])}; }); } void inverseMultipleControlledSxdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcsxdg({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -848,13 +831,13 @@ void multipleControlledRx(QCOProgramBuilder& b) { void nestedControlledRx(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.rx(0.123, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.rx(0.123, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -865,18 +848,18 @@ void trivialControlledRx(QCOProgramBuilder& b) { void inverseRx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.rx(-0.123, qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.rx(-0.123, qubits[0])}; }); } void inverseMultipleControlledRx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcrx(-0.123, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -908,13 +891,13 @@ void multipleControlledRy(QCOProgramBuilder& b) { void nestedControlledRy(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.ry(0.456, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.ry(0.456, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -925,18 +908,18 @@ void trivialControlledRy(QCOProgramBuilder& b) { void inverseRy(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.ry(-0.456, qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.ry(-0.456, qubits[0])}; }); } void inverseMultipleControlledRy(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcry(-0.456, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } void twoRyOppositePhase(QCOProgramBuilder& b) { @@ -967,13 +950,13 @@ void multipleControlledRz(QCOProgramBuilder& b) { void nestedControlledRz(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.rz(0.789, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.rz(0.789, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -984,18 +967,18 @@ void trivialControlledRz(QCOProgramBuilder& b) { void inverseRz(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.rz(-0.789, qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.rz(-0.789, qubits[0])}; }); } void inverseMultipleControlledRz(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcrz(-0.789, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -1022,13 +1005,13 @@ void multipleControlledP(QCOProgramBuilder& b) { void nestedControlledP(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.p(0.123, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.p(0.123, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1039,18 +1022,17 @@ void trivialControlledP(QCOProgramBuilder& b) { void inverseP(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.p(-0.123, qubits[0])}; - }); + b.inv({q[0]}, + [&](ValueRange qubits) { return SmallVector{b.p(-0.123, qubits[0])}; }); } void inverseMultipleControlledP(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcp(-0.123, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -1077,14 +1059,13 @@ void multipleControlledR(QCOProgramBuilder& b) { void nestedControlledR(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{ - b.r(0.123, 0.456, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.r(0.123, 0.456, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1095,18 +1076,18 @@ void trivialControlledR(QCOProgramBuilder& b) { void inverseR(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.r(-0.123, 0.456, qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.r(-0.123, 0.456, qubits[0])}; }); } void inverseMultipleControlledR(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcr(-0.123, 0.456, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -1137,14 +1118,13 @@ void multipleControlledU2(QCOProgramBuilder& b) { void nestedControlledU2(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{ - b.u2(0.234, 0.567, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.u2(0.234, 0.567, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1156,20 +1136,19 @@ void trivialControlledU2(QCOProgramBuilder& b) { void inverseU2(QCOProgramBuilder& b) { constexpr double pi = std::numbers::pi; auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{ - b.u2(-0.567 + pi, -0.234 - pi, qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.u2(-0.567 + pi, -0.234 - pi, qubits[0])}; }); } void inverseMultipleControlledU2(QCOProgramBuilder& b) { constexpr double pi = std::numbers::pi; auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcu2(-0.567 + pi, -0.234 - pi, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } void canonicalizeU2ToH(QCOProgramBuilder& b) { @@ -1203,14 +1182,13 @@ void multipleControlledU(QCOProgramBuilder& b) { void nestedControlledU(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{ - b.u(0.1, 0.2, 0.3, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.u(0.1, 0.2, 0.3, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1221,18 +1199,18 @@ void trivialControlledU(QCOProgramBuilder& b) { void inverseU(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.u(-0.1, -0.3, -0.2, qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.u(-0.1, -0.3, -0.2, qubits[0])}; }); } void inverseMultipleControlledU(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcu(-0.1, -0.3, -0.2, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -1273,15 +1251,14 @@ void multipleControlledSwap(QCOProgramBuilder& b) { void nestedControlledSwap(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.swap(innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.swap(innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1292,20 +1269,19 @@ void trivialControlledSwap(QCOProgramBuilder& b) { void inverseSwap(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.swap(qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledSwap(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcswap({qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1338,15 +1314,14 @@ void multipleControlledIswap(QCOProgramBuilder& b) { void nestedControlledIswap(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.iswap(innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.iswap(innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1357,20 +1332,19 @@ void trivialControlledIswap(QCOProgramBuilder& b) { void inverseIswap(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.iswap(qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledIswap(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mciswap({qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1391,15 +1365,14 @@ void multipleControlledDcx(QCOProgramBuilder& b) { void nestedControlledDcx(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.dcx(innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.dcx(innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1410,20 +1383,19 @@ void trivialControlledDcx(QCOProgramBuilder& b) { void inverseDcx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[1], q[0]}, [&](mlir::ValueRange qubits) { + b.inv({q[1], q[0]}, [&](ValueRange qubits) { auto res = b.dcx(qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledDcx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[3], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[3], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcdcx({qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1456,15 +1428,14 @@ void multipleControlledEcr(QCOProgramBuilder& b) { void nestedControlledEcr(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.ecr(innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.ecr(innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1475,20 +1446,19 @@ void trivialControlledEcr(QCOProgramBuilder& b) { void inverseEcr(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.ecr(qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledEcr(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcecr({qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1515,15 +1485,14 @@ void multipleControlledRxx(QCOProgramBuilder& b) { void nestedControlledRxx(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.rxx(0.123, innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.rxx(0.123, innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1534,20 +1503,19 @@ void trivialControlledRxx(QCOProgramBuilder& b) { void inverseRxx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.rxx(-0.123, qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledRxx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcrxx(-0.123, {qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1604,15 +1572,14 @@ void multipleControlledRyy(QCOProgramBuilder& b) { void nestedControlledRyy(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.ryy(0.123, innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.ryy(0.123, innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1623,20 +1590,19 @@ void trivialControlledRyy(QCOProgramBuilder& b) { void inverseRyy(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.ryy(-0.123, qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledRyy(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcryy(-0.123, {qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1683,15 +1649,14 @@ void multipleControlledRzx(QCOProgramBuilder& b) { void nestedControlledRzx(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.rzx(0.123, innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.rzx(0.123, innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1702,20 +1667,19 @@ void trivialControlledRzx(QCOProgramBuilder& b) { void inverseRzx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.rzx(-0.123, qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledRzx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcrzx(-0.123, {qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1742,15 +1706,14 @@ void multipleControlledRzz(QCOProgramBuilder& b) { void nestedControlledRzz(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.rzz(0.123, innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.rzz(0.123, innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1761,20 +1724,19 @@ void trivialControlledRzz(QCOProgramBuilder& b) { void inverseRzz(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.rzz(-0.123, qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledRzz(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcrzz(-0.123, {qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1821,16 +1783,15 @@ void multipleControlledXxPlusYY(QCOProgramBuilder& b) { void nestedControlledXxPlusYY(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.xx_plus_yy(0.123, 0.456, innerTargets[0], - innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = + b.xx_plus_yy(0.123, 0.456, innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1841,20 +1802,19 @@ void trivialControlledXxPlusYY(QCOProgramBuilder& b) { void inverseXxPlusYY(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.xx_plus_yy(-0.123, 0.456, qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledXxPlusYY(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcxx_plus_yy( -0.123, 0.456, {qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1881,16 +1841,15 @@ void multipleControlledXxMinusYY(QCOProgramBuilder& b) { void nestedControlledXxMinusYY(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.xx_minus_yy(0.123, 0.456, innerTargets[0], - innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = + b.xx_minus_yy(0.123, 0.456, innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1901,20 +1860,19 @@ void trivialControlledXxMinusYY(QCOProgramBuilder& b) { void inverseXxMinusYY(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.xx_minus_yy(-0.123, 0.456, qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledXxMinusYY(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcxx_minus_yy( -0.123, 0.456, {qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1941,15 +1899,15 @@ void barrierMultipleQubits(QCOProgramBuilder& b) { void singleControlledBarrier(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.ctrl({q[1]}, {q[0]}, [&](mlir::ValueRange targets) { - return llvm::SmallVector{b.barrier(targets[0])}; + b.ctrl({q[1]}, {q[0]}, [&](ValueRange targets) { + return SmallVector{b.barrier(targets[0])}; }); } void inverseBarrier(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.barrier(qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.barrier(qubits[0])}; }); } @@ -1963,124 +1921,119 @@ void twoBarrier(QCOProgramBuilder& b) { void trivialCtrl(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.ctrl({}, {q[0], q[1]}, [&](mlir::ValueRange targets) { + b.ctrl({}, {q[0], q[1]}, [&](ValueRange targets) { auto [q0, q1] = b.rxx(0.123, targets[0], targets[1]); - return llvm::SmallVector{q0, q1}; + return SmallVector{q0, q1}; }); } void nestedCtrl(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.ctrl({q[0]}, {q[1], q[2], q[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto [q0, q1] = b.rxx(0.123, innerTargets[0], innerTargets[1]); - return llvm::SmallVector{q0, q1}; - }); + b.ctrl({q[0]}, {q[1], q[2], q[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto [q0, q1] = b.rxx(0.123, innerTargets[0], innerTargets[1]); + return SmallVector{q0, q1}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } void tripleNestedCtrl(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(5); - b.ctrl({q[0]}, {q[1], q[2], q[3], q[4]}, [&](mlir::ValueRange targets) { + b.ctrl({q[0]}, {q[1], q[2], q[3], q[4]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( {targets[0]}, {targets[1], targets[2], targets[3]}, - [&](mlir::ValueRange innerTargets) { + [&](ValueRange innerTargets) { const auto& [innerInnerControlsOut, innerInnerTargetsOut] = b.ctrl({innerTargets[0]}, {innerTargets[1], innerTargets[2]}, - [&](mlir::ValueRange innerInnerTargets) { + [&](ValueRange innerInnerTargets) { auto [q0, q1] = b.rxx(0.123, innerInnerTargets[0], innerInnerTargets[1]); - return llvm::SmallVector{q0, q1}; + return SmallVector{q0, q1}; }); - return llvm::to_vector(llvm::concat( - innerInnerControlsOut, innerInnerTargetsOut)); + return llvm::to_vector( + llvm::concat(innerInnerControlsOut, innerInnerTargetsOut)); }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } void doubleNestedCtrlTwoQubits(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(6); - b.ctrl({q[0], q[1]}, {q[2], q[3], q[4], q[5]}, [&](mlir::ValueRange targets) { + b.ctrl({q[0], q[1]}, {q[2], q[3], q[4], q[5]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = b.ctrl({targets[0], targets[1]}, {targets[2], targets[3]}, - [&](mlir::ValueRange innerTargets) { + [&](ValueRange innerTargets) { auto [q0, q1] = b.rxx(0.123, innerTargets[0], innerTargets[1]); - return llvm::SmallVector{q0, q1}; + return SmallVector{q0, q1}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } void ctrlInvSandwich(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.ctrl({q[0]}, {q[1], q[2], q[3]}, [&](mlir::ValueRange targets) { + b.ctrl({q[0]}, {q[1], q[2], q[3]}, [&](ValueRange targets) { auto inner = b.inv( - {targets[0], targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { + {targets[0], targets[1], targets[2]}, [&](ValueRange innerTargets) { auto [innerControlsOut, innerTargetsOut] = b.ctrl({innerTargets[0]}, {innerTargets[1], innerTargets[2]}, - [&](mlir::ValueRange innerInnerTargets) { + [&](ValueRange innerInnerTargets) { auto [q0, q1] = b.rxx(-0.123, innerInnerTargets[0], innerInnerTargets[1]); - return llvm::SmallVector{q0, q1}; + return SmallVector{q0, q1}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); - return llvm::SmallVector{inner}; + return SmallVector{inner}; }); } void nestedInv(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { - auto inner = - b.inv({qubits[0], qubits[1]}, [&](mlir::ValueRange innerQubits) { - auto [q0, q1] = b.rxx(0.123, innerQubits[0], innerQubits[1]); - return llvm::SmallVector{q0, q1}; - }); - return llvm::SmallVector{inner}; + b.inv({q[0], q[1]}, [&](ValueRange qubits) { + auto inner = b.inv({qubits[0], qubits[1]}, [&](ValueRange innerQubits) { + auto [q0, q1] = b.rxx(0.123, innerQubits[0], innerQubits[1]); + return SmallVector{q0, q1}; + }); + return SmallVector{inner}; }); } void tripleNestedInv(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { - auto inner1 = - b.inv({qubits[0], qubits[1]}, [&](mlir::ValueRange innerQubits) { - auto inner2 = b.inv({innerQubits[0], innerQubits[1]}, - [&](mlir::ValueRange innerInnerQubits) { - auto [q0, q1] = - b.rxx(-0.123, innerInnerQubits[0], - innerInnerQubits[1]); - return llvm::SmallVector{q0, q1}; - }); - return llvm::SmallVector{inner2}; - }); - return llvm::SmallVector{inner1}; + b.inv({q[0], q[1]}, [&](ValueRange qubits) { + auto inner1 = b.inv({qubits[0], qubits[1]}, [&](ValueRange innerQubits) { + auto inner2 = b.inv( + {innerQubits[0], innerQubits[1]}, [&](ValueRange innerInnerQubits) { + auto [q0, q1] = + b.rxx(-0.123, innerInnerQubits[0], innerInnerQubits[1]); + return SmallVector{q0, q1}; + }); + return SmallVector{inner2}; + }); + return SmallVector{inner1}; }); } void invCtrlSandwich(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { - const auto& [controlsOut, targetsOut] = b.ctrl( - {qubits[0]}, {qubits[1], qubits[2]}, [&](mlir::ValueRange targets) { - auto inner = b.inv( - {targets[0], targets[1]}, [&](mlir::ValueRange innerQubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { + const auto& [controlsOut, targetsOut] = + b.ctrl({qubits[0]}, {qubits[1], qubits[2]}, [&](ValueRange targets) { + auto inner = + b.inv({targets[0], targets[1]}, [&](ValueRange innerQubits) { auto [q0, q1] = b.rxx(0.123, innerQubits[0], innerQubits[1]); - return llvm::SmallVector{q0, q1}; + return SmallVector{q0, q1}; }); - return llvm::SmallVector{inner}; + return SmallVector{inner}; }); - return llvm::to_vector(llvm::concat(controlsOut, targetsOut)); + return llvm::to_vector(llvm::concat(controlsOut, targetsOut)); }); } @@ -2088,9 +2041,9 @@ void simpleIf(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); auto q0 = b.h(q[0]); auto [measuredQubit, measureResult] = b.measure(q0); - b.qcoIf(measureResult, measuredQubit, [&](mlir::ValueRange qubits) { + b.qcoIf(measureResult, measuredQubit, [&](ValueRange qubits) { auto innerQubit = b.x(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); } @@ -2098,10 +2051,10 @@ void ifTwoQubits(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); auto q0 = b.h(q[0]); auto [measuredQubit, measureResult] = b.measure(q0); - b.qcoIf(measureResult, {measuredQubit, q[1]}, [&](mlir::ValueRange qubits) { + b.qcoIf(measureResult, {measuredQubit, q[1]}, [&](ValueRange qubits) { auto innerQubit0 = b.x(qubits[0]); auto innerQubit1 = b.x(qubits[1]); - return llvm::SmallVector{innerQubit0, innerQubit1}; + return SmallVector{innerQubit0, innerQubit1}; }); } @@ -2111,13 +2064,13 @@ void ifElse(QCOProgramBuilder& b) { auto [measuredQubit, measureResult] = b.measure(q0); b.qcoIf( measureResult, {measuredQubit}, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.x(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.z(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); } @@ -2125,13 +2078,13 @@ void constantTrueIf(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.qcoIf( true, q.qubits, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.x(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.z(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); } @@ -2139,13 +2092,13 @@ void constantFalseIf(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.qcoIf( false, q.qubits, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.x(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.z(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); } @@ -2153,11 +2106,11 @@ void nestedTrueIf(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); auto q0 = b.h(q[0]); auto [measuredQubit, measureResult] = b.measure(q0); - b.qcoIf(measureResult, measuredQubit, [&](mlir::ValueRange outerQubits) { + b.qcoIf(measureResult, measuredQubit, [&](ValueRange outerQubits) { auto innerResult = - b.qcoIf(measureResult, outerQubits, [&](mlir::ValueRange innerQubits) { + b.qcoIf(measureResult, outerQubits, [&](ValueRange innerQubits) { auto innerQubit = b.x(innerQubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); return llvm::to_vector(innerResult); }); @@ -2169,19 +2122,19 @@ void nestedFalseIf(QCOProgramBuilder& b) { auto [measuredQubit, measureResult] = b.measure(q0); b.qcoIf( measureResult, measuredQubit, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.x(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }, - [&](mlir::ValueRange outerQubits) { + [&](ValueRange outerQubits) { auto innerResult = b.qcoIf( measureResult, outerQubits, - [&](mlir::ValueRange innerQubits) { + [&](ValueRange innerQubits) { return llvm::to_vector(innerQubits); }, - [&](mlir::ValueRange innerQubits) { + [&](ValueRange innerQubits) { auto innerQubit = b.z(innerQubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); return llvm::to_vector(innerResult); }); From 0e61f878dff0328191cefc9432055d7e733371fa Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Wed, 29 Apr 2026 20:45:58 +0200 Subject: [PATCH 24/29] =?UTF-8?q?=F0=9F=8E=A8=20Remove=20remaining=20expli?= =?UTF-8?q?cit=20`llvm`=20namespaces=20(#1675)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 +- mlir/include/mlir/Dialect/QCO/QCOUtils.h | 16 +- mlir/include/mlir/Dialect/QCO/Utils/Drivers.h | 237 ++++++++++++------ .../mlir/Dialect/QTensor/IR/QTensorOps.td | 34 ++- .../mlir/Dialect/QTensor/IR/QTensorUtils.h | 16 +- 5 files changed, 198 insertions(+), 108 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20500bb816..cc7afe0625 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel - ✨ Add conversions between `jeff` and QCO ([#1479], [#1548], [#1565], [#1637]) ([**@denialhaag**]) - ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588], [#1664]) ([**@MatthiasReumann**], [**@burgholzer**]) - ✨ Add initial infrastructure for new QC and QCO MLIR dialects - ([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635], [#1673]) + ([#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], [#1673], [#1675]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**]) ### Changed @@ -361,6 +361,7 @@ _� Refer to the [GitHub Release Notes](https://github.com/munich-quantum-toolk +[#1675]: https://github.com/munich-quantum-toolkit/core/pull/1675 [#1673]: https://github.com/munich-quantum-toolkit/core/pull/1673 [#1664]: https://github.com/munich-quantum-toolkit/core/pull/1664 [#1662]: https://github.com/munich-quantum-toolkit/core/pull/1662 diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index a5fdba0c35..489fceb00e 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -29,8 +29,7 @@ template LogicalResult removeInversePairOneTargetZeroParameter(OpType op, PatternRewriter& rewriter) { // Check if the successor is the inverse operation - auto nextOp = - llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } @@ -54,8 +53,7 @@ template LogicalResult removeInversePairTwoTargetZeroParameter(OpType op, PatternRewriter& rewriter) { // Check if the successor is the inverse operation - auto nextOp = - llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } @@ -85,7 +83,7 @@ LogicalResult removeTwoTargetZeroParameterPairWithSwappedTargets(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation - auto nextOp = llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } @@ -120,7 +118,7 @@ template LogicalResult mergeOneTargetZeroParameter(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation - auto nextOp = llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } @@ -149,7 +147,7 @@ LogicalResult mergeOneTargetZeroParameter(OpType op, template LogicalResult mergeOneTargetOneParameter(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation - auto nextOp = llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } @@ -179,7 +177,7 @@ LogicalResult mergeOneTargetOneParameter(OpType op, PatternRewriter& rewriter) { template LogicalResult mergeTwoTargetOneParameter(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation - auto nextOp = llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } @@ -218,7 +216,7 @@ LogicalResult mergeTwoTargetOneParameterWithSwappedTargets(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation - auto nextOp = llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } diff --git a/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h b/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h index 3b8f934ab7..0f56355ec6 100644 --- a/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h +++ b/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h @@ -12,88 +12,83 @@ #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOInterfaces.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Utils/Qubits.h" #include "mlir/Dialect/QCO/Utils/WireIterator.h" +#include "mlir/Dialect/QTensor/IR/QTensorOps.h" -#include -#include #include -#include #include #include +#include #include -#include #include +#include +#include +#include +#include namespace mlir::qco { -/** - * @brief Specifies the layering direction. - */ -enum class WalkDirection : bool { Forward, Backward }; - -class Qubits { -public: - /** - * @brief Add qubit with automatically assigned index. - */ - void add(TypedValue q); - - /** - * @brief Add qubit with index. - */ - void add(TypedValue q, std::size_t index); - - /** - * @brief Remap the qubit value from prev to next. - */ - void remap(TypedValue prev, TypedValue next, - const WalkDirection& direction); - - /** - * @brief Remap all input qubits of the unitary to its outputs. - */ - void remap(UnitaryOpInterface op, const WalkDirection& direction); - - /** - * @brief Remove the qubit value. - */ - void remove(TypedValue q); - - /** - * @returns the qubit value assigned to a index. - */ - [[nodiscard]] TypedValue getQubit(std::size_t index) const; - - /** - * @returns the index assigned to the given qubit value. - */ - [[nodiscard]] std::size_t getIndex(TypedValue q) const; - -private: - DenseMap> indexToValue_; - DenseMap, std::size_t> valueToIndex_; -}; - -using WalkUnitFn = function_ref; +using WalkProgramFn = function_ref; /** * @brief Perform top-down non-recursive walk of all operations within a - * region and apply callback function. - * @details - * The signature of the callback function is: + * region of a quantum program and apply a callback function. + * @details The signature of the callback function is: * * (Operation*, Qubits& q) -> WalkResult * * where the Qubits object tracks the front of qubit SSA values. - * + * Depending on the template parameter, the callback is executed before or after + * updating the Qubits state. * @param region The targeted region. * @param fn The callback function. + * @returns success(), if all operations have been visited. */ -void walkUnit(Region& region, WalkUnitFn fn); +template +LogicalResult walkProgram(Region& region, const WalkProgramFn& fn) { + Qubits qubits; + for (Operation& curr : region.getOps()) { + if constexpr (Order == WalkOrder::PreOrder) { + if (fn(&curr, qubits).wasInterrupted()) { + return failure(); + } + } + + TypeSwitch(&curr) + .template Case( + [&](StaticOp op) { qubits.add(op.getQubit(), op.getIndex()); }) + .template Case([&](AllocOp op) { qubits.add(op.getResult()); }) + .template Case([&](UnitaryOpInterface& op) { + for (const auto& [prevV, nextV] : + llvm::zip(op.getInputQubits(), op.getOutputQubits())) { + const auto prevQ = cast>(prevV); + const auto nextQ = cast>(nextV); + qubits.remap(prevQ, nextQ); + } + }) + .template Case([&](ResetOp op) { + qubits.remap(op.getQubitIn(), op.getQubitOut()); + }) + .template Case([&](MeasureOp op) { + qubits.remap(op.getQubitIn(), op.getQubitOut()); + }) + .template Case( + [&](SinkOp op) { qubits.remove(op.getQubit()); }); + + if constexpr (Order == WalkOrder::PostOrder) { + if (fn(&curr, qubits).wasInterrupted()) { + return failure(); + } + } + } -using ReleasedOps = SmallVector; + return success(); +} +using ReleasedOps = SmallVector; using PendingWiresMap = DenseMap>; @@ -106,7 +101,7 @@ struct IsReady { using ReadyRange = decltype(make_filter_range(std::declval(), IsReady{})); -using WalkCircuitGraphFn = +using WalkProgramGraphFn = function_ref; /** @@ -115,26 +110,124 @@ using WalkCircuitGraphFn = * Depending on the template parameter, the function collects the * layers in forward or backward direction, respectively. Towards that end, * the function traverses the def-use chain of each qubit until a multi-qubit - * gate (including barriers) is found. If a multi-qubit gate is visited twice, - * it is considered ready and inserted into the layer. This process is repeated - * until no more multi-qubit gates are found anymore. + * gate (including barriers) is found. If each input qubit of a multi-qubit gate + * is visited, it is considered ready. This process is repeated until no more + * multi-qubit gates are found anymore. * * The signature of the callback function is: * - * (FrontArrayRef, ReleasedOps&) -> WalkResult + * (const ReadyRange& ready, ReleasedOps& released) -> WalkResult * * The operations inserted into the parameter "released" determine which * multi-qubit gates are released in next iteration. + * If the callback returns WalkResult::skip(), all ready operations will be + * released. * * @param wires A mutable array-ref of circuit wires (wire iterators). - * @param direction The traversal direction. * @param fn The callback function. * - * @returns - * failure(), if the callback returns WalkResult::interrupt() - * failure(), if the callback returns WalkResult::skipped() - * success(), otherwise. + * @returns success(), if all operations have been visited. */ -LogicalResult walkCircuitGraph(MutableArrayRef wires, - WalkDirection direction, WalkCircuitGraphFn fn); -} // namespace mlir::qco +template +LogicalResult walkProgramGraph(MutableArrayRef wires, + WalkProgramGraphFn fn) { + using Traits = WireTraversalTraits; + + ReleasedOps released; + + PendingWiresMap pending; + pending.reserve(wires.size()); + + SmallVector curr(wires.size()); + std::iota(curr.begin(), curr.end(), 0UL); + + SmallVector next; + next.reserve(wires.size()); + + while (!curr.empty()) { + for (std::size_t i : curr) { + auto& it = wires[i]; + while (Traits::isActive(it)) { + const auto res = + TypeSwitch(it.operation()) + .template Case([&](UnitaryOpInterface& op) { + // If there are fewer wires than the qubit requires inputs, + // it's impossible to release the operation. Hence, fail. + if (op.getNumQubits() > wires.size()) { + return WalkResult::interrupt(); + } + + if (op.getNumQubits() == 1) { + std::ranges::advance(it, Traits::stride()); + return WalkResult::advance(); + } + + // Insert the unitary to the pending map. + // The caller decides if this op should be released. + const auto [mapIt, inserted] = pending.try_emplace(op); + auto& indices = mapIt->second; + + if (inserted) { + indices.reserve(op.getNumQubits()); + } + + indices.emplace_back(i); + + return WalkResult::skip(); // Stop at multi-qubit gate. + }) + // AllocOp, StaticOp, and qtensor::ExtractOp are only reachable + // on the forward path; backward isActive() halts before + // reaching them (decrementing at a source op is a no-op). + .template Case([&](auto) { + std::ranges::advance(it, Traits::stride()); + return WalkResult::advance(); + }) + .Default([&](Operation* op) -> WalkResult { + const auto name = op->getName().getStringRef(); + report_fatal_error("unknown op encountered: " + name); + }); + + if (res.wasSkipped()) { + break; + } + + if (res.wasInterrupted()) { + return failure(); + } + } + } + + released.clear(); + const auto ready = make_filter_range(pending, IsReady{}); + const auto res = std::invoke(fn, ready, released); + if (res.wasInterrupted()) { + return failure(); + } + + if (res.wasSkipped()) { + released.clear(); + for (const auto& [op, _] : ready) { + released.emplace_back(op); + } + } + + for (const UnitaryOpInterface& op : released) { + const auto mapIt = pending.find(op); + assert(mapIt != pending.end()); + + for (std::size_t i : mapIt->second) { + std::ranges::advance(wires[i], Traits::stride()); + next.emplace_back(i); + } + + pending.erase(mapIt); + } + + curr.swap(next); + next.clear(); + } + + return success(); +} +} // namespace mlir::qco \ No newline at end of file diff --git a/mlir/include/mlir/Dialect/QTensor/IR/QTensorOps.td b/mlir/include/mlir/Dialect/QTensor/IR/QTensorOps.td index 1ac3aa1885..7a0e26ea27 100644 --- a/mlir/include/mlir/Dialect/QTensor/IR/QTensorOps.td +++ b/mlir/include/mlir/Dialect/QTensor/IR/QTensorOps.td @@ -86,8 +86,8 @@ def FromElementsOp TypesMatchWith< "operand types match result element type", "result", "elements", "SmallVector(" - "::llvm::cast($_self).getNumElements(), " - "::llvm::cast($_self).getElementType())">]> { + "cast($_self).getNumElements(), " + "cast($_self).getElementType())">]> { let summary = "Create a tensor from a range of values"; let description = [{ The `qtensor.from_elements` operation is a wrapper operation of the `tensor.from_elements` operation. @@ -110,14 +110,13 @@ def FromElementsOp } def ExtractOp - : QTensorOp< - "extract", - [Pure, - TypesMatchWith<"result type matches element type of tensor", - "tensor", "result", - "::llvm::cast($_self).getElementType()">, - TypesMatchWith<"returned tensor type matches input tensor", "tensor", - "out_tensor", "$_self">]> { + : QTensorOp<"extract", + [Pure, + TypesMatchWith<"result type matches element type of tensor", + "tensor", "result", + "cast($_self).getElementType()">, + TypesMatchWith<"returned tensor type matches input tensor", + "tensor", "out_tensor", "$_self">]> { let summary = "Extract element from tensor"; let description = [{ The `qtensor.extract` operation is the modified version of the standard `tensor.extract` @@ -138,14 +137,13 @@ def ExtractOp } def InsertOp - : QTensorOp< - "insert", - [Pure, - TypesMatchWith<"result type matches type of dest", "dest", "result", - "$_self">, - TypesMatchWith< - "scalar type matches element type of dest", "dest", "scalar", - "::llvm::cast($_self).getElementType()">]> { + : QTensorOp<"insert", [Pure, + TypesMatchWith<"result type matches type of dest", + "dest", "result", "$_self">, + TypesMatchWith< + "scalar type matches element type of dest", + "dest", "scalar", + "cast($_self).getElementType()">]> { let summary = "Insert element into tensor"; let description = [{ The `qtensor.insert` operation is a wrapper operation for the `tensor.insert` operation of the tensor dialect. diff --git a/mlir/include/mlir/Dialect/QTensor/IR/QTensorUtils.h b/mlir/include/mlir/Dialect/QTensor/IR/QTensorUtils.h index 594be34481..71a321e9fc 100644 --- a/mlir/include/mlir/Dialect/QTensor/IR/QTensorUtils.h +++ b/mlir/include/mlir/Dialect/QTensor/IR/QTensorUtils.h @@ -12,9 +12,9 @@ #include "mlir/Dialect/QTensor/IR/QTensorOps.h" -#include #include #include +#include namespace mlir::qtensor { @@ -40,17 +40,17 @@ inline bool areEquivalentIndices(Value lhs, Value rhs) { * @brief Tensor-transforming ops in a scalar extract/insert chain. */ inline bool isTensorChainOp(Operation* op) { - return llvm::isa(op); + return isa(op); } /** * @brief Returns the tensor input of a tensor-transforming op. */ inline Value getTensorChainInput(Operation* op) { - if (auto insertOp = llvm::dyn_cast(op)) { + if (auto insertOp = dyn_cast(op)) { return insertOp.getDest(); } - if (auto extractOp = llvm::dyn_cast(op)) { + if (auto extractOp = dyn_cast(op)) { return extractOp.getTensor(); } return nullptr; @@ -60,10 +60,10 @@ inline Value getTensorChainInput(Operation* op) { * @brief Returns the tensor output of a tensor-transforming op. */ inline Value getTensorChainOutput(Operation* op) { - if (auto insertOp = llvm::dyn_cast(op)) { + if (auto insertOp = dyn_cast(op)) { return insertOp.getResult(); } - if (auto extractOp = llvm::dyn_cast(op)) { + if (auto extractOp = dyn_cast(op)) { return extractOp.getOutTensor(); } return nullptr; @@ -73,11 +73,11 @@ inline Value getTensorChainOutput(Operation* op) { * @brief Rewire the tensor input of a tensor-transforming op. */ inline void setTensorChainInput(Operation* op, Value tensor) { - if (llvm::isa(op)) { + if (isa(op)) { op->setOperand(1, tensor); return; } - if (llvm::isa(op)) { + if (isa(op)) { op->setOperand(0, tensor); } } From 041cbf9ac373591fca8019be3fe6130dbd00b73f Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Fri, 17 Apr 2026 18:43:05 +0200 Subject: [PATCH 25/29] Add SwapAbsorb pass --- .../mlir/Dialect/QCO/Transforms/Passes.td | 6 +++-- mlir/lib/Compiler/CompilerPipeline.cpp | 3 +-- .../QCO/Transforms/Mapping/SwapAbsorb.cpp | 22 +++++++++---------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index 276d0ba294..fef9d41532 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -105,8 +105,10 @@ def SwapAbsorb : Pass<"absorb-swaps", "mlir::ModuleOp"> { let dependentDialects = ["mlir::qco::QCODialect"]; let summary = ""; let options = []; - let statistics = [Statistic<"numSwaps", "num-inserted-swaps", - "The number of absorbed SWAPs">]; + let statistics = [ + Statistic<"numSwaps", "num-inserted-swaps", + "The number of absorbed SWAPs"> + ]; } #endif // MLIR_DIALECT_QCO_TRANSFORMS_PASSES_TD diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index 5a4ca8697c..b3036f4544 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -135,8 +135,6 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, totalStages); } } - - // auto arch = std::make_shared("RigettiNovera", 9, COUPLING); // Stage 5: Optimization passes if (failed(runStage([&](PassManager& pm) { if (!config_.disableMergeSingleQubitRotationGates) { @@ -238,3 +236,4 @@ std::string captureIR(ModuleOp module) { } } // namespace mlir + // auto arch = std::make_shared("RigettiNovera", 9, COUPLING); \ No newline at end of file diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp index b902395ede..920fd5ba7c 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp @@ -48,17 +48,17 @@ struct SwapAbsorb : impl::SwapAbsorbBase { SmallVector readyToAbsorb; readyToAbsorb.reserve((wires.size() + 1) / 2); - std::ignore = - walkCircuitGraph(wires, WalkDirection::Forward, - [&](const ReadyRange& ready, ReleasedOps& released) { - for (const auto& [op, indices] : ready) { - if (isa(op)) { - readyToAbsorb.emplace_back(op); - } - released.emplace_back(op); - } - return WalkResult::interrupt(); - }); + // std::ignore = + // walkCircuitGraph(wires, WalkDirection::Forward, + // [&](const ReadyRange& ready, ReleasedOps& released) { + // for (const auto& [op, indices] : ready) { + // if (isa(op)) { + // readyToAbsorb.emplace_back(op); + // } + // released.emplace_back(op); + // } + // return WalkResult::interrupt(); + // }); for (auto swapOp : readyToAbsorb) { auto in0 = swapOp.getQubit0In(); From 2ba14ae951d9e03866dce51773f562e2a9e87e31 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 23 Apr 2026 06:34:06 +0000 Subject: [PATCH 26/29] =?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 --- mlir/include/mlir/Dialect/QCO/Transforms/Passes.td | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index fef9d41532..276d0ba294 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -105,10 +105,8 @@ def SwapAbsorb : Pass<"absorb-swaps", "mlir::ModuleOp"> { let dependentDialects = ["mlir::qco::QCODialect"]; let summary = ""; let options = []; - let statistics = [ - Statistic<"numSwaps", "num-inserted-swaps", - "The number of absorbed SWAPs"> - ]; + let statistics = [Statistic<"numSwaps", "num-inserted-swaps", + "The number of absorbed SWAPs">]; } #endif // MLIR_DIALECT_QCO_TRANSFORMS_PASSES_TD From 8fa9853f1dc81e60320fde79f3ff87bba6b69695 Mon Sep 17 00:00:00 2001 From: Johannes Moosburger Date: Sat, 25 Apr 2026 12:57:36 +0200 Subject: [PATCH 27/29] revert CompilerPipeline --- mlir/lib/Compiler/CompilerPipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index b3036f4544..f30e4675bf 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -236,4 +236,4 @@ std::string captureIR(ModuleOp module) { } } // namespace mlir - // auto arch = std::make_shared("RigettiNovera", 9, COUPLING); \ No newline at end of file + const static qco::Architecture::CouplingSet COUPLING{ \ No newline at end of file From cc46aa1a4d6f03168bd5e7952f09c3fedb894fc1 Mon Sep 17 00:00:00 2001 From: Johannes Moosburger Date: Thu, 30 Apr 2026 19:25:53 +0200 Subject: [PATCH 28/29] revert CompilerPipeline --- CHANGELOG.md | 2 +- mlir/lib/Compiler/CompilerPipeline.cpp | 1 - .../QCO/Transforms/Mapping/Mapping.cpp | 4 ++-- .../test_qco_merge_single_qubit_rotation.cpp | 20 ++++++++++--------- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc7afe0625..bf954c9e9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Fixed -- 🐛 Fix malformed include directories in exported `nlohmann_json` CMake targets for component-based installs ([#1662]) ([**@burgholzer**]) +- � Fix malformed include directories in exported `nlohmann_json` CMake targets for component-based installs ([#1662]) ([**@burgholzer**]) ## [3.5.0] - 2026-04-21 diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index f30e4675bf..06eb87bd89 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -236,4 +236,3 @@ std::string captureIR(ModuleOp module) { } } // namespace mlir - const static qco::Architecture::CouplingSet COUPLING{ \ No newline at end of file diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp index 73993e2d8b..a3482f50bb 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp @@ -822,8 +822,8 @@ struct MappingPass : impl::MappingPassBase { rewriter.setInsertionPoint(*anchorIt); for (const auto& [hw0, hw1] : *swapIt) { - const auto in0 = qubits.getQubit(hw0); - const auto in1 = qubits.getQubit(hw1); + const auto in0 = qubits.getHardwareQubit(hw0); + const auto in1 = qubits.getHardwareQubit(hw1); auto insertedOp = SWAPOp::create(rewriter, op->getLoc(), in0, in1); diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp index af398ea2b4..5ab06957f0 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp @@ -14,6 +14,7 @@ #include "mlir/Dialect/QCO/Transforms/Passes.h" #include +#include #include #include #include @@ -54,7 +55,7 @@ class MergeSingleQubitRotationGatesTest : public ::testing::Test { */ struct RotationGate { GateType type; - SmallVector angles; + llvm::SmallVector angles; }; MergeSingleQubitRotationGatesTest() : builder(&context) {} @@ -78,11 +79,12 @@ class MergeSingleQubitRotationGatesTest : public ::testing::Test { } /** - * @brief Extract constant floating point value from a Value + * @brief Extract constant floating point value from a mlir::Value */ - static std::optional toDouble(Value v) { - if (auto constOp = v.getDefiningOp()) { - if (auto floatAttr = dyn_cast(constOp.getValue())) { + static std::optional toDouble(mlir::Value v) { + if (auto constOp = v.getDefiningOp()) { + if (auto floatAttr = + mlir::dyn_cast(constOp.getValue())) { return floatAttr.getValueAsDouble(); } } @@ -99,7 +101,7 @@ class MergeSingleQubitRotationGatesTest : public ::testing::Test { module->walk([&](UOp op) { uOp = op; // stop after finding first UOp - return WalkResult::interrupt(); + return mlir::WalkResult::interrupt(); }); if (!uOp) { @@ -140,7 +142,7 @@ class MergeSingleQubitRotationGatesTest : public ::testing::Test { GPhaseOp gOp = nullptr; module->walk([&](GPhaseOp op) { gOp = op; - return WalkResult::interrupt(); + return mlir::WalkResult::interrupt(); }); if (!gOp) { @@ -160,7 +162,7 @@ class MergeSingleQubitRotationGatesTest : public ::testing::Test { EXPECT_NEAR(*param, expected, tolerance); } - Value buildRotations(ArrayRef rotations, Value& q) { + Value buildRotations(llvm::ArrayRef rotations, Value& q) { auto qubit = q; for (const auto& gate : rotations) { @@ -205,7 +207,7 @@ class MergeSingleQubitRotationGatesTest : public ::testing::Test { * builder api to build a small quantum circuit, where a qubit is fed through * all rotations in the list. */ - LogicalResult testGateMerge(ArrayRef rotations) { + LogicalResult testGateMerge(llvm::ArrayRef rotations) { auto q = builder.allocQubitRegister(1); buildRotations(rotations, q[0]); From 7690f33f1ba90b4d239d5fe9465bbf90d6b4f049 Mon Sep 17 00:00:00 2001 From: Johannes Moosburger Date: Thu, 30 Apr 2026 22:08:34 +0200 Subject: [PATCH 29/29] test_swap_absorb PassDoesNotChangeSwaplessProgram --- .../QCO/Transforms/Mapping/CMakeLists.txt | 2 +- .../Transforms/Mapping/test_swap_absorb.cpp | 94 +++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 mlir/unittests/Dialect/QCO/Transforms/Mapping/test_swap_absorb.cpp diff --git a/mlir/unittests/Dialect/QCO/Transforms/Mapping/CMakeLists.txt b/mlir/unittests/Dialect/QCO/Transforms/Mapping/CMakeLists.txt index 405159075a..80b9c7720d 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Mapping/CMakeLists.txt +++ b/mlir/unittests/Dialect/QCO/Transforms/Mapping/CMakeLists.txt @@ -7,7 +7,7 @@ # Licensed under the MIT License set(target_name mqt-core-mlir-unittest-mapping) -add_executable(${target_name} test_mapping.cpp) +add_executable(${target_name} test_mapping.cpp test_swap_absorb.cpp) target_link_libraries( ${target_name} diff --git a/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_swap_absorb.cpp b/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_swap_absorb.cpp new file mode 100644 index 0000000000..27cd788f9f --- /dev/null +++ b/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_swap_absorb.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + + #include "mlir/Conversion/QCOToQC/QCOToQC.h" +#include "mlir/Conversion/QCToQCO/QCToQCO.h" +#include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" +#include "mlir/Dialect/QC/IR/QCDialect.h" +#include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +using namespace mlir; +using namespace mlir::qco; + +namespace { + +class SwapAbsorbPassTest : public testing::Test{ + + +protected: + void SetUp() override { + // Register all necessary dialects + DialectRegistry registry; + registry.insert(); + context = std::make_unique(); + context->appendDialectRegistry(registry); + context->loadAllAvailableDialects(); + } + + static void applySwapAbsorb(OwningOpRef& moduleOp) { + PassManager pm(moduleOp->getContext()); + pm.addPass(createQCToQCO()); + pm.addPass(qco::createSwapAbsorb()); + pm.addPass(createQCOToQC()); + auto res = pm.run(*moduleOp); + + ASSERT_TRUE(succeeded(res)); + } + + std::unique_ptr context; +}; +}; // namespace + + + +TEST_F(SwapAbsorbPassTest, PassDoesNotChangeSwaplessProgram) { + + qc::QCProgramBuilder builder(context.get()); + builder.initialize(); + + const auto q0 = builder.allocQubit(); + const auto q1 = builder.allocQubit(); + const auto q2 = builder.allocQubit(); + + builder.h(q0); + builder.cx(q0, q1); + + builder.dealloc(q0); + builder.dealloc(q1); + + auto moduleThroughPass = builder.finalize(); + auto originalModule = moduleThroughPass->clone(); + + applySwapAbsorb(moduleThroughPass); + ASSERT_TRUE(mlir::OperationEquivalence::isEquivalentTo( + moduleThroughPass.get(), + originalModule, + mlir::OperationEquivalence::Flags::IgnoreLocations)); +} \ No newline at end of file