diff --git a/.agent/plans/mqt-shared-infrastructure.md b/.agent/plans/mqt-shared-infrastructure.md new file mode 100644 index 0000000000..3c589ad3e7 --- /dev/null +++ b/.agent/plans/mqt-shared-infrastructure.md @@ -0,0 +1,283 @@ +# Consolidate shared MQT compiler infrastructure + +This ExecPlan is a living document. The sections `Progress`, +`Surprises & Discoveries`, `Decision Log`, and `Outcomes & Retrospective` must +be kept up to date as work proceeds. + +This ExecPlan must be maintained in accordance with `.agent/PLANS.md` from the +repository root. + +## Purpose / Big Picture + +The compiler collection currently stores MQT-owned passes and quantum-specific +helpers below the broad path `mlir/Dialect/Utils`. That path overlaps MLIR's own +shared dialect utilities and obscures which code owns each semantic contract. +After this change, MQT-owned cross-dialect transformations and IR-aware quantum +helpers live below `mlir/Dialect/MQT`, and the quantum-aware module equivalence +checker is built only for tests. The utility implementations form one leaf +`MLIRMQTUtils` library. Existing pass command names and behavior remain +unchanged. + +The result is observable by building the same compiler and tests with no source +file below the project-owned `mlir/Dialect/Utils` directory. The generated MQT +documentation lists both the MQT metadata dialect and its cross-dialect passes. + +## Progress + +- [x] (2026-08-20 15:13Z) Create a dedicated worktree and branch from the + current head of the symbolic Qiskit parameter pull request. +- [x] (2026-08-20 15:13Z) Audit the shared transform, utility, verifier, test, + and build dependencies. +- [x] (2026-08-20 15:53Z) Move the existing MQT transform package and its + normalization tests. +- [x] (2026-08-20 15:53Z) Split quantum semantics and generic folding helpers by + owner. +- [x] (2026-08-20 15:53Z) Move the module equivalence checker into test support. +- [x] (2026-08-20 15:53Z) Update all includes, namespaces, CMake targets, and + documentation. +- [x] (2026-08-20 15:53Z) Run focused tests, the release build, the + documentation build, and lint. +- [x] (2026-08-20 15:53Z) Inspect the final diff and prepare signed, scoped + local commits. +- [x] (2026-08-20 17:30Z) Rebase the two follow-up commits onto the merged + symbolic Qiskit parameter change and repeat the publication checks. +- [x] (2026-08-20 19:30Z) Place MQT-owned folding support in `mlir::mqt` below + `mlir/Support/MQT` and address all review comments. +- [x] (2026-08-20 19:30Z) Repeat the release and non-unity builds, focused and + full tests, repository lint, and the full pull request Clang-Tidy diff. +- [x] (2026-08-20 21:45Z) Replace the installed support header and header-only + utility set with a cohesive `Dialect/MQT/Utils` package and leaf library. +- [x] (2026-08-20 22:20Z) Format the utility package, pass focused Clang-Tidy, + build the full release tree, and pass all 4,303 configured tests. +- [x] (2026-08-20 22:22Z) Inspect the final diff and prepare a signed local + commit for the utility package revision. + +## Surprises & Discoveries + +- Observation: The legacy transform package is already MQT-owned except for its + path. Its target is `MLIRMQTTransforms`, its generated pass group is `MQT`, + and its C++ namespace is `mlir::mqt`. Evidence: the files + `mlir/include/mlir/Dialect/MQT/Transforms/Passes.td` and + `mlir/lib/Dialect/Utils/Transforms/CMakeLists.txt`. +- Observation: The project adds custom headers to MLIR's existing + `mlir/Dialect/Utils` include hierarchy, which also supplies upstream headers + such as `StaticValueUtils.h`. A clean split must remove only project-owned + files and must continue using upstream headers from that path. +- Observation: `mlir/Support/IRVerification` is production-built but every + caller is a unit test. The implementation directly depends on QC, QCO, + QTensor, SCF, and LLVM IR details, so it is test infrastructure rather than a + generic production verifier. +- Observation: Exposing support helpers through `mlir::mqt` revealed unqualified + references to the repository's top-level `mqt::test` namespace in unit tests. + Root-qualifying those references as `::mqt::test` removes the ambiguity and + keeps the support helpers in their owning namespace. +- Observation: Pull request #2189 merged while this work was in progress and + expanded the global-phase tests. The follow-up branch was rebased onto the + updated pull request #2150 head, which already includes #2189. The moved test + retains those semantic checks. +- Observation: The release preset does not build the Python MLIR bindings. The + strict Sphinx build found stale Qiskit import and export includes after the + utility split. The corrected binding target and the full Sphinx build now + pass. +- Observation: Current upstream MLIR keeps `mlir/Support` independent of IR, + dialect, and interface libraries. IR-aware shared helpers instead use + dedicated utility libraries, such as `MLIRDialectUtils`, below the IR or + dialect hierarchy. +- Observation: `add_mlir_dialect_library` appends its target to + `MLIR_DIALECT_LIBS`. `MLIRMQTUtils` is not a dialect registration target and + must remain below the MQT, QC, and QCO dialect libraries, so + `add_mlir_library` describes its role and dependencies more accurately. + +## Decision Log + +- Decision: Keep `MLIRMQTDialect` and `MLIRMQTTransforms` as separate targets. + Rationale: an IR dialect library must not pull pass infrastructure and + cross-dialect rewrite dependencies into every metadata consumer. Date/Author: + 2026-08-20 / Codex. +- Decision: Move both `NormalizeGlobalPhases` and `UnrollModifiers` as one + transform package. Rationale: both passes already share the MQT namespace, + generated pass group, target, and QC/QCO ownership. Moving only one would + retain an artificial package split. Date/Author: 2026-08-20 / Codex. +- Decision: Preserve the pass arguments, target names, and dependent dialect + lists. Rationale: ownership does not change runtime behavior, and MLIR pass + dependencies describe dialect entities that a pass may create. The MQT + transforms create Arith, QC, and QCO entities but no MQT entities. + Date/Author: 2026-08-20 / Codex. +- Decision: Do not introduce a shared QC/QCO unitary operation interface. + Rationale: QC has reference semantics and QCO has value semantics. The user + explicitly excluded that abstraction from this refactor. Date/Author: + 2026-08-20 / Codex. +- Decision: Split the former `Utils.h` by semantic responsibility instead of + renaming the monolith. Rationale: a path move alone would preserve unclear + ownership and excessive include dependencies. Date/Author: 2026-08-20 / Codex. +- Decision: Do not preserve forwarding headers below the old project-owned + `mlir/Dialect/Utils` path. Rationale: the compiler collection and these APIs + are part of the unreleased general launch, and the requested cleanup should + remove the ambiguous project-owned include surface. Date/Author: 2026-08-20 / + Codex. +- Decision: Put all IR-aware shared helpers below `mlir/Dialect/MQT/Utils` in + `mlir::mqt`, including constant folding. Rationale: the helpers depend on MLIR + IR, dialects, or interfaces, so `mlir/Support` is the wrong dependency layer. + A named owner and a dedicated library match current MLIR organization without + creating loose headers or an umbrella include. Date/Author: 2026-08-20 / + Codex. +- Decision: Split the utility surface into `Angles`, `ConstantFolding`, + `DenseUnitary`, `GatePowering`, `Modifiers`, and `Parameters`. Rationale: each + header has one semantic responsibility and enough related declarations to + justify the file. `Math.h` and an umbrella `Utils.h` would hide those + contracts. Non-template implementations belong in matching source files. + Date/Author: 2026-08-20 / Codex. +- Decision: Build the helper package with `add_mlir_library(MLIRMQTUtils)` and + link only `MLIRArithDialect`, `MLIRIR`, and `MLIRSideEffectInterfaces`. + Rationale: the target is an IR-aware leaf library, not a dialect registration + library. It must not link the MQT, QC, or QCO dialects because those dialects + consume it. Date/Author: 2026-08-20 / Codex. +- Decision: Build the module equivalence checker in one concrete test-support + library and expose that library through `MLIRTestCaseUtils`. Rationale: every + caller is a unit test, while a single target avoids repeating its quantum + dialect dependencies across test directories. Date/Author: 2026-08-20 / Codex. + +## Outcomes & Retrospective + +The follow-up now has explicit ownership boundaries. MQT owns the cross-dialect +passes, IR-aware quantum semantics, and project-specific constant folding in a +dedicated utility library. Unit test support owns the module equivalence +checker. The implementation does not add a shared QC/QCO unitary interface. + +After pull request #2150 merged, the follow-up commits were rebased onto its +squash commit. The final review update passed the release build and the +non-unity debug build, including the Python MLIR binding. Five focused binaries +passed 322 tests. CTest reported 100% success across 4,301 configured tests; one +QDMI test was skipped by its own condition. The repository lint suite and the +full pull request Clang-Tidy diff passed. Generated MLIR documentation and the +strict Sphinx documentation build passed before the final review update, which +does not change generated documentation or Sphinx inputs. The signed commits are +ready for review. + +The utility package revision builds all six implementation files separately in +the non-unity lint configuration and builds the complete release tree. Focused +Clang-Tidy reports no project diagnostics across the six implementations and two +utility test files. The utility binary passes 20 tests. CTest reports 100% +success across all 4,303 configured tests; one QDMI test is skipped by its own +condition. Repository lint and the final diff check pass. + +## Context and Orientation + +MQT Core embeds an MLIR-based compiler collection below `mlir/`. QC represents +mutable qubit references. QCO represents linear qubit values. The MQT dialect, +defined in `mlir/include/mlir/Dialect/MQT/IR/MQTDialect.td`, owns metadata and +other infrastructure shared across those representations. + +The files below `mlir/include/mlir/Dialect/MQT/Transforms` and +`mlir/lib/Dialect/Utils/Transforms` define two module passes. Global-phase +normalization combines and moves `qc.gphase` and `qco.gphase` operations while +preserving modifier semantics. Modifier unrolling splits multi-operation QC and +QCO control, inverse, and power regions. Their library is already named +`MLIRMQTTransforms`. + +The MQT utility headers define angle and global-phase contracts, constant +folding, dense-unitary validation, gate powering, parameter handling, and +modifier-region rewrites. QC, QCO, builders, transforms, conversions, and the +Qiskit binding consume different subsets of those contracts. They need a shared +leaf target that does not depend on any consuming dialect. + +The function `areModulesEquivalentWithPermutations` is declared in +`mlir/include/mlir/Support/IRVerification.h`, implemented in +`mlir/lib/Support/IRVerification.cpp`, and called only by tests. The test root +already provides an interface target named `MLIRTestCaseUtils`; a new concrete +`MLIRTestSupport` target can carry the checker implementation without exposing +it through the installed `MLIRSupportMQT` library. + +## Plan of Work + +Move the transform headers, TableGen file, generated include locations, source +files, and CMake files to matching `Dialect/MQT/Transforms` directories. Update +all includes and parent CMake files. Move the global-phase normalization test to +`mlir/unittests/Dialect/MQT/Transforms`. Leave QC- and QCO-specific modifier +tests with their respective dialect tests. + +Create cohesive headers below `mlir/include/mlir/Dialect/MQT/Utils`: `Angles`, +`ConstantFolding`, `DenseUnitary`, `GatePowering`, `Modifiers`, and +`Parameters`. Do not add `Math.h` or an umbrella header. Put non-template +implementations in matching source files below `mlir/lib/Dialect/MQT/Utils`. +Build them as the leaf `MLIRMQTUtils` target and link direct consumers to it. +Keep the constant-folding tests with the utility package below +`mlir/unittests/Dialect/MQT/Utils`. + +Move the quantum module equivalence header and implementation below +`mlir/unittests/Support`. Build them in a test-only `MLIRTestSupport` target and +make `MLIRTestCaseUtils` expose that target to configured test executables. +Remove the implementation and its test-only dependencies from `MLIRSupportMQT`. +Update every test include. + +Remove the now-empty project-owned `Dialect/Utils` CMake subdirectories and +parent `add_subdirectory(Utils)` entries. Continue using upstream includes such +as `mlir/Dialect/Utils/StaticValueUtils.h`; those files are not part of this +repository and must not be changed. + +Extend `docs/mlir/MQT.md` with the generated MQT pass reference. Do not add a +changelog entry because the change reorganizes unreleased compiler collection +internals without changing supported behavior or command-line interfaces. + +## Concrete Steps + +Run all commands from the repository root. Use `git status --short` before each +edit batch. Apply source edits with `apply_patch`; use ordinary `mv` only for +pure file relocation, followed by explicit content patches. + +Configure and build the release preset: + + cmake --preset release + cmake --build --preset release + +During iteration, build and run the MQT transform, support, QC IR, QCO IR, and +compiler tests. The exact target names will be recorded after the CMake split. +Generate the pass and dialect documentation with: + + cmake --build --preset release --target mlir-doc + uvx nox --non-interactive -s docs + +Finish with: + + ctest --preset release + uvx nox -s lint + +Before each commit, inspect `git diff --check`, the staged diff, and the +complete commit message. Sign each commit and verify it with +`git verify-commit HEAD`. Do not push or create a pull request without separate +authorization. + +## Validation and Acceptance + +The repository contains no project-owned files below `mlir/Dialect/Utils`, and +no project source includes the removed custom paths. Includes of upstream MLIR +headers in that hierarchy remain valid. + +The textual pass names `normalize-global-phases` and `unroll-modifiers` still +parse and run. Their focused QC and QCO tests pass with unchanged semantic +expectations. QC and QCO reject the same invalid global-phase values through one +shared helper, and both unitary interfaces apply one shared finite-parameter +validator. Dense-unitary and gate-power tests pass from their new include +locations. Constant-folding tests pass from the MQT utility test suite. + +`MLIRMQTUtils` builds as an ordinary MLIR library. It links only MLIR Arith, IR, +and side-effect interfaces. It does not register as a dialect library and does +not link MQT, QC, or QCO. All direct consumers declare the utility target. + +The production `MLIRSupportMQT` target no longer compiles or installs the +quantum module equivalence checker. All existing tests that compare modules +still link and pass through `MLIRTestSupport`. + +The release build, configured CTest suite, generated MLIR documentation, full +Sphinx documentation, and repository lint suite complete successfully. The +worktree is clean after signed local commits, and no remote state changes are +made for this follow-up branch. + +## Idempotence and Recovery + +The include replacements, formatting, builds, and tests are repeatable. CMake +may retain stale generated include paths after the move; rerun +`cmake --preset release` before diagnosing missing generated files. If a move is +interrupted, use `git status --short` to identify source and destination files, +then complete the move without deleting unrelated work. Never reset the worktree +or discard changes from another task. diff --git a/bindings/mlir/CMakeLists.txt b/bindings/mlir/CMakeLists.txt index 0f1bbb6106..d21d2425fb 100644 --- a/bindings/mlir/CMakeLists.txt +++ b/bindings/mlir/CMakeLists.txt @@ -83,6 +83,7 @@ if(NOT TARGET ${TARGET_NAME}) MQTCompilerQDMIAdapter MQTCompilerPipeline MLIRMQTDialect + MLIRMQTUtils MLIRQCTranslationSupport) if(MQT_QISKIT_CAPI_CANDIDATE_VERSION) diff --git a/bindings/mlir/qiskit/QiskitExport.cpp b/bindings/mlir/qiskit/QiskitExport.cpp index f4c02e0c11..1dc865c452 100644 --- a/bindings/mlir/qiskit/QiskitExport.cpp +++ b/bindings/mlir/qiskit/QiskitExport.cpp @@ -18,11 +18,12 @@ #include "mlir/Dialect/CBit/IR/CBitDialect.h" #include "mlir/Dialect/CBit/IR/CBitOps.h" #include "mlir/Dialect/MQT/IR/MQTDialect.h" +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/IR/QCInterfaces.h" #include "mlir/Dialect/QC/IR/QCOps.h" #include "mlir/Dialect/QC/Translation/StandardGate.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -117,7 +118,7 @@ using ExportedParameters = llvm::DenseMap; if (++nodes > MAX_PARAMETER_EXPRESSION_NODES) { throwExportedParameterExpressionSizeError(); } - if (const auto number = mlir::utils::valueToDouble(value)) { + if (const auto number = mlir::mqt::valueToDouble(value)) { auto result = numberParameter(*number); parameters.try_emplace(value, result); return result; @@ -376,12 +377,13 @@ void addGlobalPhase(ExportState& state, const Parameter& phase) { state.globalPhase = Parameter::number(sum); return; } - if (std::abs(number->value) <= mlir::utils::TOLERANCE) { + if (std::abs(number->value) <= mlir::mqt::PARAMETER_COMPARISON_TOLERANCE) { return; } } else if (const auto* globalNumber = state.globalPhase.getNumber(); globalNumber != nullptr && - std::abs(globalNumber->value) <= mlir::utils::TOLERANCE) { + std::abs(globalNumber->value) <= + mlir::mqt::PARAMETER_COMPARISON_TOLERANCE) { state.globalPhase = phase; return; } diff --git a/bindings/mlir/qiskit/QiskitImport.cpp b/bindings/mlir/qiskit/QiskitImport.cpp index 98e406d692..6a91dfec8f 100644 --- a/bindings/mlir/qiskit/QiskitImport.cpp +++ b/bindings/mlir/qiskit/QiskitImport.cpp @@ -16,12 +16,12 @@ #include "mlir/Compiler/Programs.h" #include "mlir/Dialect/CBit/IR/CBitDialect.h" #include "mlir/Dialect/MQT/IR/MQTDialect.h" +#include "mlir/Dialect/MQT/Utils/DenseUnitary.h" #include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/Translation/StandardGate.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" -#include "mlir/Dialect/Utils/DenseUnitary.h" #include #include @@ -324,10 +324,10 @@ denseUnitaryArity(const Instruction& instruction) { "Qiskit unitary instruction has an unsupported operand arity"); } const auto targets = instruction.qubits.size() - controls; - if (targets > mlir::utils::MAX_DENSE_UNITARY_QUBITS) { + if (targets > mlir::mqt::MAX_DENSE_UNITARY_QUBITS) { throw std::runtime_error( "Qiskit unitary supports at most " + - std::to_string(mlir::utils::MAX_DENSE_UNITARY_QUBITS) + " qubits"); + std::to_string(mlir::mqt::MAX_DENSE_UNITARY_QUBITS) + " qubits"); } return {.controls = controls, .targets = targets}; } diff --git a/docs/mlir/MQT.md b/docs/mlir/MQT.md index 4f941ea488..bb6a43d64c 100644 --- a/docs/mlir/MQT.md +++ b/docs/mlir/MQT.md @@ -5,3 +5,9 @@ tocdepth: 3 ```{include} Dialects/MQTDialect.md ``` + +## Passes + +```{include} Passes/MQTTransforms.md + +``` diff --git a/mlir/include/mlir/Dialect/CMakeLists.txt b/mlir/include/mlir/Dialect/CMakeLists.txt index 04bd1922f4..af4c93346b 100644 --- a/mlir/include/mlir/Dialect/CMakeLists.txt +++ b/mlir/include/mlir/Dialect/CMakeLists.txt @@ -12,4 +12,3 @@ add_subdirectory(QC) add_subdirectory(QCO) add_subdirectory(QIR) add_subdirectory(QTensor) -add_subdirectory(Utils) diff --git a/mlir/include/mlir/Dialect/MQT/CMakeLists.txt b/mlir/include/mlir/Dialect/MQT/CMakeLists.txt index b181a84fed..3b0a561d0f 100644 --- a/mlir/include/mlir/Dialect/MQT/CMakeLists.txt +++ b/mlir/include/mlir/Dialect/MQT/CMakeLists.txt @@ -7,3 +7,4 @@ # Licensed under the MIT License add_subdirectory(IR) +add_subdirectory(Transforms) diff --git a/mlir/include/mlir/Dialect/Utils/Transforms/CMakeLists.txt b/mlir/include/mlir/Dialect/MQT/Transforms/CMakeLists.txt similarity index 100% rename from mlir/include/mlir/Dialect/Utils/Transforms/CMakeLists.txt rename to mlir/include/mlir/Dialect/MQT/Transforms/CMakeLists.txt diff --git a/mlir/include/mlir/Dialect/Utils/Transforms/GlobalPhaseNormalization.h b/mlir/include/mlir/Dialect/MQT/Transforms/GlobalPhaseNormalization.h similarity index 100% rename from mlir/include/mlir/Dialect/Utils/Transforms/GlobalPhaseNormalization.h rename to mlir/include/mlir/Dialect/MQT/Transforms/GlobalPhaseNormalization.h diff --git a/mlir/include/mlir/Dialect/Utils/Transforms/Passes.h b/mlir/include/mlir/Dialect/MQT/Transforms/Passes.h similarity index 72% rename from mlir/include/mlir/Dialect/Utils/Transforms/Passes.h rename to mlir/include/mlir/Dialect/MQT/Transforms/Passes.h index 8f5607afc1..1236cbb470 100644 --- a/mlir/include/mlir/Dialect/Utils/Transforms/Passes.h +++ b/mlir/include/mlir/Dialect/MQT/Transforms/Passes.h @@ -16,9 +16,9 @@ namespace mlir::mqt { #define GEN_PASS_DECL -#include "mlir/Dialect/Utils/Transforms/Passes.h.inc" // IWYU pragma: export +#include "mlir/Dialect/MQT/Transforms/Passes.h.inc" // IWYU pragma: export #define GEN_PASS_REGISTRATION -#include "mlir/Dialect/Utils/Transforms/Passes.h.inc" // IWYU pragma: export +#include "mlir/Dialect/MQT/Transforms/Passes.h.inc" // IWYU pragma: export } // namespace mlir::mqt diff --git a/mlir/include/mlir/Dialect/Utils/Transforms/Passes.td b/mlir/include/mlir/Dialect/MQT/Transforms/Passes.td similarity index 94% rename from mlir/include/mlir/Dialect/Utils/Transforms/Passes.td rename to mlir/include/mlir/Dialect/MQT/Transforms/Passes.td index 7dfb697285..5b4685d8b4 100644 --- a/mlir/include/mlir/Dialect/Utils/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/MQT/Transforms/Passes.td @@ -6,8 +6,8 @@ // // Licensed under the MIT License -#ifndef MLIR_DIALECT_UTILS_TRANSFORMS_PASSES_TD -#define MLIR_DIALECT_UTILS_TRANSFORMS_PASSES_TD +#ifndef MLIR_DIALECT_MQT_TRANSFORMS_PASSES_TD +#define MLIR_DIALECT_MQT_TRANSFORMS_PASSES_TD include "mlir/Pass/PassBase.td" @@ -55,4 +55,4 @@ def UnrollModifiers : Pass<"unroll-modifiers", "mlir::ModuleOp"> { }]; } -#endif // MLIR_DIALECT_UTILS_TRANSFORMS_PASSES_TD +#endif // MLIR_DIALECT_MQT_TRANSFORMS_PASSES_TD diff --git a/mlir/include/mlir/Dialect/MQT/Utils/Angles.h b/mlir/include/mlir/Dialect/MQT/Utils/Angles.h new file mode 100644 index 0000000000..d9e941aa8d --- /dev/null +++ b/mlir/include/mlir/Dialect/MQT/Utils/Angles.h @@ -0,0 +1,32 @@ +/* + * 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 +#include +#include + +namespace mlir::mqt { + +/// Largest supported magnitude of a global-phase angle in radians. +inline constexpr double MAX_GLOBAL_PHASE_ANGLE = 1.0e4; + +/// Normalize an angle to (-pi, pi]. +[[nodiscard]] double normalizeAngle(double theta); + +/// Check the compiler-wide global-phase angle contract. +[[nodiscard]] bool isValidGlobalPhaseAngle(double theta); + +/// Verify the compiler-wide global-phase angle contract. +[[nodiscard]] LogicalResult verifyGlobalPhaseAngle(Operation* operation, + Value angle); + +} // namespace mlir::mqt diff --git a/mlir/include/mlir/Dialect/MQT/Utils/ConstantFolding.h b/mlir/include/mlir/Dialect/MQT/Utils/ConstantFolding.h new file mode 100644 index 0000000000..52187ad578 --- /dev/null +++ b/mlir/include/mlir/Dialect/MQT/Utils/ConstantFolding.h @@ -0,0 +1,46 @@ +/* + * 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 +#include +#include + +#include + +namespace mlir::mqt { + +/// Convert a floating-point or integer attribute to a double. +[[nodiscard]] std::optional attributeToDouble(Attribute attr); + +/// Convert a direct arithmetic constant to a double. +[[nodiscard]] std::optional valueToDouble(Value value); + +/** + * Recursively constant-fold a pure SSA expression DAG to an attribute. + * + * The cache memoizes successful and failed evaluations so shared operands are + * resolved once. + * + * @param value SSA value to evaluate. + * @param cache Evaluation results indexed by SSA value. + */ +[[nodiscard]] std::optional +valueToConstantAttr(Value value, + DenseMap>& cache); + +/// Recursively constant-fold a pure SSA expression DAG to an attribute. +[[nodiscard]] std::optional valueToConstantAttr(Value value); + +/// Recursively constant-fold a pure SSA expression DAG to a double. +[[nodiscard]] std::optional valueToConstantDouble(Value value); + +} // namespace mlir::mqt diff --git a/mlir/include/mlir/Dialect/MQT/Utils/DenseUnitary.h b/mlir/include/mlir/Dialect/MQT/Utils/DenseUnitary.h new file mode 100644 index 0000000000..6742f49375 --- /dev/null +++ b/mlir/include/mlir/Dialect/MQT/Utils/DenseUnitary.h @@ -0,0 +1,42 @@ +/* + * 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 +#include +#include +#include + +#include + +namespace mlir::mqt { + +/** + * Maximum absolute entry-wise deviation of U^dagger U from the identity. + * + * This tolerance accounts for binary64 accumulation error while remaining + * substantially below the precision at which dense input matrices are + * normally specified. + */ +inline constexpr double DENSE_UNITARY_TOLERANCE = 1e-10; + +/** Maximum matrix arity accepted by the deterministic unitarity verifier. */ +inline constexpr size_t MAX_DENSE_UNITARY_QUBITS = 8; + +/** Verify the common dense-matrix contract of QC and QCO unitary operations. */ +[[nodiscard]] LogicalResult verifyDenseUnitaryMatrix(Operation* operation, + ElementsAttr matrixAttr, + ValueRange qubits); + +/** Return whether a dense square matrix is exactly the identity. */ +[[nodiscard]] bool isExactIdentityMatrix(ElementsAttr matrixAttr); + +} // namespace mlir::mqt diff --git a/mlir/include/mlir/Dialect/MQT/Utils/GatePowering.h b/mlir/include/mlir/Dialect/MQT/Utils/GatePowering.h new file mode 100644 index 0000000000..12bd01fcb5 --- /dev/null +++ b/mlir/include/mlir/Dialect/MQT/Utils/GatePowering.h @@ -0,0 +1,61 @@ +/* + * 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 +#include + +namespace mlir::mqt { + +/** + * Maximum exponent considered for safe binary64 U-gate powering. + * + * Even with analytical SU(2) powering, uncertainty in the input angles is + * magnified by the exponent. Candidate rewrites are additionally checked + * against the source matrix before they are accepted. + */ +inline constexpr uint64_t MAX_SAFE_U_POWER_EXPONENT = 1024U; + +/// Maximum entry-wise matrix error accepted for a powered U-gate rewrite. +inline constexpr double U_POWER_EQUIVALENCE_TOLERANCE = 5e-13; + +/** + * @brief Parameters representing a powered U gate. + * + * All values are in radians. The phase satisfies + * `U(input)^exponent = exp(i * phase) * U(theta, phi, lambda)`. + */ +struct UPowerParameters { + double theta; ///< Resulting U rotation angle. + double phi; ///< Resulting U phi angle. + double lambda; ///< Resulting U lambda angle. + double phase; ///< Remaining global phase. +}; + +/// Check whether a floating-point exponent is an integer. +[[nodiscard]] bool isIntegerExponent(double value); + +/// Check whether a floating-point exponent is an even integer. +[[nodiscard]] bool isEvenExponent(double value); + +/** + * @brief Compute a positive integral power of a constant U gate. + * + * @return Parameters satisfying + * `U(theta, phi, lambda)^exponent = exp(i*phase) * U(result)`, or + * `std::nullopt` if @p exponent is not a positive integer no greater than + * `MAX_SAFE_U_POWER_EXPONENT`, an input is not finite, or the binary64 result + * cannot be reconstructed within `U_POWER_EQUIVALENCE_TOLERANCE`. + */ +[[nodiscard]] std::optional +powerUParameters(double theta, double phi, double lambda, double exponent); + +} // namespace mlir::mqt diff --git a/mlir/include/mlir/Dialect/MQT/Utils/Modifiers.h b/mlir/include/mlir/Dialect/MQT/Utils/Modifiers.h new file mode 100644 index 0000000000..61e13953c8 --- /dev/null +++ b/mlir/include/mlir/Dialect/MQT/Utils/Modifiers.h @@ -0,0 +1,138 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace mlir::mqt { + +/// Populate a modifier region and invoke @p emitBody. +template +inline void +buildModifierBody(OpBuilder& builder, OperationState& state, + const size_t numBlockArgs, + const function_ref& emitBody) { + auto& block = state.regions.front()->emplaceBlock(); + auto qubitType = QubitType::get(builder.getContext()); + for (size_t i = 0; i < numBlockArgs; ++i) { + block.addArgument(qubitType, state.location); + } + + const OpBuilder::InsertionGuard guard(builder); + builder.setInsertionPointToStart(&block); + emitBody(builder, block); +} + +/// Parse a modifier's block arguments and aliased qubit operands. +template +[[nodiscard]] ParseResult +parseTargetAliasing(OpAsmParser& parser, Region& region, + SmallVectorImpl& operands) { + if (parser.parseLParen()) { + return failure(); + } + + SmallVector blockArgs; + if (failed(parser.parseOptionalRParen())) { + do { + OpAsmParser::Argument newArg; + OpAsmParser::UnresolvedOperand oldOperand; + if (parser.parseArgument(newArg) || parser.parseEqual() || + parser.parseOperand(oldOperand)) { + return failure(); + } + operands.push_back(oldOperand); + newArg.type = QubitType::get(parser.getBuilder().getContext()); + blockArgs.push_back(newArg); + } while (succeeded(parser.parseOptionalComma())); + + if (parser.parseRParen()) { + return failure(); + } + } + + return parser.parseRegion(region, blockArgs); +} + +/// Print a modifier's block arguments and aliased qubit operands. +void printTargetAliasing(OpAsmPrinter& printer, Region& region, + OperandRange targetsIn); + +/// Resolve a modifier block argument to the corresponding outer value. +[[nodiscard]] Value getValueFromBlockArgument(Value qubit, ValueRange qubits); + +/// Return the number of operations implementing @p UnitaryInterface. +template +[[nodiscard]] size_t getNumBodyUnitaries(Block& block) { + return static_cast(llvm::count_if( + block, [](Operation& op) { return isa(op); })); +} + +/// Return the indexed body unitary or report an invalid API use. +template +[[nodiscard]] UnitaryInterface getBodyUnitary(Block& block, + const size_t index) { + auto unitaries = llvm::make_filter_range( + block, [](Operation& op) { return isa(op); }); + auto it = std::next(unitaries.begin(), static_cast(index)); + if (it == unitaries.end()) { + llvm::reportFatalUsageError("Unitary index out of bounds"); + } + return cast(*it); +} + +/// Return the sole body unitary, or a null interface if there is not one. +template +[[nodiscard]] UnitaryInterface getSoleBodyUnitary(Block& block) { + auto unitaries = llvm::make_filter_range( + block, [](Operation& op) { return isa(op); }); + auto it = unitaries.begin(); + if (it == unitaries.end()) { + return {}; + } + auto unitary = cast(*it); + if (++it != unitaries.end()) { + return {}; + } + return unitary; +} + +/// Move a modifier body's support operations before @p target. +void hoistSupportingOpsBefore(Block& body, Operation* keep, Operation* target, + RewriterBase& rewriter); + +/// Inline a modifier body and replace the modifier with the yielded values. +void inlineModifierBody(Operation* operation, Block& body, + ValueRange blockArgReplacements, + RewriterBase& rewriter); + +/// Inline @p source into the current block and return its yielded values. +[[nodiscard]] SmallVector +inlineBodyReturningYields(Block& source, ValueRange blockArgReplacements, + RewriterBase& rewriter); + +} // namespace mlir::mqt diff --git a/mlir/include/mlir/Dialect/MQT/Utils/Parameters.h b/mlir/include/mlir/Dialect/MQT/Utils/Parameters.h new file mode 100644 index 0000000000..e1c6108bfd --- /dev/null +++ b/mlir/include/mlir/Dialect/MQT/Utils/Parameters.h @@ -0,0 +1,54 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include +#include + +namespace mlir::mqt { + +/// Absolute tolerance used when comparing static operation parameters. +inline constexpr double PARAMETER_COMPARISON_TOLERANCE = 1e-15; + +/// Materialize a scalar as an arithmetic constant. +[[nodiscard]] Value constantFromScalar(OpBuilder& builder, Location loc, + double value); + +/// Materialize a scalar as an arithmetic constant. +[[nodiscard]] Value constantFromScalar(OpBuilder& builder, Location loc, + int64_t value); + +/// Materialize a scalar as an arithmetic constant. +[[nodiscard]] Value constantFromScalar(OpBuilder& builder, Location loc, + bool value); + +/// Convert a scalar or existing SSA value to an SSA value. +template +[[nodiscard]] Value variantToValue(OpBuilder& builder, Location loc, + const std::variant& parameter) { + if (const auto* value = std::get_if(¶meter)) { + return *value; + } + return constantFromScalar(builder, loc, std::get(parameter)); +} + +/// Verify that each statically known floating-point parameter is finite. +[[nodiscard]] LogicalResult +verifyFiniteConstantParameters(Operation* operation, ValueRange parameters); + +} // namespace mlir::mqt diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h index 7c0ea4fab2..1ec9793681 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h @@ -10,7 +10,7 @@ #pragma once -#include "mlir/Dialect/Utils/Utils.h" +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" #include #include @@ -132,7 +132,7 @@ template class TargetAndParameterArityTrait { return true; } else { return llvm::all_of(this->getParameters(), [](Value param) { - return utils::valueToDouble(param).has_value(); + return mlir::mqt::valueToDouble(param).has_value(); }); } } diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index bbd8eac1e4..412e2ca691 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -10,12 +10,13 @@ #pragma once +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" #include #include -#include #include #include #include @@ -74,7 +75,7 @@ inline constexpr size_t kMaxModifierTargetQubits = 10; * * @details * Identical SSA values always match. Otherwise, if both are constants, they - * are compared with @ref utils::TOLERANCE. + * are compared with @ref mqt::PARAMETER_COMPARISON_TOLERANCE. * * @param lhs The first parameter value. * @param rhs The second parameter value. @@ -84,9 +85,10 @@ static bool valuesMatchWithinTolerance(Value lhs, Value rhs) { if (lhs == rhs) { return true; } - const auto lhsVal = utils::valueToDouble(lhs); - const auto rhsVal = utils::valueToDouble(rhs); - return lhsVal && rhsVal && std::abs(*lhsVal - *rhsVal) <= utils::TOLERANCE; + const auto lhsVal = mlir::mqt::valueToDouble(lhs); + const auto rhsVal = mlir::mqt::valueToDouble(rhs); + return lhsVal && rhsVal && + std::abs(*lhsVal - *rhsVal) <= mqt::PARAMETER_COMPARISON_TOLERANCE; } /** diff --git a/mlir/include/mlir/Dialect/Utils/CMakeLists.txt b/mlir/include/mlir/Dialect/Utils/CMakeLists.txt deleted file mode 100644 index 3c339729a9..0000000000 --- a/mlir/include/mlir/Dialect/Utils/CMakeLists.txt +++ /dev/null @@ -1,9 +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 - -add_subdirectory(Transforms) diff --git a/mlir/include/mlir/Dialect/Utils/Utils.h b/mlir/include/mlir/Dialect/Utils/Utils.h deleted file mode 100644 index ed7afbd38d..0000000000 --- a/mlir/include/mlir/Dialect/Utils/Utils.h +++ /dev/null @@ -1,495 +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 - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace mlir::utils { - -/// Check if a floating-point value is an integer. -[[nodiscard]] inline bool isIntegerExponent(double r) { - return r == std::floor(r) && std::isfinite(r); -} - -/// Check if a floating-point value is an even integer. -/// Uses fmod to avoid UB from narrowing to int64_t for large values. -[[nodiscard]] inline bool isEvenExponent(double r) { - return std::fmod(std::fabs(r), 2.0) == 0.0; -} - -/// Normalize an angle to (-π, π]. -[[nodiscard]] inline double normalizeAngle(double theta) { - const double twoPi = 2.0 * std::numbers::pi; - theta = std::fmod(theta, twoPi); - if (theta > std::numbers::pi) { - theta -= twoPi; - } - if (theta <= -std::numbers::pi) { - theta += twoPi; - } - return theta; -} - -/// Default absolute tolerance for MLIR dialect numerics (angle wrapping, -/// phase-zero checks). -constexpr auto TOLERANCE = 1e-15; - -/// Largest supported magnitude of a global-phase angle in radians. -/// -/// Keeping phase angles in this generous practical range makes binary64 angle -/// reduction accurate enough for exact-unitary compiler rewrites. -constexpr double MAX_GLOBAL_PHASE_ANGLE = 1.0e4; - -/// Check the compiler-wide global-phase angle contract. -[[nodiscard]] inline bool isValidGlobalPhaseAngle(const double theta) { - return std::isfinite(theta) && std::abs(theta) <= MAX_GLOBAL_PHASE_ANGLE; -} - -inline Value constantFromScalar(OpBuilder& builder, Location loc, double v) { - return arith::ConstantOp::create(builder, loc, builder.getF64FloatAttr(v)); -} - -inline Value constantFromScalar(OpBuilder& builder, Location loc, int64_t v) { - return arith::ConstantOp::create(builder, loc, builder.getIndexAttr(v)); -} - -inline Value constantFromScalar(OpBuilder& builder, Location loc, bool v) { - return arith::ConstantOp::create(builder, loc, builder.getBoolAttr(v)); -} - -/// Populate a modifier region with @p numBlockArgs qubits and invoke @p -/// emitBody. -template -inline void -buildModifierBody(OpBuilder& builder, OperationState& state, - const size_t numBlockArgs, - const function_ref& emitBody) { - auto& block = state.regions.front()->emplaceBlock(); - const auto qubitType = QubitType::get(builder.getContext()); - for (size_t i = 0; i < numBlockArgs; ++i) { - block.addArgument(qubitType, state.location); - } - - const OpBuilder::InsertionGuard guard(builder); - builder.setInsertionPointToStart(&block); - emitBody(builder, block); -} - -/** - * @brief Convert a variant parameter (T or Value) to a Value. - * - * @param builder The operation builder. - * @param loc The location of the operation. - * @param parameter The parameter as a variant (T or Value). - * @return Value The parameter as a Value. - */ -template -[[nodiscard]] inline Value -variantToValue(OpBuilder& builder, Location loc, - const std::variant& parameter) { - if (const auto* value = std::get_if(¶meter)) { - return *value; - } - return constantFromScalar(builder, loc, std::get(parameter)); -} - -[[nodiscard]] inline std::optional attributeToDouble(Attribute attr) { - if (auto floatAttr = dyn_cast(attr)) { - return floatAttr.getValueAsDouble(); - } - if (auto intAttr = dyn_cast(attr)) { - const bool isSigned = !intAttr.getType().isUnsignedInteger(); - APFloat apf(APFloat::IEEEdouble(), APInt::getZero(64)); - apf.convertFromAPInt(intAttr.getValue(), isSigned, - APFloat::rmNearestTiesToEven); - return apf.convertToDouble(); - } - return std::nullopt; -} - -/** - * @brief Try to convert a mlir::Value to a standard C++ double - * - * @details - * Resolving the mlir::Value will only work if it is a static value, so a value - * defined via a "arith.constant" operation. It must also be of type - * float or integer. - */ -[[nodiscard]] inline std::optional valueToDouble(Value value) { - auto constantOp = value.getDefiningOp(); - if (!constantOp) { - return std::nullopt; - } - return attributeToDouble(constantOp.getValue()); -} - -/// Recursively constant-fold a pure SSA expression DAG to an attribute. -/// -/// @p cache memoizes both successful and failed evaluations so shared SSA -/// operands are resolved once (linear in the expression DAG). -[[nodiscard]] inline std::optional -valueToConstantAttr(Value value, - DenseMap>& cache) { - if (const auto it = cache.find(value); it != cache.end()) { - return it->second; - } - - Attribute attr; - if (matchPattern(value, m_Constant(&attr))) { - return cache[value] = attr; - } - - Operation* op = value.getDefiningOp(); - if (op == nullptr || op->getNumRegions() != 0 || !isPure(op)) { - return cache[value] = std::nullopt; - } - - SmallVector operands; - operands.reserve(op->getNumOperands()); - for (const Value operand : op->getOperands()) { - const auto folded = valueToConstantAttr(operand, cache); - if (!folded) { - return cache[value] = std::nullopt; - } - operands.push_back(*folded); - } - - SmallVector results; - if (failed(op->fold(operands, results)) || results.size() != 1) { - return cache[value] = std::nullopt; - } - std::optional folded; - if (const auto resultAttr = dyn_cast_if_present(results.front())) { - folded = resultAttr; - } else if (const auto resultValue = - dyn_cast_if_present(results.front())) { - // Identity-style folds may return an existing SSA value (e.g. `addf x, - // -0`). - folded = valueToConstantAttr(resultValue, cache); - } - return cache[value] = folded; -} - -/// Recursively constant-fold a pure SSA expression DAG to an attribute. -[[nodiscard]] inline std::optional valueToConstantAttr(Value value) { - DenseMap> cache; - return valueToConstantAttr(value, cache); -} - -/// Recursively constant-fold a pure SSA expression tree to a double. -/// -/// Used by global-phase normalization so merged phases stay inside the -/// practical `gphase` angle contract instead of emitting long add-chains that -/// later constant-fold past `MAX_GLOBAL_PHASE_ANGLE`. -[[nodiscard]] inline std::optional valueToConstantDouble(Value value) { - if (const auto attr = valueToConstantAttr(value)) { - return attributeToDouble(*attr); - } - return std::nullopt; -} - -/// Verify that each statically known floating-point value in a parameter -/// expression is finite. -[[nodiscard]] inline LogicalResult -verifyFiniteConstantParameters(Operation* op, const ValueRange parameters) { - DenseMap> constantCache; - DenseSet visited; - for (const auto [index, parameter] : llvm::enumerate(parameters)) { - SmallVector worklist{parameter}; - while (!worklist.empty()) { - const Value value = worklist.pop_back_val(); - if (!visited.insert(value).second) { - continue; - } - if (const auto constant = valueToConstantAttr(value, constantCache)) { - if (const auto floating = dyn_cast(*constant); - floating && !floating.getValue().isFinite()) { - return op->emitOpError() << "constant parameter expression at index " - << index << " must be finite"; - } - } - Operation* definingOp = value.getDefiningOp(); - if (definingOp == nullptr || definingOp->getNumRegions() != 0 || - !isPure(definingOp)) { - continue; - } - llvm::append_range(worklist, definingOp->getOperands()); - } - } - return success(); -} - -/** - * @brief Parse a list of aliased qubits. - * - * @details - * The modifier operations use aliased qubits inside of their region. This - * function resolves the relationship between the block arguments and the qubit - * operands. In the example below, the block argument `%a0` aliases the operand - * `%q1`. - * - * ```mlir - * qc.ctrl(%q0) targets(%a0 = %q1) { - * qc.x %a0 : !qc.qubit - * } : !qc.qubit - * ``` - */ -template -[[nodiscard]] -ParseResult -parseTargetAliasing(OpAsmParser& parser, Region& region, - SmallVectorImpl& operands) { - // 1. Parse the opening parenthesis - if (parser.parseLParen()) { - return failure(); - } - - // Temporary storage for block arguments we are about to create - SmallVector blockArgs; - - // 2. Prepare to parse the list - if (failed(parser.parseOptionalRParen())) { - do { - OpAsmParser::Argument newArg; // The "new" variable name - OpAsmParser::UnresolvedOperand oldOperand; // The "old" input variable - - // Parse "%new" - if (parser.parseArgument(newArg)) { - return failure(); - } - - // Parse "=" - if (parser.parseEqual()) { - return failure(); - } - - // Parse "%old" - if (parser.parseOperand(oldOperand)) { - return failure(); - } - operands.push_back(oldOperand); - - // Hard-code QubitType because the modifiers only alias qubits - newArg.type = QubitType::get(parser.getBuilder().getContext()); - blockArgs.push_back(newArg); - } while (succeeded(parser.parseOptionalComma())); - - if (parser.parseRParen()) { - return failure(); - } - } - - // 4. Parse the Region - // We explicitly pass the blockArgs we just parsed so they become the entry - // block! - if (parser.parseRegion(region, blockArgs)) { - return failure(); - } - - return success(); -} - -/** - * @brief Print a list of aliased qubits. - * - * @details - * The modifier operations use aliased qubits inside of their region. This - * function prints a representation of the relationship between the block - * arguments and the qubit operands. In the example below, the block argument - * `%a0` aliases the operand `%q1`. - * - * ```mlir - * qc.ctrl(%q0) targets(%a0 = %q1) { - * qc.x %a0 : !qc.qubit - * } : !qc.qubit - * ``` - */ -inline void printTargetAliasing(OpAsmPrinter& printer, Region& region, - OperandRange targetsIn) { - printer << "("; - if (region.empty()) { - printer << ") "; - printer.printRegion(region, false); - return; - } - auto& entryBlock = region.front(); - - const auto numTargets = targetsIn.size(); - for (unsigned i = 0; i < numTargets; ++i) { - if (i > 0) { - printer << ", "; - } - printer.printOperand(entryBlock.getArgument(i)); - printer << " = "; - printer.printOperand(targetsIn[i]); - } - printer << ") "; - - printer.printRegion(region, false); -} - -/** - * @brief Get the value corresponding to @p qubit from the block arguments @p - * qubits if @p qubit is a block argument, otherwise return @p qubit itself. - */ -inline Value getValueFromBlockArgument(Value qubit, ValueRange qubits) { - if (auto blockArg = dyn_cast(qubit)) { - assert(blockArg.getArgNumber() < qubits.size() && - "block argument index must be within qubits range"); - return qubits[blockArg.getArgNumber()]; - } - return qubit; -} - -/** - * @brief Returns the number of operations implementing @p UnitaryInterface in - * @p block. - */ -template -[[nodiscard]] size_t getNumBodyUnitaries(Block& block) { - return static_cast(llvm::count_if( - block, [](Operation& op) { return isa(op); })); -} - -/** - * @brief Returns the @p i-th operation implementing @p UnitaryInterface in - * @p block, reporting a fatal error if @p i is out of bounds. - */ -template -[[nodiscard]] UnitaryInterface getBodyUnitary(Block& block, size_t i) { - auto unitaries = llvm::make_filter_range( - block, [](Operation& op) { return isa(op); }); - auto it = std::next(unitaries.begin(), static_cast(i)); - if (it == unitaries.end()) { - llvm::reportFatalUsageError("Unitary index out of bounds"); - } - return cast(*it); -} - -/** - * @brief Returns the single operation implementing @p UnitaryInterface in - * @p block, or a null interface if @p block does not contain exactly one. - */ -template -[[nodiscard]] UnitaryInterface getSoleBodyUnitary(Block& block) { - auto unitaries = llvm::make_filter_range( - block, [](Operation& op) { return isa(op); }); - auto it = unitaries.begin(); - if (it == unitaries.end()) { - return {}; - } - auto unitary = cast(*it); - if (++it != unitaries.end()) { - return {}; - } - return unitary; -} - -/** - * @brief Hoists a body's supporting ops out before the modifier is erased. - * - * @details Moves every operation in @p body except @p keep and the block - * terminator to just before @p target. This keeps Values that feed @p keep - * (e.g., an exponent produced by constants/arithmetic) available after the - * modifier's region is erased, avoiding dangling operands. - * - * Unlike @c inlineBlockBefore, this is selective (@p keep and the terminator - * stay in @p body) and does not remap block arguments; the moved ops are - * classical and never reference the body's block arguments. - */ -inline void hoistSupportingOpsBefore(Block& body, Operation* keep, - Operation* target, - RewriterBase& rewriter) { - for (auto& bodyOp : llvm::make_early_inc_range(body)) { - if (&bodyOp != keep && !bodyOp.hasTrait()) { - rewriter.moveOpBefore(&bodyOp, target); - } - } -} - -/** - * @brief Inlines a modifier body and replaces the modifier with its results. - * - * @details Inlines the operations of @p body in front of @p op, substituting - * the block arguments of @p body with @p blockArgReplacements, and replaces - * @p op with the values yielded by the body's terminator. - */ -inline void inlineModifierBody(Operation* op, Block& body, - ValueRange blockArgReplacements, - RewriterBase& rewriter) { - auto* terminator = body.getTerminator(); - // Yielded block arguments are substituted when the body is inlined, so - // resolve them to their replacements before they are erased. - const auto results = - llvm::map_to_vector(terminator->getOperands(), [&](Value yielded) { - return getValueFromBlockArgument(yielded, blockArgReplacements); - }); - rewriter.inlineBlockBefore(&body, op, blockArgReplacements); - rewriter.eraseOp(terminator); - rewriter.replaceOp(op, results); -} - -/** - * @brief Inline @p source into the block currently being built and return the - * values its terminator yielded. - * - * @details Intended for use inside a modifier's body-builder callback, where - * the current insertion block is the freshly created (still terminator-less) - * body of the op under construction. Inlines @p source at the start of that - * block, substituting @p source's block arguments with @p blockArgReplacements, - * then drops @p source's terminator and returns the values it yielded so the - * caller can re-yield them. - * - * Unlike @c inlineModifierBody, this does not replace an existing op: it - * splices a body into the block being constructed and hands back the yielded - * values. In dialects whose bodies yield nothing, the returned vector is empty. - */ -inline SmallVector -inlineBodyReturningYields(Block& source, ValueRange blockArgReplacements, - RewriterBase& rewriter) { - auto* dest = rewriter.getInsertionBlock(); - rewriter.inlineBlockBefore(&source, dest, dest->begin(), - blockArgReplacements); - auto yielded = llvm::to_vector(dest->back().getOperands()); - rewriter.eraseOp(&dest->back()); - return yielded; -} - -} // namespace mlir::utils diff --git a/mlir/lib/Compiler/Programs.cpp b/mlir/lib/Compiler/Programs.cpp index 4828575d56..a2c447d7f2 100644 --- a/mlir/lib/Compiler/Programs.cpp +++ b/mlir/lib/Compiler/Programs.cpp @@ -19,6 +19,8 @@ #include "mlir/Conversion/QCToQIR/QIRBase/QCToQIRBase.h" #include "mlir/Dialect/CBit/IR/CBitDialect.h" #include "mlir/Dialect/MQT/IR/MQTDialect.h" +#include "mlir/Dialect/MQT/Transforms/GlobalPhaseNormalization.h" +#include "mlir/Dialect/MQT/Transforms/Passes.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/Translation/TranslateQASM3ToQC.h" #include "mlir/Dialect/QC/Translation/TranslateQCToOpenQASM3.h" @@ -26,8 +28,6 @@ #include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Dialect/QIR/Utils/QIRUtils.h" #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" -#include "mlir/Dialect/Utils/Transforms/GlobalPhaseNormalization.h" -#include "mlir/Dialect/Utils/Transforms/Passes.h" #include "mlir/Support/Passes.h" #include diff --git a/mlir/lib/Conversion/QCOToJeff/CMakeLists.txt b/mlir/lib/Conversion/QCOToJeff/CMakeLists.txt index d04a1f0ea5..9028a06963 100644 --- a/mlir/lib/Conversion/QCOToJeff/CMakeLists.txt +++ b/mlir/lib/Conversion/QCOToJeff/CMakeLists.txt @@ -20,6 +20,7 @@ add_mlir_conversion_library( MLIRNativeToJeff MLIRMQTDialect MLIRMQTTransforms + MLIRMQTUtils MLIRQCODialect MLIRTransforms) diff --git a/mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp b/mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp index ca29ebec3e..e34fd48b09 100644 --- a/mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp +++ b/mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp @@ -13,12 +13,12 @@ #include "mlir/Dialect/CBit/IR/CBitDialect.h" #include "mlir/Dialect/CBit/IR/CBitOps.h" #include "mlir/Dialect/MQT/IR/MQTDialect.h" +#include "mlir/Dialect/MQT/Transforms/GlobalPhaseNormalization.h" +#include "mlir/Dialect/MQT/Utils/GatePowering.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" #include "mlir/Dialect/QTensor/IR/QTensorOps.h" -#include "mlir/Dialect/Utils/Transforms/GlobalPhaseNormalization.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -1284,7 +1284,7 @@ struct ConvertQCOPowOpToJeff final : StatefulOpConversionPattern { // jeff only supports compile-time integer exponents between 0 and 255 const auto exponent = op.getExponentValue(); - if (!exponent || !utils::isIntegerExponent(*exponent) || *exponent < 0.0 || + if (!exponent || !mqt::isIntegerExponent(*exponent) || *exponent < 0.0 || *exponent > std::numeric_limits::max()) { return rewriter.notifyMatchFailure( op, "Only compile-time integer exponents between 0 and 255 are " diff --git a/mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp b/mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp index 9294657c50..b9d001399e 100644 --- a/mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp +++ b/mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp @@ -14,10 +14,10 @@ #include "mlir/Dialect/CBit/IR/CBitAttributes.h" #include "mlir/Dialect/CBit/IR/CBitDialect.h" #include "mlir/Dialect/CBit/IR/CBitOps.h" +#include "mlir/Dialect/MQT/Transforms/GlobalPhaseNormalization.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/IR/QCOps.h" #include "mlir/Dialect/QIR/Utils/QIRUtils.h" -#include "mlir/Dialect/Utils/Transforms/GlobalPhaseNormalization.h" #include #include diff --git a/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp b/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp index 87d02a9d21..659fe81ae8 100644 --- a/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp +++ b/mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp @@ -13,10 +13,10 @@ #include "mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h" #include "mlir/Dialect/CBit/IR/CBitDialect.h" #include "mlir/Dialect/CBit/IR/CBitOps.h" +#include "mlir/Dialect/MQT/Transforms/GlobalPhaseNormalization.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/IR/QCOps.h" #include "mlir/Dialect/QIR/Utils/QIRUtils.h" -#include "mlir/Dialect/Utils/Transforms/GlobalPhaseNormalization.h" #include #include diff --git a/mlir/lib/Dialect/CMakeLists.txt b/mlir/lib/Dialect/CMakeLists.txt index 89f90c85c5..a0934ac040 100644 --- a/mlir/lib/Dialect/CMakeLists.txt +++ b/mlir/lib/Dialect/CMakeLists.txt @@ -12,4 +12,3 @@ add_subdirectory(QCO) add_subdirectory(QIR) add_subdirectory(QC) add_subdirectory(QTensor) -add_subdirectory(Utils) diff --git a/mlir/lib/Dialect/MQT/CMakeLists.txt b/mlir/lib/Dialect/MQT/CMakeLists.txt index b181a84fed..59df224492 100644 --- a/mlir/lib/Dialect/MQT/CMakeLists.txt +++ b/mlir/lib/Dialect/MQT/CMakeLists.txt @@ -6,4 +6,6 @@ # # Licensed under the MIT License +add_subdirectory(Utils) add_subdirectory(IR) +add_subdirectory(Transforms) diff --git a/mlir/lib/Dialect/Utils/Transforms/CMakeLists.txt b/mlir/lib/Dialect/MQT/Transforms/CMakeLists.txt similarity index 81% rename from mlir/lib/Dialect/Utils/Transforms/CMakeLists.txt rename to mlir/lib/Dialect/MQT/Transforms/CMakeLists.txt index f6ce4ea6fc..68aea6ef52 100644 --- a/mlir/lib/Dialect/Utils/Transforms/CMakeLists.txt +++ b/mlir/lib/Dialect/MQT/Transforms/CMakeLists.txt @@ -11,11 +11,12 @@ add_mlir_library( NormalizeGlobalPhases.cpp UnrollModifiers.cpp ADDITIONAL_HEADER_DIRS - ${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Dialect/Utils/Transforms + ${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Dialect/MQT/Transforms LINK_LIBS PRIVATE MLIRArithDialect MLIRIR + MLIRMQTUtils MLIRPass MLIRQCDialect MLIRQCODialect @@ -26,9 +27,9 @@ add_mlir_library( mqt_mlir_target_use_project_options(MLIRMQTTransforms) file(GLOB_RECURSE PASSES_HEADERS_SOURCE - ${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Dialect/Utils/Transforms/*.h) + ${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Dialect/MQT/Transforms/*.h) file(GLOB_RECURSE PASSES_HEADERS_BUILD - ${MQT_MLIR_BUILD_INCLUDE_DIR}/mlir/Dialect/Utils/Transforms/*.inc) + ${MQT_MLIR_BUILD_INCLUDE_DIR}/mlir/Dialect/MQT/Transforms/*.inc) target_sources( MLIRMQTTransforms diff --git a/mlir/lib/Dialect/Utils/Transforms/NormalizeGlobalPhases.cpp b/mlir/lib/Dialect/MQT/Transforms/NormalizeGlobalPhases.cpp similarity index 92% rename from mlir/lib/Dialect/Utils/Transforms/NormalizeGlobalPhases.cpp rename to mlir/lib/Dialect/MQT/Transforms/NormalizeGlobalPhases.cpp index bfec94d218..99c58a95e1 100644 --- a/mlir/lib/Dialect/Utils/Transforms/NormalizeGlobalPhases.cpp +++ b/mlir/lib/Dialect/MQT/Transforms/NormalizeGlobalPhases.cpp @@ -8,11 +8,14 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Transforms/GlobalPhaseNormalization.h" +#include "mlir/Dialect/MQT/Transforms/Passes.h" +#include "mlir/Dialect/MQT/Utils/Angles.h" +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/GatePowering.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" -#include "mlir/Dialect/Utils/Transforms/GlobalPhaseNormalization.h" -#include "mlir/Dialect/Utils/Transforms/Passes.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -40,7 +43,7 @@ namespace mlir::mqt { #define GEN_PASS_DEF_NORMALIZEGLOBALPHASES -#include "mlir/Dialect/Utils/Transforms/Passes.h.inc" +#include "mlir/Dialect/MQT/Transforms/Passes.h.inc" namespace { @@ -59,8 +62,8 @@ using PhaseInstruction = std::variant; class PhaseExpression final { public: explicit PhaseExpression(Value angle) { - if (const auto constant = utils::valueToConstantDouble(angle)) { - instructions.emplace_back(utils::normalizeAngle(*constant)); + if (const auto constant = valueToConstantDouble(angle)) { + instructions.emplace_back(normalizeAngle(*constant)); } else { instructions.emplace_back(angle); leaves.push_back(angle); @@ -84,7 +87,7 @@ class PhaseExpression final { const auto rhs = other.getConstant(); if (lhs && rhs) { instructions.clear(); - instructions.emplace_back(utils::normalizeAngle(*lhs + *rhs)); + instructions.emplace_back(normalizeAngle(*lhs + *rhs)); leaves.clear(); return; } @@ -97,7 +100,7 @@ class PhaseExpression final { void negate() { if (const auto constant = getConstant()) { - instructions.front() = utils::normalizeAngle(-*constant); + instructions.front() = normalizeAngle(-*constant); return; } instructions.emplace_back(Negate{}); @@ -114,7 +117,7 @@ class PhaseExpression final { return; } if (const auto constant = getConstant()) { - instructions.front() = utils::normalizeAngle(*constant * factor); + instructions.front() = normalizeAngle(*constant * factor); return; } instructions.emplace_back(Scale{factor}); @@ -131,7 +134,7 @@ class PhaseExpression final { SmallVector stack; for (const auto& instruction : instructions) { if (const auto* constant = std::get_if(&instruction)) { - stack.push_back(utils::constantFromScalar(rewriter, loc, *constant)); + stack.push_back(constantFromScalar(rewriter, loc, *constant)); continue; } if (const auto* value = std::get_if(&instruction)) { @@ -152,7 +155,7 @@ class PhaseExpression final { continue; } const auto factor = std::get(instruction).factor; - const auto factorValue = utils::constantFromScalar(rewriter, loc, factor); + const auto factorValue = constantFromScalar(rewriter, loc, factor); stack.push_back( rewriter.createOrFold(loc, factorValue, operand)); } @@ -160,9 +163,8 @@ class PhaseExpression final { const Value result = stack.front(); // Fold pure constant arith trees back to a single normalized angle so // merged exit phases stay within the GPhase verifier contract. - if (const auto constant = utils::valueToConstantDouble(result)) { - return utils::constantFromScalar(rewriter, loc, - utils::normalizeAngle(*constant)); + if (const auto constant = valueToConstantDouble(result)) { + return constantFromScalar(rewriter, loc, normalizeAngle(*constant)); } return result; } @@ -297,7 +299,7 @@ class GlobalPhaseNormalizer final { template [[nodiscard]] std::optional factorPower(PowOp op) { const auto exponent = op.getExponentValue(); - if (!exponent || !utils::isIntegerExponent(*exponent)) { + if (!exponent || !isIntegerExponent(*exponent)) { normalizeRegion(op->getRegion(0)); return std::nullopt; } @@ -426,9 +428,9 @@ class GlobalPhaseNormalizer final { dyn_cast(directPhases.front()) ? cast(directPhases.front()).getTheta() : cast(directPhases.front()).getTheta(); - const auto constant = utils::valueToConstantDouble(angle); + const auto constant = valueToConstantDouble(angle); if (!constant || - (utils::normalizeAngle(*constant) == *constant && *constant != 0.0)) { + (normalizeAngle(*constant) == *constant && *constant != 0.0)) { return std::nullopt; } } diff --git a/mlir/lib/Dialect/Utils/Transforms/UnrollModifiers.cpp b/mlir/lib/Dialect/MQT/Transforms/UnrollModifiers.cpp similarity index 96% rename from mlir/lib/Dialect/Utils/Transforms/UnrollModifiers.cpp rename to mlir/lib/Dialect/MQT/Transforms/UnrollModifiers.cpp index 41f4547b81..cd3a63767b 100644 --- a/mlir/lib/Dialect/Utils/Transforms/UnrollModifiers.cpp +++ b/mlir/lib/Dialect/MQT/Transforms/UnrollModifiers.cpp @@ -8,12 +8,13 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Transforms/Passes.h" +#include "mlir/Dialect/MQT/Utils/GatePowering.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" #include "mlir/Dialect/QC/IR/QCInterfaces.h" #include "mlir/Dialect/QC/IR/QCOps.h" #include "mlir/Dialect/QCO/IR/QCOInterfaces.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" -#include "mlir/Dialect/Utils/Transforms/Passes.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -39,7 +40,7 @@ namespace mlir::mqt { #define GEN_PASS_DEF_UNROLLMODIFIERS -#include "mlir/Dialect/Utils/Transforms/Passes.h.inc" +#include "mlir/Dialect/MQT/Transforms/Passes.h.inc" /** *@brief Move the classical operations of @p body in front of @p modifier. @@ -73,7 +74,7 @@ static LogicalResult hoistClassicalOps(Block& body, Operation* modifier, /// Check whether the exponent of @p op is a compile-time known integer. template static bool hasIntegerExponent(PowOp op) { const auto exponent = op.getExponentValue(); - return exponent && utils::isIntegerExponent(*exponent); + return exponent && isIntegerExponent(*exponent); } //===----------------------------------------------------------------------===// @@ -103,7 +104,7 @@ static LogicalResult unrollModifier(qc::CtrlOp op, RewriterBase& rewriter) { rewriter.setInsertionPoint(op); for (auto unitary : body->getOps()) { const auto targets = llvm::map_to_vector(unitary.getQubits(), [&](Value q) { - return utils::getValueFromBlockArgument(q, op.getTargets()); + return getValueFromBlockArgument(q, op.getTargets()); }); qc::CtrlOp::create( rewriter, op.getLoc(), op.getControls(), targets, @@ -128,7 +129,7 @@ static LogicalResult unrollModifier(qc::InvOp op, RewriterBase& rewriter) { // (a b)^-1 = b^-1 a^-1, so the operations are inverted in reverse order. for (auto unitary : llvm::reverse(body->getOps())) { const auto qubits = llvm::map_to_vector(unitary.getQubits(), [&](Value q) { - return utils::getValueFromBlockArgument(q, op.getQubits()); + return getValueFromBlockArgument(q, op.getQubits()); }); qc::InvOp::create(rewriter, op.getLoc(), qubits, [&](ValueRange args) { cloneIntoBody(unitary, args, rewriter); @@ -168,7 +169,7 @@ static LogicalResult unrollModifier(qc::PowOp op, RewriterBase& rewriter) { rewriter.setInsertionPoint(op); for (auto unitary : body->getOps()) { const auto qubits = llvm::map_to_vector(unitary.getQubits(), [&](Value q) { - return utils::getValueFromBlockArgument(q, op.getQubits()); + return getValueFromBlockArgument(q, op.getQubits()); }); qc::PowOp::create( rewriter, op.getLoc(), op.getExponent(), qubits, diff --git a/mlir/lib/Dialect/MQT/Utils/Angles.cpp b/mlir/lib/Dialect/MQT/Utils/Angles.cpp new file mode 100644 index 0000000000..aef5707c78 --- /dev/null +++ b/mlir/lib/Dialect/MQT/Utils/Angles.cpp @@ -0,0 +1,53 @@ +/* + * 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/MQT/Utils/Angles.h" + +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" + +#include +#include +#include + +#include +#include + +namespace mlir::mqt { + +double normalizeAngle(double theta) { + const double twoPi = 2.0 * std::numbers::pi; + theta = std::fmod(theta, twoPi); + if (theta > std::numbers::pi) { + theta -= twoPi; + } + if (theta <= -std::numbers::pi) { + theta += twoPi; + } + return theta; +} + +bool isValidGlobalPhaseAngle(const double theta) { + return std::isfinite(theta) && std::abs(theta) <= MAX_GLOBAL_PHASE_ANGLE; +} + +LogicalResult verifyGlobalPhaseAngle(Operation* operation, Value angle) { + const auto constant = valueToConstantDouble(angle); + if (!constant || !std::isfinite(*constant)) { + return success(); + } + if (!isValidGlobalPhaseAngle(*constant)) { + return operation->emitOpError() + << "constant angle must have magnitude at most " + << MAX_GLOBAL_PHASE_ANGLE << " radians"; + } + return success(); +} + +} // namespace mlir::mqt diff --git a/mlir/lib/Dialect/MQT/Utils/CMakeLists.txt b/mlir/lib/Dialect/MQT/Utils/CMakeLists.txt new file mode 100644 index 0000000000..5919c49565 --- /dev/null +++ b/mlir/lib/Dialect/MQT/Utils/CMakeLists.txt @@ -0,0 +1,29 @@ +# 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 + +add_mlir_library( + MLIRMQTUtils + Angles.cpp + ConstantFolding.cpp + DenseUnitary.cpp + GatePowering.cpp + Modifiers.cpp + Parameters.cpp + ADDITIONAL_HEADER_DIRS + ${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Dialect/MQT/Utils + LINK_LIBS + PUBLIC + MLIRArithDialect + MLIRIR + MLIRSideEffectInterfaces) + +mqt_mlir_target_use_project_options(MLIRMQTUtils) + +file(GLOB UTILS_HEADERS_SOURCE "${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Dialect/MQT/Utils/*.h") +target_sources(MLIRMQTUtils PUBLIC FILE_SET HEADERS BASE_DIRS ${MQT_MLIR_SOURCE_INCLUDE_DIR} FILES + ${UTILS_HEADERS_SOURCE}) diff --git a/mlir/lib/Dialect/MQT/Utils/ConstantFolding.cpp b/mlir/lib/Dialect/MQT/Utils/ConstantFolding.cpp new file mode 100644 index 0000000000..b9a446ec3c --- /dev/null +++ b/mlir/lib/Dialect/MQT/Utils/ConstantFolding.cpp @@ -0,0 +1,105 @@ +/* + * 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/MQT/Utils/ConstantFolding.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace mlir::mqt { + +std::optional attributeToDouble(Attribute attr) { + if (auto floatAttr = dyn_cast(attr)) { + return floatAttr.getValueAsDouble(); + } + if (auto intAttr = dyn_cast(attr)) { + const bool isSigned = !intAttr.getType().isUnsignedInteger(); + APFloat value(APFloat::IEEEdouble(), APInt::getZero(64)); + value.convertFromAPInt(intAttr.getValue(), isSigned, + APFloat::rmNearestTiesToEven); + return value.convertToDouble(); + } + return std::nullopt; +} + +std::optional valueToDouble(Value value) { + auto constantOp = value.getDefiningOp(); + if (!constantOp) { + return std::nullopt; + } + return attributeToDouble(constantOp.getValue()); +} + +std::optional +valueToConstantAttr(Value value, + DenseMap>& cache) { + if (const auto it = cache.find(value); it != cache.end()) { + return it->second; + } + + Attribute attr; + if (matchPattern(value, m_Constant(&attr))) { + return cache[value] = attr; + } + + Operation* operation = value.getDefiningOp(); + if (operation == nullptr || operation->getNumRegions() != 0 || + !isPure(operation)) { + return cache[value] = std::nullopt; + } + + SmallVector operands; + operands.reserve(operation->getNumOperands()); + for (Value operand : operation->getOperands()) { + const auto folded = valueToConstantAttr(operand, cache); + if (!folded) { + return cache[value] = std::nullopt; + } + operands.push_back(*folded); + } + + SmallVector results; + if (failed(operation->fold(operands, results)) || results.size() != 1) { + return cache[value] = std::nullopt; + } + std::optional folded; + if (auto resultAttr = dyn_cast_if_present(results.front())) { + folded = resultAttr; + } else if (auto resultValue = dyn_cast_if_present(results.front())) { + /* Identity-style folds can return an existing SSA value. */ + folded = valueToConstantAttr(resultValue, cache); + } + return cache[value] = folded; +} + +std::optional valueToConstantAttr(Value value) { + DenseMap> cache; + return valueToConstantAttr(value, cache); +} + +std::optional valueToConstantDouble(Value value) { + if (const auto attr = valueToConstantAttr(value)) { + return attributeToDouble(*attr); + } + return std::nullopt; +} + +} // namespace mlir::mqt diff --git a/mlir/include/mlir/Dialect/Utils/DenseUnitary.h b/mlir/lib/Dialect/MQT/Utils/DenseUnitary.cpp similarity index 72% rename from mlir/include/mlir/Dialect/Utils/DenseUnitary.h rename to mlir/lib/Dialect/MQT/Utils/DenseUnitary.cpp index 18e3e06d30..551408b6c3 100644 --- a/mlir/include/mlir/Dialect/Utils/DenseUnitary.h +++ b/mlir/lib/Dialect/MQT/Utils/DenseUnitary.cpp @@ -8,16 +8,17 @@ * Licensed under the MIT License */ -#pragma once +#include "mlir/Dialect/MQT/Utils/DenseUnitary.h" #include #include -#include +#include #include #include #include #include #include +#include #include #include @@ -25,24 +26,11 @@ #include #include -namespace mlir::utils { +namespace mlir::mqt { -/** - * Maximum absolute entry-wise deviation of U^dagger U from the identity. - * - * This tolerance accounts for binary64 accumulation error while remaining - * substantially below the precision at which dense input matrices are - * normally specified. - */ -inline constexpr double DENSE_UNITARY_TOLERANCE = 1e-10; - -/** Maximum matrix arity accepted by the deterministic unitarity verifier. */ -inline constexpr size_t MAX_DENSE_UNITARY_QUBITS = 8; - -/** Verify the common dense-matrix contract of QC and QCO unitary operations. */ -[[nodiscard]] inline LogicalResult -verifyDenseUnitaryMatrix(Operation* operation, const ElementsAttr matrixAttr, - const ValueRange qubits) { +LogicalResult verifyDenseUnitaryMatrix(Operation* operation, + ElementsAttr matrixAttr, + ValueRange qubits) { const auto numQubits = qubits.size(); if (numQubits == 0U) { return operation->emitOpError("requires at least one qubit"); @@ -52,21 +40,21 @@ verifyDenseUnitaryMatrix(Operation* operation, const ElementsAttr matrixAttr, << "supports at most " << MAX_DENSE_UNITARY_QUBITS << " qubits"; } llvm::SmallDenseSet uniqueQubits; - for (const auto qubit : qubits) { + for (auto qubit : qubits) { if (!uniqueQubits.insert(qubit).second) { return operation->emitOpError("duplicate qubit operand"); } } - const auto matrix = dyn_cast(matrixAttr); + auto matrix = dyn_cast(matrixAttr); if (!matrix) { return operation->emitOpError("matrix must use dense element storage"); } - const auto type = dyn_cast(matrix.getType()); + auto type = dyn_cast(matrix.getType()); if (!type || type.getRank() != 2 || type.getShape()[0] != type.getShape()[1]) { return operation->emitOpError("matrix must be a square rank-two tensor"); } - const auto complexType = dyn_cast(type.getElementType()); + auto complexType = dyn_cast(type.getElementType()); if (!complexType || !complexType.getElementType().isF64()) { return operation->emitOpError( "matrix elements must have type complex"); @@ -109,18 +97,17 @@ verifyDenseUnitaryMatrix(Operation* operation, const ElementsAttr matrixAttr, return success(); } -/** Return whether a dense square matrix is exactly the identity. */ -[[nodiscard]] inline bool isExactIdentityMatrix(const ElementsAttr matrixAttr) { - const auto matrix = dyn_cast(matrixAttr); +bool isExactIdentityMatrix(ElementsAttr matrixAttr) { + auto matrix = dyn_cast(matrixAttr); if (!matrix) { return false; } - const auto type = dyn_cast(matrix.getType()); + auto type = dyn_cast(matrix.getType()); if (!type || type.getRank() != 2 || type.getShape()[0] != type.getShape()[1] || type.getShape()[0] <= 0) { return false; } - const auto complexType = dyn_cast(type.getElementType()); + auto complexType = dyn_cast(type.getElementType()); if (!complexType || !complexType.getElementType().isF64()) { return false; } @@ -138,4 +125,4 @@ verifyDenseUnitaryMatrix(Operation* operation, const ElementsAttr matrixAttr, return true; } -} // namespace mlir::utils +} // namespace mlir::mqt diff --git a/mlir/include/mlir/Dialect/Utils/UGateUtils.h b/mlir/lib/Dialect/MQT/Utils/GatePowering.cpp similarity index 72% rename from mlir/include/mlir/Dialect/Utils/UGateUtils.h rename to mlir/lib/Dialect/MQT/Utils/GatePowering.cpp index 9ff7b41c3f..61ccb2bf14 100644 --- a/mlir/include/mlir/Dialect/Utils/UGateUtils.h +++ b/mlir/lib/Dialect/MQT/Utils/GatePowering.cpp @@ -8,9 +8,7 @@ * Licensed under the MIT License */ -#pragma once - -#include "mlir/Dialect/Utils/Utils.h" +#include "mlir/Dialect/MQT/Utils/GatePowering.h" #include #include @@ -21,47 +19,22 @@ #include #include -namespace mlir::utils { +namespace mlir::mqt { -/** - * Maximum exponent considered for safe binary64 U-gate powering. - * - * Even with analytical SU(2) powering, uncertainty in the input angles is - * magnified by the exponent. Candidate rewrites are additionally checked - * against the source matrix before they are accepted. - */ -inline constexpr uint64_t MAX_SAFE_U_POWER_EXPONENT = 1024U; - -/// Maximum entry-wise matrix error accepted for a powered U-gate rewrite. -inline constexpr double U_POWER_EQUIVALENCE_TOLERANCE = 5e-13; +bool isIntegerExponent(const double value) { + return value == std::floor(value) && std::isfinite(value); +} -/** - * @brief Parameters representing a powered U gate. - * - * All values are in radians. The phase satisfies - * `U(input)^exponent = exp(i * phase) * U(theta, phi, lambda)`. - */ -struct UPowerParameters { - double theta; ///< Resulting U rotation angle. - double phi; ///< Resulting U phi angle. - double lambda; ///< Resulting U lambda angle. - double phase; ///< Remaining global phase. -}; +bool isEvenExponent(const double value) { + return isIntegerExponent(value) && std::fmod(std::fabs(value), 2.0) == 0.0; +} -/** - * @brief Compute a positive integral power of a constant U gate. - * - * @return Parameters satisfying - * `U(theta, phi, lambda)^exponent = exp(i*phase) * U(result)`, or - * `std::nullopt` if @p exponent is not a positive integer no greater than - * `MAX_SAFE_U_POWER_EXPONENT`, an input is not finite, or the binary64 result - * cannot be reconstructed within `U_POWER_EQUIVALENCE_TOLERANCE`. - */ -[[nodiscard]] inline std::optional -powerUParameters(const double theta, const double phi, const double lambda, - const double exponent) { +std::optional powerUParameters(const double theta, + const double phi, + const double lambda, + const double exponent) { if (!std::isfinite(theta) || !std::isfinite(phi) || !std::isfinite(lambda) || - !mlir::utils::isIntegerExponent(exponent) || exponent <= 0.0 || + !isIntegerExponent(exponent) || exponent <= 0.0 || exponent > static_cast(MAX_SAFE_U_POWER_EXPONENT)) { return std::nullopt; } @@ -70,9 +43,9 @@ powerUParameters(const double theta, const double phi, const double lambda, using Complex = std::complex; using Matrix = std::array; - // U(theta, phi, lambda) = exp(i * (phi + lambda) / 2) * - // RZ(phi) * RY(theta) * RZ(lambda). Represent the SU(2) factor by a unit - // quaternion and power it analytically by multiplying its axis angle. + /// U(theta, phi, lambda) = exp(i * (phi + lambda) / 2) * + /// RZ(phi) * RY(theta) * RZ(lambda). Represent the SU(2) factor by a unit + /// quaternion and power it analytically by multiplying its axis angle. const double halfTheta = theta / 2.0; const double halfPhi = phi / 2.0; const double halfLambda = lambda / 2.0; @@ -154,10 +127,10 @@ powerUParameters(const double theta, const double phi, const double lambda, std::remainder(poweredPhase - ((resultPhi + resultLambda) / 2.0), 2.0 * std::numbers::pi); - // Binary64 evaluation of the source U matrix can itself deviate from an - // exact unitary for large angles, and powering magnifies that deviation. - // Reject a rewrite when the analytical unitary cannot represent the source - // operation closely enough for the dialect's full-matrix contract. + /// Binary64 evaluation of the source U matrix can itself deviate from an + /// exact unitary for large angles, and powering magnifies that deviation. + /// Reject a rewrite when the analytical unitary cannot represent the source + /// operation closely enough for the dialect's full-matrix contract. const Complex imaginary{0.0, 1.0}; const auto uMatrix = [&](const double matrixTheta, const double matrixPhi, const double matrixLambda) { @@ -198,7 +171,10 @@ powerUParameters(const double theta, const double phi, const double lambda, return std::nullopt; } } - return UPowerParameters{resultTheta, resultPhi, resultLambda, resultPhase}; + return UPowerParameters{.theta = resultTheta, + .phi = resultPhi, + .lambda = resultLambda, + .phase = resultPhase}; } -} // namespace mlir::utils +} // namespace mlir::mqt diff --git a/mlir/lib/Dialect/MQT/Utils/Modifiers.cpp b/mlir/lib/Dialect/MQT/Utils/Modifiers.cpp new file mode 100644 index 0000000000..e5a299bcda --- /dev/null +++ b/mlir/lib/Dialect/MQT/Utils/Modifiers.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/Dialect/MQT/Utils/Modifiers.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace mlir::mqt { + +void printTargetAliasing(OpAsmPrinter& printer, Region& region, + OperandRange targetsIn) { + printer << "("; + if (region.empty()) { + printer << ") "; + printer.printRegion(region, false); + return; + } + auto& entryBlock = region.front(); + + for (unsigned i = 0; i < targetsIn.size(); ++i) { + if (i > 0) { + printer << ", "; + } + printer.printOperand(entryBlock.getArgument(i)); + printer << " = "; + printer.printOperand(targetsIn[i]); + } + printer << ") "; + printer.printRegion(region, false); +} + +Value getValueFromBlockArgument(Value qubit, ValueRange qubits) { + if (auto blockArg = dyn_cast(qubit)) { + assert(blockArg.getArgNumber() < qubits.size() && + "block argument index must be within qubits range"); + return qubits[blockArg.getArgNumber()]; + } + return qubit; +} + +void hoistSupportingOpsBefore(Block& body, Operation* keep, Operation* target, + RewriterBase& rewriter) { + for (auto& bodyOp : llvm::make_early_inc_range(body)) { + if (&bodyOp != keep && !bodyOp.hasTrait()) { + rewriter.moveOpBefore(&bodyOp, target); + } + } +} + +void inlineModifierBody(Operation* operation, Block& body, + ValueRange blockArgReplacements, + RewriterBase& rewriter) { + auto* terminator = body.getTerminator(); + const auto results = + llvm::map_to_vector(terminator->getOperands(), [&](Value yielded) { + return getValueFromBlockArgument(yielded, blockArgReplacements); + }); + rewriter.inlineBlockBefore(&body, operation, blockArgReplacements); + rewriter.eraseOp(terminator); + rewriter.replaceOp(operation, results); +} + +SmallVector inlineBodyReturningYields(Block& source, + ValueRange blockArgReplacements, + RewriterBase& rewriter) { + auto* destination = rewriter.getInsertionBlock(); + rewriter.inlineBlockBefore(&source, destination, destination->begin(), + blockArgReplacements); + auto yielded = llvm::to_vector(destination->back().getOperands()); + rewriter.eraseOp(&destination->back()); + return yielded; +} + +} // namespace mlir::mqt diff --git a/mlir/lib/Dialect/MQT/Utils/Parameters.cpp b/mlir/lib/Dialect/MQT/Utils/Parameters.cpp new file mode 100644 index 0000000000..0af4a8241a --- /dev/null +++ b/mlir/lib/Dialect/MQT/Utils/Parameters.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/MQT/Utils/Parameters.h" + +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace mlir::mqt { + +Value constantFromScalar(OpBuilder& builder, Location loc, const double value) { + return arith::ConstantOp::create(builder, loc, + builder.getF64FloatAttr(value)); +} + +Value constantFromScalar(OpBuilder& builder, Location loc, + const int64_t value) { + return arith::ConstantOp::create(builder, loc, builder.getIndexAttr(value)); +} + +Value constantFromScalar(OpBuilder& builder, Location loc, const bool value) { + return arith::ConstantOp::create(builder, loc, builder.getBoolAttr(value)); +} + +LogicalResult verifyFiniteConstantParameters(Operation* operation, + ValueRange parameters) { + DenseMap> constantCache; + DenseSet visited; + for (const auto [index, parameter] : llvm::enumerate(parameters)) { + SmallVector worklist{parameter}; + while (!worklist.empty()) { + auto value = worklist.pop_back_val(); + if (!visited.insert(value).second) { + continue; + } + if (const auto constant = valueToConstantAttr(value, constantCache)) { + if (auto floating = dyn_cast(*constant); + floating && !floating.getValue().isFinite()) { + return operation->emitOpError() + << "constant parameter expression at index " << index + << " must be finite"; + } + } + Operation* definingOp = value.getDefiningOp(); + if (definingOp == nullptr || definingOp->getNumRegions() != 0 || + !isPure(definingOp)) { + continue; + } + llvm::append_range(worklist, definingOp->getOperands()); + } + } + return success(); +} + +} // namespace mlir::mqt diff --git a/mlir/lib/Dialect/QC/Builder/CMakeLists.txt b/mlir/lib/Dialect/QC/Builder/CMakeLists.txt index e88124e02e..ab3419682f 100644 --- a/mlir/lib/Dialect/QC/Builder/CMakeLists.txt +++ b/mlir/lib/Dialect/QC/Builder/CMakeLists.txt @@ -17,7 +17,9 @@ add_mlir_library( MLIRMemRefDialect MLIRMQTDialect MLIRSCFDialect - MLIRQCDialect) + MLIRQCDialect + PRIVATE + MLIRMQTUtils) mqt_mlir_target_use_project_options(MLIRQCProgramBuilder) diff --git a/mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp b/mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp index 5034956482..d56f53461a 100644 --- a/mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp +++ b/mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp @@ -14,9 +14,9 @@ #include "mlir/Dialect/CBit/IR/CBitDialect.h" #include "mlir/Dialect/CBit/IR/CBitOps.h" #include "mlir/Dialect/MQT/IR/MQTDialect.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -41,7 +41,7 @@ #include #include -using namespace mlir::utils; +using namespace mlir::mqt; namespace mlir::qc { QCProgramBuilder::QCProgramBuilder(MLIRContext* context) diff --git a/mlir/lib/Dialect/QC/IR/CMakeLists.txt b/mlir/lib/Dialect/QC/IR/CMakeLists.txt index 682bf5b43c..7a2722cc89 100644 --- a/mlir/lib/Dialect/QC/IR/CMakeLists.txt +++ b/mlir/lib/Dialect/QC/IR/CMakeLists.txt @@ -26,6 +26,7 @@ add_mlir_dialect_library( MLIRCBitDialect MLIRIR MLIRInferTypeOpInterface + MLIRMQTUtils MLIRSideEffectInterfaces MLIRTransformUtils) diff --git a/mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp b/mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp index 11efc3168e..6a88c3982f 100644 --- a/mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp @@ -9,10 +9,10 @@ */ #include "ModifierUtils.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/IR/QCInterfaces.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -52,7 +52,7 @@ struct MergeNestedCtrl final : OpRewritePattern { return failure(); } - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -70,18 +70,17 @@ struct MergeNestedCtrl final : OpRewritePattern { auto outerTargets = op.getTargets(); SmallVector controls(op.getControls()); for (auto control : innerCtrlOp.getControls()) { - controls.push_back( - utils::getValueFromBlockArgument(control, outerTargets)); + controls.push_back(mqt::getValueFromBlockArgument(control, outerTargets)); } const auto targets = llvm::map_to_vector(innerCtrlOp.getTargets(), [&](Value t) { - return utils::getValueFromBlockArgument(t, outerTargets); + return mqt::getValueFromBlockArgument(t, outerTargets); }); CtrlOp::create(rewriter, op.getLoc(), controls, targets, [&](ValueRange mergedTargets) { - utils::inlineBodyReturningYields(*innerCtrlOp.getBody(), - mergedTargets, rewriter); + mqt::inlineBodyReturningYields(*innerCtrlOp.getBody(), + mergedTargets, rewriter); }); rewriter.eraseOp(op); return success(); @@ -97,7 +96,7 @@ struct ReduceCtrl final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(CtrlOp op, PatternRewriter& rewriter) const override { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -105,7 +104,7 @@ struct ReduceCtrl final : OpRewritePattern { // Inline ops from empty control modifiers, IdOp and BarrierOp if (op.getNumControls() == 0 || isa(innerOp)) { - utils::inlineModifierBody(op, *op.getBody(), op.getTargets(), rewriter); + mqt::inlineModifierBody(op, *op.getBody(), op.getTargets(), rewriter); return success(); } @@ -195,23 +194,23 @@ struct DropUnusedTargets final : OpRewritePattern { } // namespace size_t CtrlOp::getNumBodyUnitaries() { - return utils::getNumBodyUnitaries(*getBody()); + return mqt::getNumBodyUnitaries(*getBody()); } UnitaryOpInterface CtrlOp::getBodyUnitary(const size_t i) { - return utils::getBodyUnitary(*getBody(), i); + return mqt::getBodyUnitary(*getBody(), i); } void CtrlOp::build(OpBuilder& odsBuilder, OperationState& odsState, ValueRange controls, ValueRange targets, const function_ref& body) { build(odsBuilder, odsState, controls, targets); - utils::buildModifierBody(odsBuilder, odsState, targets.size(), - [&](OpBuilder& builder, Block& block) { - body(block.getArguments()); - YieldOp::create(builder, - odsState.location); - }); + mqt::buildModifierBody(odsBuilder, odsState, targets.size(), + [&](OpBuilder& builder, Block& block) { + body(block.getArguments()); + YieldOp::create(builder, + odsState.location); + }); } void CtrlOp::build(OpBuilder& odsBuilder, OperationState& odsState, @@ -224,7 +223,7 @@ void CtrlOp::build(OpBuilder& odsBuilder, OperationState& odsState, odsState.getOrAddProperties() .operandSegmentSizes.begin()); odsState.addRegion(); - utils::buildModifierBody( + mqt::buildModifierBody( odsBuilder, odsState, 1, [&](OpBuilder& builder, Block& block) { bodyBuilder(block.getArgument(0)); YieldOp::create(builder, odsState.location); diff --git a/mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp b/mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp index c26ccb6ace..0d9c71b39f 100644 --- a/mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp @@ -9,10 +9,10 @@ */ #include "ModifierUtils.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/IR/QCInterfaces.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -44,7 +44,7 @@ struct MoveCtrlOutsideInv final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp op, PatternRewriter& rewriter) const override { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -60,19 +60,19 @@ struct MoveCtrlOutsideInv final : OpRewritePattern { auto outerQubits = op.getQubits(); const auto controls = llvm::map_to_vector(innerCtrlOp.getControls(), [&](Value c) { - return utils::getValueFromBlockArgument(c, outerQubits); + return mqt::getValueFromBlockArgument(c, outerQubits); }); const auto targets = llvm::map_to_vector(innerCtrlOp.getTargets(), [&](Value t) { - return utils::getValueFromBlockArgument(t, outerQubits); + return mqt::getValueFromBlockArgument(t, outerQubits); }); rewriter.replaceOpWithNewOp( op, controls, targets, [&](ValueRange targetArgs) { InvOp::create(rewriter, op.getLoc(), targetArgs, [&](ValueRange invArgs) { - utils::inlineBodyReturningYields( - *innerCtrlOp.getBody(), invArgs, rewriter); + mqt::inlineBodyReturningYields(*innerCtrlOp.getBody(), + invArgs, rewriter); }); }); @@ -92,8 +92,7 @@ struct InvPowToNegPow final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(InvOp invOp, PatternRewriter& rewriter) const override { - auto inner = - utils::getSoleBodyUnitary(*invOp.getBody()); + auto inner = mqt::getSoleBodyUnitary(*invOp.getBody()); if (!inner) { return failure(); } @@ -104,8 +103,8 @@ struct InvPowToNegPow final : OpRewritePattern { // Move supporting ops (constants, arithmetic) out of the body so their // Values are accessible from outside and survive InvOp erasure. - utils::hoistSupportingOpsBefore(*invOp.getBody(), innerPow.getOperation(), - invOp, rewriter); + mqt::hoistSupportingOpsBefore(*invOp.getBody(), innerPow.getOperation(), + invOp, rewriter); Value negExponent = arith::NegFOp::create(rewriter, invOp.getLoc(), innerPow.getExponent()); // The inner pow's operands alias the inv's block args; translate them back @@ -113,13 +112,13 @@ struct InvPowToNegPow final : OpRewritePattern { // parent scope. auto outerQubits = invOp.getQubits(); const auto qubits = llvm::map_to_vector(innerPow.getQubits(), [&](Value v) { - return utils::getValueFromBlockArgument(v, outerQubits); + return mqt::getValueFromBlockArgument(v, outerQubits); }); rewriter.replaceOpWithNewOp( invOp, negExponent, qubits, [&](ValueRange powArgs) { // Inner pow body args now match the new pow's args positionally. - utils::inlineBodyReturningYields(*innerPow.getBody(), powArgs, - rewriter); + mqt::inlineBodyReturningYields(*innerPow.getBody(), powArgs, + rewriter); }); return success(); } @@ -135,7 +134,7 @@ struct InlineSelfAdjoint final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp op, PatternRewriter& rewriter) const override { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -147,7 +146,7 @@ struct InlineSelfAdjoint final : OpRewritePattern { // A self-adjoint gate is its own inverse, so the modifier can be dropped // and its body applied directly to the involved qubits. - utils::inlineModifierBody(op, *op.getBody(), op.getQubits(), rewriter); + mqt::inlineModifierBody(op, *op.getBody(), op.getQubits(), rewriter); return success(); } }; @@ -163,7 +162,7 @@ struct ReplaceWithKnownGates final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp op, PatternRewriter& rewriter) const override { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -293,7 +292,7 @@ struct ReplaceWithKnownGates final : OpRewritePattern { return failure(); } - utils::inlineModifierBody(op, *op.getBody(), op.getQubits(), rewriter); + mqt::inlineModifierBody(op, *op.getBody(), op.getQubits(), rewriter); return success(); } }; @@ -305,7 +304,7 @@ struct CancelNestedInv final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(InvOp op, PatternRewriter& rewriter) const override { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -313,7 +312,7 @@ struct CancelNestedInv final : OpRewritePattern { if (!innerInvOp) { return failure(); } - if (!utils::getSoleBodyUnitary(*innerInvOp.getBody())) { + if (!mqt::getSoleBodyUnitary(*innerInvOp.getBody())) { return failure(); } @@ -322,10 +321,9 @@ struct CancelNestedInv final : OpRewritePattern { // inputs, which in turn alias the outer qubits. const auto replacements = llvm::map_to_vector(innerInvOp.getQubits(), [&](Value q) { - return utils::getValueFromBlockArgument(q, op.getQubits()); + return mqt::getValueFromBlockArgument(q, op.getQubits()); }); - utils::inlineModifierBody(op, *innerInvOp.getBody(), replacements, - rewriter); + mqt::inlineModifierBody(op, *innerInvOp.getBody(), replacements, rewriter); return success(); } }; @@ -372,30 +370,30 @@ struct DropUnusedInvQubits final : OpRewritePattern { } // namespace size_t InvOp::getNumBodyUnitaries() { - return utils::getNumBodyUnitaries(*getBody()); + return mqt::getNumBodyUnitaries(*getBody()); } UnitaryOpInterface InvOp::getBodyUnitary(const size_t i) { - return utils::getBodyUnitary(*getBody(), i); + return mqt::getBodyUnitary(*getBody(), i); } void InvOp::build(OpBuilder& odsBuilder, OperationState& odsState, ValueRange qubits, const function_ref& body) { build(odsBuilder, odsState, qubits); - utils::buildModifierBody(odsBuilder, odsState, qubits.size(), - [&](OpBuilder& builder, Block& block) { - body(block.getArguments()); - YieldOp::create(builder, - odsState.location); - }); + mqt::buildModifierBody(odsBuilder, odsState, qubits.size(), + [&](OpBuilder& builder, Block& block) { + body(block.getArguments()); + YieldOp::create(builder, + odsState.location); + }); } void InvOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubit, const function_ref& bodyBuilder) { odsState.addOperands(qubit); odsState.addRegion(); - utils::buildModifierBody( + mqt::buildModifierBody( odsBuilder, odsState, 1, [&](OpBuilder& builder, Block& block) { bodyBuilder(block.getArgument(0)); YieldOp::create(builder, odsState.location); diff --git a/mlir/lib/Dialect/QC/IR/Modifiers/ModifierUtils.cpp b/mlir/lib/Dialect/QC/IR/Modifiers/ModifierUtils.cpp index cf07712f41..4d9a4faccd 100644 --- a/mlir/lib/Dialect/QC/IR/Modifiers/ModifierUtils.cpp +++ b/mlir/lib/Dialect/QC/IR/Modifiers/ModifierUtils.cpp @@ -11,9 +11,9 @@ #include "ModifierUtils.h" #include "mlir/Dialect/CBit/IR/CBitOps.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -91,7 +91,7 @@ void inlineNarrowedBody(Block& body, ValueRange qubits, ArrayRef used, for (auto [index, arg] : llvm::zip_equal(used, args)) { replacements[index] = arg; } - utils::inlineBodyReturningYields(body, replacements, rewriter); + mqt::inlineBodyReturningYields(body, replacements, rewriter); } } // namespace mlir::qc::detail diff --git a/mlir/lib/Dialect/QC/IR/Modifiers/PowOp.cpp b/mlir/lib/Dialect/QC/IR/Modifiers/PowOp.cpp index 5fd6c8fe93..89d980e739 100644 --- a/mlir/lib/Dialect/QC/IR/Modifiers/PowOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Modifiers/PowOp.cpp @@ -9,11 +9,14 @@ */ #include "ModifierUtils.h" +#include "mlir/Dialect/MQT/Utils/Angles.h" +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/GatePowering.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/IR/QCInterfaces.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/UGateUtils.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -36,7 +39,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; /** * @brief If the computed P-gate angle corresponds to a named gate, emit it @@ -82,27 +85,27 @@ static LogicalResult tryReplacePOpWithNamedGate(double angle, PowOp op, const double norm = normalizeAngle(angle); const double pi = std::numbers::pi; - if (std::abs(norm) < TOLERANCE) { + if (std::abs(norm) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.eraseOp(op); return success(); } - if (std::abs(std::abs(norm) - pi) < TOLERANCE) { + if (std::abs(std::abs(norm) - pi) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp(op, target); return success(); } - if (std::abs(norm - (pi / 2.0)) < TOLERANCE) { + if (std::abs(norm - (pi / 2.0)) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp(op, target); return success(); } - if (std::abs(norm + (pi / 2.0)) < TOLERANCE) { + if (std::abs(norm + (pi / 2.0)) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp(op, target); return success(); } - if (std::abs(norm - (pi / 4.0)) < TOLERANCE) { + if (std::abs(norm - (pi / 4.0)) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp(op, target); return success(); } - if (std::abs(norm + (pi / 4.0)) < TOLERANCE) { + if (std::abs(norm + (pi / 4.0)) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp(op, target); return success(); } @@ -122,10 +125,11 @@ struct InlinePow1 final : OpRewritePattern { LogicalResult matchAndRewrite(PowOp op, PatternRewriter& rewriter) const override { const auto exponent = op.getExponentValue(); - if (!exponent || std::abs(*exponent - 1.0) > TOLERANCE) { + if (!exponent || + std::abs(*exponent - 1.0) > PARAMETER_COMPARISON_TOLERANCE) { return failure(); } - utils::inlineModifierBody(op, *op.getBody(), op.getQubits(), rewriter); + mqt::inlineModifierBody(op, *op.getBody(), op.getQubits(), rewriter); return success(); } }; @@ -136,7 +140,7 @@ struct ErasePow0 final : OpRewritePattern { LogicalResult matchAndRewrite(PowOp op, PatternRewriter& rewriter) const override { const auto exponent = op.getExponentValue(); - if (!exponent || std::abs(*exponent) > TOLERANCE) { + if (!exponent || std::abs(*exponent) > PARAMETER_COMPARISON_TOLERANCE) { return failure(); } rewriter.eraseOp(op); @@ -152,8 +156,7 @@ struct NegPowToInvPow final : OpRewritePattern { const auto exponent = op.getExponentValue(); // U^{-r} = (U^{-1})^r only when r is an integer: for fractional r, // eigenvalue -1 yields (-1)^{-r} ≠ (-1)^r (conjugated phase factors). - if (!exponent || *exponent >= 0.0 || - !utils::isIntegerExponent(-*exponent)) { + if (!exponent || *exponent >= 0.0 || !mqt::isIntegerExponent(-*exponent)) { return failure(); } const double exp = *exponent; @@ -164,8 +167,8 @@ struct NegPowToInvPow final : OpRewritePattern { [&](ValueRange invArgs) { // Inline the old pow body, remapping its block args // to the new inv body's block args. - utils::inlineBodyReturningYields(*op.getBody(), - invArgs, rewriter); + mqt::inlineBodyReturningYields(*op.getBody(), invArgs, + rewriter); }); }); return success(); @@ -181,10 +184,10 @@ struct MergeNestedPow final : OpRewritePattern { // Principal matrix powers do not generally satisfy (U^b)^a = U^(a*b) // across branch cuts. The rewrite is valid for integral outer powers, // where any branch phase is raised to an integer and cancels. - if (!outerExponent || !utils::isIntegerExponent(*outerExponent)) { + if (!outerExponent || !mqt::isIntegerExponent(*outerExponent)) { return failure(); } - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -197,18 +200,18 @@ struct MergeNestedPow final : OpRewritePattern { // so the merged pow's footprint matches the inner pow positionally. auto outerQubits = op.getQubits(); const auto qubits = llvm::map_to_vector(innerPow.getQubits(), [&](Value v) { - return utils::getValueFromBlockArgument(v, outerQubits); + return mqt::getValueFromBlockArgument(v, outerQubits); }); // Move supporting ops (constants, arithmetic) out of the body so their // Values are accessible from outside and survive PowOp erasure. - utils::hoistSupportingOpsBefore(*op.getBody(), innerPow.getOperation(), op, - rewriter); + mqt::hoistSupportingOpsBefore(*op.getBody(), innerPow.getOperation(), op, + rewriter); auto merged = scaleByExponent(innerPow.getExponent(), op, rewriter); rewriter.replaceOpWithNewOp( op, merged, qubits, [&](ValueRange powArgs) { // Inner pow body args now match the new pow's args positionally. - utils::inlineBodyReturningYields(*innerPow.getBody(), powArgs, - rewriter); + mqt::inlineBodyReturningYields(*innerPow.getBody(), powArgs, + rewriter); }); return success(); } @@ -220,7 +223,7 @@ struct MoveCtrlOutsidePow final : OpRewritePattern { LogicalResult matchAndRewrite(PowOp op, PatternRewriter& rewriter) const override { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -236,19 +239,19 @@ struct MoveCtrlOutsidePow final : OpRewritePattern { auto outerQubits = op.getQubits(); const auto controls = llvm::map_to_vector(innerCtrlOp.getControls(), [&](Value c) { - return utils::getValueFromBlockArgument(c, outerQubits); + return mqt::getValueFromBlockArgument(c, outerQubits); }); const auto targets = llvm::map_to_vector(innerCtrlOp.getTargets(), [&](Value t) { - return utils::getValueFromBlockArgument(t, outerQubits); + return mqt::getValueFromBlockArgument(t, outerQubits); }); rewriter.replaceOpWithNewOp( op, controls, targets, [&](ValueRange targetArgs) { PowOp::create(rewriter, op.getLoc(), op.getExponent(), targetArgs, [&](ValueRange powArgs) { - utils::inlineBodyReturningYields( - *innerCtrlOp.getBody(), powArgs, rewriter); + mqt::inlineBodyReturningYields(*innerCtrlOp.getBody(), + powArgs, rewriter); }); }); @@ -274,7 +277,7 @@ struct FoldPowIntoGate final : OpRewritePattern { LogicalResult matchAndRewrite(PowOp op, PatternRewriter& rewriter) const override { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -305,13 +308,13 @@ struct FoldPowIntoGate final : OpRewritePattern { // principal branch. Keep arbitrary parameters inside fractional powers. if (isa(innerOp) && - !utils::isIntegerExponent(r)) { + !mqt::isIntegerExponent(r)) { return failure(); } // HOp, ECROp, RCCXOp, and SWAPOp also only have the simple parity fold for // integral exponents. if (isa(innerOp) && - !utils::isIntegerExponent(r)) { + !mqt::isIntegerExponent(r)) { return failure(); } if (!isa { // Move supporting ops (constants, arithmetic) out of the body so their // Values are accessible from outside and survive PowOp erasure. - utils::hoistSupportingOpsBefore(*op.getBody(), innerOp, op, rewriter); + mqt::hoistSupportingOpsBefore(*op.getBody(), innerOp, op, rewriter); return TypeSwitch(innerOp) // --- Rotation gates: multiply angle by exponent --- @@ -338,8 +341,7 @@ struct FoldPowIntoGate final : OpRewritePattern { auto newParam = scaleByExponent(gate.getTheta(), op, rewriter); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), newParam); return success(); }) @@ -348,10 +350,8 @@ struct FoldPowIntoGate final : OpRewritePattern { auto newParam = scaleByExponent(gate.getTheta(), op, rewriter); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), - utils::getValueFromBlockArgument(gate.getTarget(1), - op.getQubits()), + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), + mqt::getValueFromBlockArgument(gate.getTarget(1), op.getQubits()), newParam); return success(); }) @@ -360,8 +360,7 @@ struct FoldPowIntoGate final : OpRewritePattern { auto mul = scaleByExponent(gate.getTheta(), op, rewriter); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), mul, gate.getPhi()); return success(); }) @@ -370,17 +369,16 @@ struct FoldPowIntoGate final : OpRewritePattern { auto mul = scaleByExponent(gate.getTheta(), op, rewriter); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), - utils::getValueFromBlockArgument(gate.getTarget(1), - op.getQubits()), + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), + mqt::getValueFromBlockArgument(gate.getTarget(1), op.getQubits()), mul, gate.getBeta()); return success(); }) // pow(n) { u(theta, phi, lambda) } => // gphase(delta); u(theta', phi', lambda') .Case([&](auto) { - if (std::abs(normalizeAngle(uPower->phase)) > TOLERANCE) { + if (std::abs(normalizeAngle(uPower->phase)) > + PARAMETER_COMPARISON_TOLERANCE) { GPhaseOp::create(rewriter, loc, uPower->phase); } rewriter.replaceOpWithNewOp(op, op.getTarget(0), uPower->theta, @@ -393,59 +391,56 @@ struct FoldPowIntoGate final : OpRewritePattern { const double angle = r * std::numbers::pi; if (succeeded(tryReplacePOpWithNamedGate( angle, op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), + mqt::getValueFromBlockArgument(gate.getTarget(0), + op.getQubits()), rewriter))) { return success(); } rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * std::numbers::pi)); + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * std::numbers::pi)); return success(); }) // pow(r) { x } => gphase(r*π/2); rx(r*π) // pow(1/2) x => sx (X^(1/2) = SX exactly) // pow(-1/2) x => sxdg (X^(-1/2) = SXdg exactly) .Case([&](auto gate) { - if (std::abs(r - 0.5) < TOLERANCE) { + if (std::abs(r - 0.5) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp( - op, utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits())); + op, mqt::getValueFromBlockArgument(gate.getTarget(0), + op.getQubits())); return success(); } - if (std::abs(r + 0.5) < TOLERANCE) { + if (std::abs(r + 0.5) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp( - op, utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits())); + op, mqt::getValueFromBlockArgument(gate.getTarget(0), + op.getQubits())); return success(); } GPhaseOp::create( rewriter, loc, - utils::constantFromScalar(rewriter, op.getLoc(), - r * (std::numbers::pi / 2.0))); + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (std::numbers::pi / 2.0))); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * std::numbers::pi)); + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * std::numbers::pi)); return success(); }) // pow(r) { y } => gphase(r*π/2); ry(r*π) .Case([&](auto gate) { GPhaseOp::create( rewriter, loc, - utils::constantFromScalar(rewriter, op.getLoc(), - r * (std::numbers::pi / 2.0))); + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (std::numbers::pi / 2.0))); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * std::numbers::pi)); + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * std::numbers::pi)); return success(); }) // --- Phase/diagonal gates: named gate if angle matches, else P gate @@ -454,17 +449,16 @@ struct FoldPowIntoGate final : OpRewritePattern { const double angle = r * std::numbers::pi / 2.0; if (succeeded(tryReplacePOpWithNamedGate( angle, op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), + mqt::getValueFromBlockArgument(gate.getTarget(0), + op.getQubits()), rewriter))) { return success(); } rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (std::numbers::pi / 2.0))); + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (std::numbers::pi / 2.0))); return success(); }) // pow(r) { sdg } => named gate if angle matches, else p(-r*π/2) @@ -472,17 +466,16 @@ struct FoldPowIntoGate final : OpRewritePattern { const double angle = r * -std::numbers::pi / 2.0; if (succeeded(tryReplacePOpWithNamedGate( angle, op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), + mqt::getValueFromBlockArgument(gate.getTarget(0), + op.getQubits()), rewriter))) { return success(); } rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (-std::numbers::pi / 2.0))); + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (-std::numbers::pi / 2.0))); return success(); }) // pow(r) { t } => named gate if angle matches, else p(r*π/4) @@ -490,17 +483,16 @@ struct FoldPowIntoGate final : OpRewritePattern { const double angle = r * std::numbers::pi / 4.0; if (succeeded(tryReplacePOpWithNamedGate( angle, op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), + mqt::getValueFromBlockArgument(gate.getTarget(0), + op.getQubits()), rewriter))) { return success(); } rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (std::numbers::pi / 4.0))); + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (std::numbers::pi / 4.0))); return success(); }) // pow(r) { tdg } => named gate if angle matches, else p(-r*π/4) @@ -508,66 +500,63 @@ struct FoldPowIntoGate final : OpRewritePattern { const double angle = r * -std::numbers::pi / 4.0; if (succeeded(tryReplacePOpWithNamedGate( angle, op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), + mqt::getValueFromBlockArgument(gate.getTarget(0), + op.getQubits()), rewriter))) { return success(); } rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (-std::numbers::pi / 4.0))); + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (-std::numbers::pi / 4.0))); return success(); }) // --- SX/SXdg gates: decompose to rotation + global phase --- // pow(r) { sx } => gphase(r*π/4); rx(r*π/2) // pow(±2) sx => x .Case([&](auto gate) { - if (std::abs(std::abs(r) - 2.0) < TOLERANCE) { + if (std::abs(std::abs(r) - 2.0) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp( - op, utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits())); + op, mqt::getValueFromBlockArgument(gate.getTarget(0), + op.getQubits())); return success(); } GPhaseOp::create( rewriter, loc, - utils::constantFromScalar(rewriter, op.getLoc(), - r * (std::numbers::pi / 4.0))); + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (std::numbers::pi / 4.0))); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (std::numbers::pi / 2.0))); + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (std::numbers::pi / 2.0))); return success(); }) // pow(r) { sxdg } => gphase(-r*π/4); rx(-r*π/2) // pow(±2) sxdg => x .Case([&](auto gate) { - if (std::abs(std::abs(r) - 2.0) < TOLERANCE) { + if (std::abs(std::abs(r) - 2.0) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp( - op, utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits())); + op, mqt::getValueFromBlockArgument(gate.getTarget(0), + op.getQubits())); return success(); } GPhaseOp::create( rewriter, loc, - utils::constantFromScalar(rewriter, op.getLoc(), - r * (-std::numbers::pi / 4.0))); + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (-std::numbers::pi / 4.0))); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (-std::numbers::pi / 2.0))); + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (-std::numbers::pi / 2.0))); return success(); }) // --- Hermitian gates (integer exponent): even => erase/id, odd => gate // --- pow(n) { h } => id (n even) | h (n odd) .Case([&](auto) { - if (utils::isEvenExponent(r)) { + if (mqt::isEvenExponent(r)) { // pow(even) { h } => identity. Erase it. rewriter.eraseOp(op); } else { @@ -575,21 +564,21 @@ struct FoldPowIntoGate final : OpRewritePattern { // block args; inline the body, remapping them to the outer qubit // operands, instead of hoisting the gate out (which would leave it // referencing the erased block args). - utils::inlineModifierBody(op, *op.getBody(), op.getQubits(), - rewriter); + mqt::inlineModifierBody(op, *op.getBody(), op.getQubits(), + rewriter); } return success(); }) // pow(n) { ecr/rccx/swap } => erase (n even) | gate (n odd) .Case([&](auto) { - if (utils::isEvenExponent(r)) { + if (mqt::isEvenExponent(r)) { // pow(even) { ecr/rccx/swap } => identity. Erase it. rewriter.eraseOp(op); } else { // pow(odd) { ecr/rccx/swap } => gate. Inline the body, remapping // its block args to the outer qubit operands (see HOp case above). - utils::inlineModifierBody(op, *op.getBody(), op.getQubits(), - rewriter); + mqt::inlineModifierBody(op, *op.getBody(), op.getQubits(), + rewriter); } return success(); }) @@ -598,28 +587,26 @@ struct FoldPowIntoGate final : OpRewritePattern { .Case([&](auto gate) { rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits()), - utils::getValueFromBlockArgument(gate.getTarget(1), - op.getQubits()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (-std::numbers::pi)), - utils::constantFromScalar(rewriter, op.getLoc(), 0.0)); + mqt::getValueFromBlockArgument(gate.getTarget(0), op.getQubits()), + mqt::getValueFromBlockArgument(gate.getTarget(1), op.getQubits()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (-std::numbers::pi)), + mqt::constantFromScalar(rewriter, op.getLoc(), 0.0)); return success(); }) // --- Identity and barrier: pass through unchanged --- // pow(r) { id } => id .Case([&](auto gate) { rewriter.replaceOpWithNewOp( - op, utils::getValueFromBlockArgument(gate.getTarget(0), - op.getQubits())); + op, mqt::getValueFromBlockArgument(gate.getTarget(0), + op.getQubits())); return success(); }) // pow(r) { barrier } => barrier .Case([&](auto gate) { rewriter.replaceOpWithNewOp( op, llvm::map_to_vector(gate.getTargets(), [&](Value qubit) { - return utils::getValueFromBlockArgument(qubit, op.getQubits()); + return mqt::getValueFromBlockArgument(qubit, op.getQubits()); })); return success(); }) @@ -669,15 +656,15 @@ struct DropUnusedPowQubits final : OpRewritePattern { } // namespace std::optional PowOp::getExponentValue() { - return utils::valueToDouble(getExponent()); + return mlir::mqt::valueToDouble(getExponent()); } size_t PowOp::getNumBodyUnitaries() { - return utils::getNumBodyUnitaries(*getBody()); + return mqt::getNumBodyUnitaries(*getBody()); } UnitaryOpInterface PowOp::getBodyUnitary(const size_t i) { - return utils::getBodyUnitary(*getBody(), i); + return mqt::getBodyUnitary(*getBody(), i); } void PowOp::build(OpBuilder& odsBuilder, OperationState& odsState, diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/GPhaseOp.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/GPhaseOp.cpp index 5a010790e2..85bbf1c141 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/GPhaseOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/GPhaseOp.cpp @@ -8,8 +8,9 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Angles.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -20,7 +21,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void GPhaseOp::build(OpBuilder& odsBuilder, OperationState& odsState, const std::variant& theta) { @@ -29,10 +30,5 @@ void GPhaseOp::build(OpBuilder& odsBuilder, OperationState& odsState, } LogicalResult GPhaseOp::verify() { - const auto theta = valueToConstantDouble(getTheta()); - if (theta && std::abs(*theta) > MAX_GLOBAL_PHASE_ANGLE) { - return emitOpError() << "constant angle must have magnitude at most " - << MAX_GLOBAL_PHASE_ANGLE << " radians"; - } - return success(); + return verifyGlobalPhaseAngle(getOperation(), getTheta()); } diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/POp.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/POp.cpp index 4e61fddfad..e4c4b5c866 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/POp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/POp.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -18,7 +18,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void POp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, const std::variant& theta) { diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/ROp.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/ROp.cpp index 05e8378958..f84d368b1a 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/ROp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/ROp.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -18,7 +18,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void ROp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, const std::variant& theta, diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RXOp.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RXOp.cpp index 3cb66f0254..7cb025e7a3 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RXOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RXOp.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -18,7 +18,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void RXOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, const std::variant& theta) { diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RXXOp.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RXXOp.cpp index 56ad9556df..354264e564 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RXXOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RXXOp.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -18,7 +18,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void RXXOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubit0In, Value qubit1In, diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RYOp.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RYOp.cpp index b20092ce21..8737b06be6 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RYOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RYOp.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -18,7 +18,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void RYOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, const std::variant& theta) { diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RYYOp.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RYYOp.cpp index d1169afe2c..d04eaba4dd 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RYYOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RYYOp.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -18,7 +18,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void RYYOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubit0In, Value qubit1In, diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RZOp.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RZOp.cpp index 51dd85af93..263e0ce68b 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RZOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RZOp.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -18,7 +18,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void RZOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, const std::variant& theta) { diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RZXOp.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RZXOp.cpp index 7f9ea437cd..1fd22806bd 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RZXOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RZXOp.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -18,7 +18,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void RZXOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubit0In, Value qubit1In, diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RZZOp.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RZZOp.cpp index 6590726bb1..df543386a4 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RZZOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/RZZOp.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -18,7 +18,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void RZZOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubit0In, Value qubit1In, diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/U2Op.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/U2Op.cpp index ced78281ed..62f68fe358 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/U2Op.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/U2Op.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -18,7 +18,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void U2Op::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, const std::variant& phi, diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/UOp.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/UOp.cpp index 7539b6e773..10f19a0f80 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/UOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/UOp.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -18,7 +18,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void UOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, const std::variant& theta, diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/XXMinusYYOp.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/XXMinusYYOp.cpp index 00fa8b7c12..da7c909c3a 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/XXMinusYYOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/XXMinusYYOp.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -18,7 +18,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void XXMinusYYOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubit0In, Value qubit1In, diff --git a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/XXPlusYYOp.cpp b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/XXPlusYYOp.cpp index 96d8f0fa32..ee53349d67 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/StandardGates/XXPlusYYOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/StandardGates/XXPlusYYOp.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -18,7 +18,7 @@ using namespace mlir; using namespace mlir::qc; -using namespace mlir::utils; +using namespace mlir::mqt; void XXPlusYYOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubit0In, Value qubit1In, diff --git a/mlir/lib/Dialect/QC/IR/Operations/UnitaryOp.cpp b/mlir/lib/Dialect/QC/IR/Operations/UnitaryOp.cpp index 92a95d60e5..63ea007a94 100644 --- a/mlir/lib/Dialect/QC/IR/Operations/UnitaryOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Operations/UnitaryOp.cpp @@ -8,8 +8,8 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/DenseUnitary.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/DenseUnitary.h" #include #include @@ -25,7 +25,7 @@ struct EraseIdentityUnitary final : OpRewritePattern { LogicalResult matchAndRewrite(UnitaryOp op, PatternRewriter& rewriter) const override { - if (!utils::isExactIdentityMatrix(op.getMatrix())) { + if (!mqt::isExactIdentityMatrix(op.getMatrix())) { return failure(); } rewriter.eraseOp(op); @@ -36,8 +36,8 @@ struct EraseIdentityUnitary final : OpRewritePattern { } // namespace LogicalResult UnitaryOp::verify() { - return utils::verifyDenseUnitaryMatrix(getOperation(), getMatrix(), - getQubits()); + return mqt::verifyDenseUnitaryMatrix(getOperation(), getMatrix(), + getQubits()); } void UnitaryOp::getCanonicalizationPatterns(RewritePatternSet& results, diff --git a/mlir/lib/Dialect/QC/IR/QCOps.cpp b/mlir/lib/Dialect/QC/IR/QCOps.cpp index 19378d60b1..f6b6944c6e 100644 --- a/mlir/lib/Dialect/QC/IR/QCOps.cpp +++ b/mlir/lib/Dialect/QC/IR/QCOps.cpp @@ -10,8 +10,9 @@ #include "mlir/Dialect/QC/IR/QCOps.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/IR/QCDialect.h" // IWYU pragma: associated -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -32,12 +33,12 @@ using namespace mlir::qc; static ParseResult parseTargetAliasing(OpAsmParser& parser, Region& region, SmallVectorImpl& operands) { - return utils::parseTargetAliasing(parser, region, operands); + return mqt::parseTargetAliasing(parser, region, operands); } static void printTargetAliasing(OpAsmPrinter& printer, Operation* /*op*/, Region& region, OperandRange targetsIn) { - utils::printTargetAliasing(printer, region, targetsIn); + mqt::printTargetAliasing(printer, region, targetsIn); } //===----------------------------------------------------------------------===// @@ -73,7 +74,7 @@ void QCDialect::initialize() { //===----------------------------------------------------------------------===// LogicalResult mlir::qc::verifyUnitaryOpInterface(Operation* op) { - return utils::verifyFiniteConstantParameters( + return mqt::verifyFiniteConstantParameters( op, cast(op).getParameters()); } diff --git a/mlir/lib/Dialect/QCO/Builder/CMakeLists.txt b/mlir/lib/Dialect/QCO/Builder/CMakeLists.txt index 344c9abe83..d8f282e109 100644 --- a/mlir/lib/Dialect/QCO/Builder/CMakeLists.txt +++ b/mlir/lib/Dialect/QCO/Builder/CMakeLists.txt @@ -17,7 +17,9 @@ add_mlir_library( MLIRMQTDialect MLIRSCFDialect MLIRQCODialect - MLIRQTensorDialect) + MLIRQTensorDialect + PRIVATE + MLIRMQTUtils) mqt_mlir_target_use_project_options(MLIRQCOProgramBuilder) diff --git a/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp b/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp index e0056cce32..722afea9f8 100644 --- a/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp +++ b/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp @@ -14,12 +14,12 @@ #include "mlir/Dialect/CBit/IR/CBitDialect.h" #include "mlir/Dialect/CBit/IR/CBitOps.h" #include "mlir/Dialect/MQT/IR/MQTDialect.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" #include "mlir/Dialect/QTensor/IR/QTensorOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -49,7 +49,7 @@ #include #include -using namespace mlir::utils; +using namespace mlir::mqt; namespace mlir::qco { QCOProgramBuilder::QCOProgramBuilder(MLIRContext* context) diff --git a/mlir/lib/Dialect/QCO/IR/CMakeLists.txt b/mlir/lib/Dialect/QCO/IR/CMakeLists.txt index 75311615ef..e779bf21c0 100644 --- a/mlir/lib/Dialect/QCO/IR/CMakeLists.txt +++ b/mlir/lib/Dialect/QCO/IR/CMakeLists.txt @@ -26,6 +26,7 @@ add_mlir_dialect_library( MLIRQCOInterfacesIncGen LINK_LIBS PUBLIC + MLIRMQTUtils MLIRQCOMatrix PRIVATE MLIRCBitDialect diff --git a/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp b/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp index 1d51472c4d..19139a9bbb 100644 --- a/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp @@ -9,12 +9,12 @@ */ #include "ModifierUtils.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" #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/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -59,7 +59,7 @@ struct MergeNestedCtrl final : OpRewritePattern { return failure(); } - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -85,17 +85,16 @@ struct MergeNestedCtrl final : OpRewritePattern { SmallVector controls(op.getControlsIn()); for (auto control : innerControls) { - controls.push_back( - utils::getValueFromBlockArgument(control, outerTargets)); + controls.push_back(mqt::getValueFromBlockArgument(control, outerTargets)); } const auto targets = llvm::map_to_vector(innerTargets, [&](Value t) { - return utils::getValueFromBlockArgument(t, outerTargets); + return mqt::getValueFromBlockArgument(t, outerTargets); }); auto merged = CtrlOp::create(rewriter, op.getLoc(), controls, targets, [&](ValueRange mergedTargets) -> SmallVector { - return utils::inlineBodyReturningYields( + return mqt::inlineBodyReturningYields( *innerCtrlOp.getBody(), mergedTargets, rewriter); }); @@ -118,7 +117,7 @@ struct ReduceCtrl final : OpRewritePattern { using OpRewritePattern::OpRewritePattern; LogicalResult matchAndRewrite(CtrlOp op, PatternRewriter& rewriter) const override { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -248,11 +247,11 @@ struct DropUnusedTargets final : OpRewritePattern { } // namespace size_t CtrlOp::getNumBodyUnitaries() { - return utils::getNumBodyUnitaries(*getBody()); + return mqt::getNumBodyUnitaries(*getBody()); } UnitaryOpInterface CtrlOp::getBodyUnitary(const size_t i) { - return utils::getBodyUnitary(*getBody(), i); + return mqt::getBodyUnitary(*getBody(), i); } Value CtrlOp::getInputForOutput(Value output) { @@ -276,12 +275,12 @@ void CtrlOp::build(OpBuilder& odsBuilder, OperationState& odsState, ValueRange controls, ValueRange targets, function_ref(ValueRange)> bodyBuilder) { build(odsBuilder, odsState, controls, targets); - utils::buildModifierBody(odsBuilder, odsState, targets.size(), - [&](OpBuilder& builder, Block& block) { - YieldOp::create( - builder, odsState.location, - bodyBuilder(block.getArguments())); - }); + mqt::buildModifierBody(odsBuilder, odsState, targets.size(), + [&](OpBuilder& builder, Block& block) { + YieldOp::create( + builder, odsState.location, + bodyBuilder(block.getArguments())); + }); } void CtrlOp::build(OpBuilder& odsBuilder, OperationState& odsState, @@ -289,7 +288,7 @@ void CtrlOp::build(OpBuilder& odsBuilder, OperationState& odsState, function_ref bodyBuilder) { build(odsBuilder, odsState, controls.getTypes(), target.getType(), controls, target); - utils::buildModifierBody( + mqt::buildModifierBody( odsBuilder, odsState, 1, [&](OpBuilder& builder, Block& block) { YieldOp::create(builder, odsState.location, bodyBuilder(block.getArgument(0))); @@ -385,7 +384,7 @@ std::optional CtrlOp::getUnitaryMatrix() { // Single inner unitary (e.g. `ctrl { h }`, `ctrl { cx }`). if (auto bodyUnitary = - utils::getSoleBodyUnitary(*getBody())) { + mqt::getSoleBodyUnitary(*getBody())) { if (const auto targetMatrix = bodyUnitary.getUnitaryMatrix()) { assert(targetMatrix->cols() == targetMatrix->rows()); diff --git a/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp b/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp index ae6b464040..a54ad37cc3 100644 --- a/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp @@ -9,12 +9,12 @@ */ #include "ModifierUtils.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" #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/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -50,7 +50,7 @@ struct MoveCtrlOutsideInv final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp op, PatternRewriter& rewriter) const override { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -73,11 +73,11 @@ struct MoveCtrlOutsideInv final : OpRewritePattern { auto outerQubits = op.getQubitsIn(); const auto controls = llvm::map_to_vector(innerCtrlOp.getControlsIn(), [&](Value c) { - return utils::getValueFromBlockArgument(c, outerQubits); + return mqt::getValueFromBlockArgument(c, outerQubits); }); const auto targets = llvm::map_to_vector(innerCtrlOp.getTargetsIn(), [&](Value t) { - return utils::getValueFromBlockArgument(t, outerQubits); + return mqt::getValueFromBlockArgument(t, outerQubits); }); auto newCtrl = @@ -86,7 +86,7 @@ struct MoveCtrlOutsideInv final : OpRewritePattern { auto innerInv = InvOp::create( rewriter, op.getLoc(), targetArgs, [&](ValueRange invArgs) -> SmallVector { - return utils::inlineBodyReturningYields( + return mqt::inlineBodyReturningYields( *innerCtrlOp.getBody(), invArgs, rewriter); }); return innerInv.getResults(); @@ -115,8 +115,7 @@ struct InvPowToNegPow final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp invOp, PatternRewriter& rewriter) const override { - auto inner = - utils::getSoleBodyUnitary(*invOp.getBody()); + auto inner = mqt::getSoleBodyUnitary(*invOp.getBody()); if (!inner) { return failure(); } @@ -133,8 +132,8 @@ struct InvPowToNegPow final : OpRewritePattern { // Move supporting ops (constants, arithmetic) out of the body so their // Values are accessible from outside and survive InvOp erasure. - utils::hoistSupportingOpsBefore(*invOp.getBody(), innerPow.getOperation(), - invOp, rewriter); + mqt::hoistSupportingOpsBefore(*invOp.getBody(), innerPow.getOperation(), + invOp, rewriter); Value negExponent = arith::NegFOp::create(rewriter, invOp.getLoc(), innerPow.getExponent()); // The inner pow's operands alias the inv's block args; translate them back @@ -143,13 +142,13 @@ struct InvPowToNegPow final : OpRewritePattern { auto outerQubits = invOp.getQubitsIn(); const auto qubits = llvm::map_to_vector(innerPow.getInputQubits(), [&](Value v) { - return utils::getValueFromBlockArgument(v, outerQubits); + return mqt::getValueFromBlockArgument(v, outerQubits); }); auto newPow = PowOp::create(rewriter, invOp.getLoc(), qubits, negExponent, [&](ValueRange powArgs) -> llvm::SmallVector { - return utils::inlineBodyReturningYields( + return mqt::inlineBodyReturningYields( *innerPow.getBody(), powArgs, rewriter); }); @@ -174,7 +173,7 @@ struct InlineSelfAdjoint final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp op, PatternRewriter& rewriter) const override { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -186,7 +185,7 @@ struct InlineSelfAdjoint final : OpRewritePattern { // A self-adjoint gate is its own inverse, so the modifier can be dropped // and its body applied directly to the input qubits. - utils::inlineModifierBody(op, *op.getBody(), op.getInputQubits(), rewriter); + mqt::inlineModifierBody(op, *op.getBody(), op.getInputQubits(), rewriter); return success(); } }; @@ -202,7 +201,7 @@ struct ReplaceWithKnownGates final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp op, PatternRewriter& rewriter) const override { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -338,7 +337,7 @@ struct ReplaceWithKnownGates final : OpRewritePattern { return failure(); } - utils::inlineModifierBody(op, *op.getBody(), op.getInputQubits(), rewriter); + mqt::inlineModifierBody(op, *op.getBody(), op.getInputQubits(), rewriter); return success(); } }; @@ -351,7 +350,7 @@ struct CancelNestedInv final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp op, PatternRewriter& rewriter) const override { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -366,7 +365,7 @@ struct CancelNestedInv final : OpRewritePattern { return failure(); } - if (!utils::getSoleBodyUnitary(*innerInvOp.getBody())) { + if (!mqt::getSoleBodyUnitary(*innerInvOp.getBody())) { return failure(); } @@ -375,10 +374,9 @@ struct CancelNestedInv final : OpRewritePattern { // inputs, which in turn alias the outer input qubits. const auto replacements = llvm::map_to_vector(innerInvOp.getInputQubits(), [&](Value q) { - return utils::getValueFromBlockArgument(q, op.getInputQubits()); + return mqt::getValueFromBlockArgument(q, op.getInputQubits()); }); - utils::inlineModifierBody(op, *innerInvOp.getBody(), replacements, - rewriter); + mqt::inlineModifierBody(op, *innerInvOp.getBody(), replacements, rewriter); return success(); } }; @@ -427,11 +425,11 @@ struct DropUnusedInvQubits final : OpRewritePattern { } // namespace size_t InvOp::getNumBodyUnitaries() { - return utils::getNumBodyUnitaries(*getBody()); + return mqt::getNumBodyUnitaries(*getBody()); } UnitaryOpInterface InvOp::getBodyUnitary(const size_t i) { - return utils::getBodyUnitary(*getBody(), i); + return mqt::getBodyUnitary(*getBody(), i); } Value InvOp::getInputForOutput(Value output) { @@ -455,18 +453,18 @@ void InvOp::build(OpBuilder& odsBuilder, OperationState& odsState, ValueRange qubits, function_ref(ValueRange)> bodyBuilder) { build(odsBuilder, odsState, qubits); - utils::buildModifierBody(odsBuilder, odsState, qubits.size(), - [&](OpBuilder& builder, Block& block) { - YieldOp::create( - builder, odsState.location, - bodyBuilder(block.getArguments())); - }); + mqt::buildModifierBody(odsBuilder, odsState, qubits.size(), + [&](OpBuilder& builder, Block& block) { + YieldOp::create( + builder, odsState.location, + bodyBuilder(block.getArguments())); + }); } void InvOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubit, function_ref bodyBuilder) { build(odsBuilder, odsState, qubit.getType(), qubit); - utils::buildModifierBody( + mqt::buildModifierBody( odsBuilder, odsState, 1, [&](OpBuilder& builder, Block& block) { YieldOp::create(builder, odsState.location, bodyBuilder(block.getArgument(0))); @@ -529,7 +527,7 @@ std::optional InvOp::getUnitaryMatrix() { // Single inner unitary (e.g. `inv { h }`, `inv { cx }`). if (auto bodyUnitary = - utils::getSoleBodyUnitary(*getBody())) { + mqt::getSoleBodyUnitary(*getBody())) { if (const auto targetMatrix = bodyUnitary.getUnitaryMatrix()) { return targetMatrix->adjoint(); diff --git a/mlir/lib/Dialect/QCO/IR/Modifiers/ModifierUtils.cpp b/mlir/lib/Dialect/QCO/IR/Modifiers/ModifierUtils.cpp index 9f0e564964..3cbf1fcf4b 100644 --- a/mlir/lib/Dialect/QCO/IR/Modifiers/ModifierUtils.cpp +++ b/mlir/lib/Dialect/QCO/IR/Modifiers/ModifierUtils.cpp @@ -11,9 +11,9 @@ #include "ModifierUtils.h" #include "mlir/Dialect/CBit/IR/CBitOps.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -107,7 +107,7 @@ SmallVector inlineNarrowedBody(Block& body, ValueRange qubits, } const auto yielded = - utils::inlineBodyReturningYields(body, replacements, rewriter); + mqt::inlineBodyReturningYields(body, replacements, rewriter); return llvm::map_to_vector( used, [&](const size_t index) { return yielded[index]; }); } diff --git a/mlir/lib/Dialect/QCO/IR/Modifiers/PowOp.cpp b/mlir/lib/Dialect/QCO/IR/Modifiers/PowOp.cpp index c3d3f7183e..d0f58d6aaf 100644 --- a/mlir/lib/Dialect/QCO/IR/Modifiers/PowOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Modifiers/PowOp.cpp @@ -9,13 +9,16 @@ */ #include "ModifierUtils.h" +#include "mlir/Dialect/MQT/Utils/Angles.h" +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/GatePowering.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #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/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/UGateUtils.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -42,7 +45,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; static void replacePowResults(PowOp powOp, UnitaryOpInterface bodyUnitary, ValueRange replacementOutputs, @@ -100,28 +103,28 @@ static LogicalResult tryReplacePOpWithNamedGate(double angle, PowOp op, const double norm = normalizeAngle(angle); const double pi = std::numbers::pi; - if (std::abs(norm) < TOLERANCE) { + if (std::abs(norm) < PARAMETER_COMPARISON_TOLERANCE) { // pow(r) folds to the identity: thread the input qubits to the results. rewriter.replaceOp(op, op.getQubitsIn()); return success(); } - if (std::abs(std::abs(norm) - pi) < TOLERANCE) { + if (std::abs(std::abs(norm) - pi) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp(op, target); return success(); } - if (std::abs(norm - (pi / 2.0)) < TOLERANCE) { + if (std::abs(norm - (pi / 2.0)) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp(op, target); return success(); } - if (std::abs(norm + (pi / 2.0)) < TOLERANCE) { + if (std::abs(norm + (pi / 2.0)) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp(op, target); return success(); } - if (std::abs(norm - (pi / 4.0)) < TOLERANCE) { + if (std::abs(norm - (pi / 4.0)) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp(op, target); return success(); } - if (std::abs(norm + (pi / 4.0)) < TOLERANCE) { + if (std::abs(norm + (pi / 4.0)) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp(op, target); return success(); } @@ -141,11 +144,12 @@ struct InlinePow1 final : OpRewritePattern { LogicalResult matchAndRewrite(PowOp op, PatternRewriter& rewriter) const override { const auto exponent = op.getExponentValue(); - if (!exponent || std::abs(*exponent - 1.0) > TOLERANCE) { + if (!exponent || + std::abs(*exponent - 1.0) > PARAMETER_COMPARISON_TOLERANCE) { return failure(); } - utils::inlineModifierBody(op, *op.getBody(), op.getInputQubits(), rewriter); + mqt::inlineModifierBody(op, *op.getBody(), op.getInputQubits(), rewriter); return success(); } }; @@ -157,7 +161,7 @@ struct ErasePow0 final : OpRewritePattern { LogicalResult matchAndRewrite(PowOp op, PatternRewriter& rewriter) const override { const auto exponent = op.getExponentValue(); - if (!exponent || std::abs(*exponent) > TOLERANCE) { + if (!exponent || std::abs(*exponent) > PARAMETER_COMPARISON_TOLERANCE) { return failure(); } @@ -176,8 +180,7 @@ struct NegPowToInvPow final : OpRewritePattern { const auto exponent = op.getExponentValue(); // U^{-r} = (U^{-1})^r only when r is an integer: for fractional r, // eigenvalue -1 yields (-1)^{-r} ≠ (-1)^r (conjugated phase factors). - if (!exponent || *exponent >= 0.0 || - !utils::isIntegerExponent(-*exponent)) { + if (!exponent || *exponent >= 0.0 || !mqt::isIntegerExponent(-*exponent)) { return failure(); } const double exp = *exponent; @@ -187,7 +190,7 @@ struct NegPowToInvPow final : OpRewritePattern { [&](ValueRange powArgs) -> SmallVector { return InvOp::create(rewriter, op.getLoc(), powArgs, [&](ValueRange invArgs) -> SmallVector { - return utils::inlineBodyReturningYields( + return mqt::inlineBodyReturningYields( *op.getBody(), invArgs, rewriter); }) .getResults(); @@ -207,11 +210,11 @@ struct MergeNestedPow final : OpRewritePattern { // Principal matrix powers do not generally satisfy (U^b)^a = U^(a*b) // across branch cuts. The rewrite is valid for integral outer powers, // where any branch phase is raised to an integer and cancels. - if (!outerExponent || !utils::isIntegerExponent(*outerExponent)) { + if (!outerExponent || !mqt::isIntegerExponent(*outerExponent)) { return failure(); } auto bodyUnitary = - utils::getSoleBodyUnitary(*op.getBody()); + mqt::getSoleBodyUnitary(*op.getBody()); if (!bodyUnitary) { return failure(); } @@ -232,19 +235,19 @@ struct MergeNestedPow final : OpRewritePattern { auto outerQubits = op.getQubitsIn(); const auto qubits = llvm::map_to_vector(innerPow.getInputQubits(), [&](Value v) { - return utils::getValueFromBlockArgument(v, outerQubits); + return mqt::getValueFromBlockArgument(v, outerQubits); }); // Move supporting ops (constants, arithmetic) out of the body so their // Values are accessible from outside and survive PowOp erasure. - utils::hoistSupportingOpsBefore(*op.getBody(), innerPow.getOperation(), op, - rewriter); + mqt::hoistSupportingOpsBefore(*op.getBody(), innerPow.getOperation(), op, + rewriter); Value merged = scaleByExponent(innerPow.getExponent(), op, rewriter); auto newPow = PowOp::create(rewriter, op.getLoc(), qubits, merged, [&](ValueRange powArgs) -> llvm::SmallVector { // Inner pow body args now match the new pow's args // positionally. - return utils::inlineBodyReturningYields( + return mqt::inlineBodyReturningYields( *innerPow.getBody(), powArgs, rewriter); }); @@ -265,7 +268,7 @@ struct MoveCtrlOutsidePow final : OpRewritePattern { LogicalResult matchAndRewrite(PowOp op, PatternRewriter& rewriter) const override { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -288,11 +291,11 @@ struct MoveCtrlOutsidePow final : OpRewritePattern { auto outerQubits = op.getQubitsIn(); const auto controls = llvm::map_to_vector(innerCtrlOp.getControlsIn(), [&](Value c) { - return utils::getValueFromBlockArgument(c, outerQubits); + return mqt::getValueFromBlockArgument(c, outerQubits); }); const auto targets = llvm::map_to_vector(innerCtrlOp.getTargetsIn(), [&](Value t) { - return utils::getValueFromBlockArgument(t, outerQubits); + return mqt::getValueFromBlockArgument(t, outerQubits); }); auto newCtrl = CtrlOp::create( @@ -301,7 +304,7 @@ struct MoveCtrlOutsidePow final : OpRewritePattern { auto innerPow = PowOp::create(rewriter, op.getLoc(), targetArgs, op.getExponent(), [&](ValueRange powArgs) -> SmallVector { - return utils::inlineBodyReturningYields( + return mqt::inlineBodyReturningYields( *innerCtrlOp.getBody(), powArgs, rewriter); }); return innerPow.getResults(); @@ -336,7 +339,7 @@ struct FoldPowIntoGate final : OpRewritePattern { LogicalResult matchAndRewrite(PowOp op, PatternRewriter& rewriter) const override { auto bodyUnitary = - utils::getSoleBodyUnitary(*op.getBody()); + mqt::getSoleBodyUnitary(*op.getBody()); if (!bodyUnitary) { return failure(); } @@ -372,13 +375,13 @@ struct FoldPowIntoGate final : OpRewritePattern { // principal branch. Keep arbitrary parameters inside fractional powers. if (isa(innerOp) && - !utils::isIntegerExponent(r)) { + !mqt::isIntegerExponent(r)) { return failure(); } // HOp, ECROp, RCCXOp, and SWAPOp also only have the simple parity fold for // integral exponents. if (isa(innerOp) && - !utils::isIntegerExponent(r)) { + !mqt::isIntegerExponent(r)) { return failure(); } if (!isa { // Move supporting ops (constants, arithmetic) out of the body so their // Values are accessible from outside and survive PowOp erasure. - utils::hoistSupportingOpsBefore(*op.getBody(), innerOp, op, rewriter); + mqt::hoistSupportingOpsBefore(*op.getBody(), innerOp, op, rewriter); const LogicalResult result = TypeSwitch(innerOp) @@ -406,8 +409,8 @@ struct FoldPowIntoGate final : OpRewritePattern { auto newParam = scaleByExponent(gate.getTheta(), op, rewriter); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), newParam); return success(); }) @@ -416,10 +419,10 @@ struct FoldPowIntoGate final : OpRewritePattern { auto newParam = scaleByExponent(gate.getTheta(), op, rewriter); auto replacement = decltype(gate)::create( rewriter, op.getLoc(), - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), - utils::getValueFromBlockArgument(gate.getInputTarget(1), - op.getQubitsIn()), + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), + mqt::getValueFromBlockArgument(gate.getInputTarget(1), + op.getQubitsIn()), newParam); replacePowResults(op, gate, replacement.getOutputQubits(), rewriter); @@ -430,8 +433,8 @@ struct FoldPowIntoGate final : OpRewritePattern { auto mul = scaleByExponent(gate.getTheta(), op, rewriter); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), mul, gate.getPhi()); return success(); }) @@ -440,10 +443,10 @@ struct FoldPowIntoGate final : OpRewritePattern { auto mul = scaleByExponent(gate.getTheta(), op, rewriter); auto replacement = decltype(gate)::create( rewriter, op.getLoc(), - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), - utils::getValueFromBlockArgument(gate.getInputTarget(1), - op.getQubitsIn()), + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), + mqt::getValueFromBlockArgument(gate.getInputTarget(1), + op.getQubitsIn()), mul, gate.getBeta()); replacePowResults(op, gate, replacement.getOutputQubits(), rewriter); @@ -452,7 +455,8 @@ struct FoldPowIntoGate final : OpRewritePattern { // pow(n) { u(theta, phi, lambda) } => // gphase(delta); u(theta', phi', lambda') .Case([&](auto) { - if (std::abs(normalizeAngle(uPower->phase)) > TOLERANCE) { + if (std::abs(normalizeAngle(uPower->phase)) > + PARAMETER_COMPARISON_TOLERANCE) { GPhaseOp::create(rewriter, loc, uPower->phase); } rewriter.replaceOpWithNewOp(op, op.getInputTarget(0), @@ -465,42 +469,42 @@ struct FoldPowIntoGate final : OpRewritePattern { // pow(1/2) x => sx (X^(1/2) = SX exactly) // pow(-1/2) x => sxdg (X^(-1/2) = SXdg exactly) .Case([&](auto gate) { - if (std::abs(r - 0.5) < TOLERANCE) { + if (std::abs(r - 0.5) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp( - op, utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn())); + op, mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn())); return success(); } - if (std::abs(r + 0.5) < TOLERANCE) { + if (std::abs(r + 0.5) < PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp( - op, utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn())); + op, mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn())); return success(); } GPhaseOp::create( rewriter, loc, - utils::constantFromScalar(rewriter, op.getLoc(), - r * (std::numbers::pi / 2.0))); + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (std::numbers::pi / 2.0))); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * std::numbers::pi)); + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * std::numbers::pi)); return success(); }) // pow(r) { y } => gphase(r*π/2); ry(r*π) .Case([&](auto gate) { GPhaseOp::create( rewriter, loc, - utils::constantFromScalar(rewriter, op.getLoc(), - r * (std::numbers::pi / 2.0))); + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (std::numbers::pi / 2.0))); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * std::numbers::pi)); + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * std::numbers::pi)); return success(); }) // pow(r) { z } => named gate if angle matches, else p(r*π) @@ -508,17 +512,17 @@ struct FoldPowIntoGate final : OpRewritePattern { const double angle = r * std::numbers::pi; if (succeeded(tryReplacePOpWithNamedGate( angle, op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), rewriter))) { return success(); } rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * std::numbers::pi)); + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * std::numbers::pi)); return success(); }) // --- Phase/diagonal gates: named gate if angle matches, else P @@ -528,17 +532,17 @@ struct FoldPowIntoGate final : OpRewritePattern { const double angle = r * std::numbers::pi / 2.0; if (succeeded(tryReplacePOpWithNamedGate( angle, op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), rewriter))) { return success(); } rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (std::numbers::pi / 2.0))); + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (std::numbers::pi / 2.0))); return success(); }) // pow(r) { sdg } => named gate if angle matches, else p(-r*π/2) @@ -546,17 +550,17 @@ struct FoldPowIntoGate final : OpRewritePattern { const double angle = r * -std::numbers::pi / 2.0; if (succeeded(tryReplacePOpWithNamedGate( angle, op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), rewriter))) { return success(); } rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (-std::numbers::pi / 2.0))); + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (-std::numbers::pi / 2.0))); return success(); }) // pow(r) { t } => named gate if angle matches, else p(r*π/4) @@ -564,17 +568,17 @@ struct FoldPowIntoGate final : OpRewritePattern { const double angle = r * std::numbers::pi / 4.0; if (succeeded(tryReplacePOpWithNamedGate( angle, op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), rewriter))) { return success(); } rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (std::numbers::pi / 4.0))); + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (std::numbers::pi / 4.0))); return success(); }) // pow(r) { tdg } => named gate if angle matches, else p(-r*π/4) @@ -582,75 +586,77 @@ struct FoldPowIntoGate final : OpRewritePattern { const double angle = r * -std::numbers::pi / 4.0; if (succeeded(tryReplacePOpWithNamedGate( angle, op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), rewriter))) { return success(); } rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (-std::numbers::pi / 4.0))); + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (-std::numbers::pi / 4.0))); return success(); }) // --- SX/SXdg gates: decompose to rotation + global phase --- // pow(r) { sx } => gphase(r*π/4); rx(r*π/2) // pow(±2) sx => x .Case([&](auto gate) { - if (std::abs(std::abs(r) - 2.0) < TOLERANCE) { + if (std::abs(std::abs(r) - 2.0) < + PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp( - op, utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn())); + op, mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn())); return success(); } GPhaseOp::create( rewriter, loc, - utils::constantFromScalar(rewriter, op.getLoc(), - r * (std::numbers::pi / 4.0))); + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (std::numbers::pi / 4.0))); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (std::numbers::pi / 2.0))); + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (std::numbers::pi / 2.0))); return success(); }) // pow(r) { sxdg } => gphase(-r*π/4); rx(-r*π/2) // pow(±2) sxdg => x .Case([&](auto gate) { - if (std::abs(std::abs(r) - 2.0) < TOLERANCE) { + if (std::abs(std::abs(r) - 2.0) < + PARAMETER_COMPARISON_TOLERANCE) { rewriter.replaceOpWithNewOp( - op, utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn())); + op, mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn())); return success(); } GPhaseOp::create( rewriter, loc, - utils::constantFromScalar(rewriter, op.getLoc(), - r * (-std::numbers::pi / 4.0))); + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (-std::numbers::pi / 4.0))); rewriter.replaceOpWithNewOp( op, - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (-std::numbers::pi / 2.0))); + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (-std::numbers::pi / 2.0))); return success(); }) // --- Hermitian gates (integer exponent): even => id, odd => gate // --- pow(n) { h/ecr/rccx/swap } => id (n even) | gate (n odd) .Case([&](auto gate) { - if (utils::isEvenExponent(r)) { + if (mqt::isEvenExponent(r)) { const auto identityOutputs = llvm::map_to_vector( gate.getInputQubits(), [&](Value input) { - return utils::getValueFromBlockArgument(input, - op.getQubitsIn()); + return mqt::getValueFromBlockArgument(input, + op.getQubitsIn()); }); replacePowResults(op, gate, identityOutputs, rewriter); } else { - utils::inlineModifierBody(op, *op.getBody(), - op.getInputQubits(), rewriter); + mqt::inlineModifierBody(op, *op.getBody(), op.getInputQubits(), + rewriter); } return success(); }) @@ -661,13 +667,13 @@ struct FoldPowIntoGate final : OpRewritePattern { .Case([&](auto gate) { auto replacement = XXPlusYYOp::create( rewriter, op.getLoc(), - utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn()), - utils::getValueFromBlockArgument(gate.getInputTarget(1), - op.getQubitsIn()), - utils::constantFromScalar(rewriter, op.getLoc(), - r * (-std::numbers::pi)), - utils::constantFromScalar(rewriter, op.getLoc(), 0.0)); + mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn()), + mqt::getValueFromBlockArgument(gate.getInputTarget(1), + op.getQubitsIn()), + mqt::constantFromScalar(rewriter, op.getLoc(), + r * (-std::numbers::pi)), + mqt::constantFromScalar(rewriter, op.getLoc(), 0.0)); replacePowResults(op, gate, replacement.getOutputQubits(), rewriter); return success(); @@ -676,16 +682,16 @@ struct FoldPowIntoGate final : OpRewritePattern { // pow(r) { id } => id .Case([&](auto gate) { rewriter.replaceOpWithNewOp( - op, utils::getValueFromBlockArgument(gate.getInputTarget(0), - op.getQubitsIn())); + op, mqt::getValueFromBlockArgument(gate.getInputTarget(0), + op.getQubitsIn())); return success(); }) // pow(r) { barrier } => barrier .Case([&](auto gate) { const auto inputs = llvm::map_to_vector(gate.getInputQubits(), [&](Value input) { - return utils::getValueFromBlockArgument(input, - op.getQubitsIn()); + return mqt::getValueFromBlockArgument(input, + op.getQubitsIn()); }); auto replacement = BarrierOp::create(rewriter, op.getLoc(), inputs); @@ -745,15 +751,15 @@ struct DropUnusedPowQubits final : OpRewritePattern { } // namespace std::optional PowOp::getExponentValue() { - return utils::valueToDouble(getExponent()); + return mlir::mqt::valueToDouble(getExponent()); } size_t PowOp::getNumBodyUnitaries() { - return utils::getNumBodyUnitaries(*getBody()); + return mqt::getNumBodyUnitaries(*getBody()); } UnitaryOpInterface PowOp::getBodyUnitary(const size_t i) { - return utils::getBodyUnitary(*getBody(), i); + return mqt::getBodyUnitary(*getBody(), i); } Value PowOp::getInputQubit(const size_t i) { @@ -909,12 +915,12 @@ std::optional PowOp::getUnitaryMatrix() { const auto raiseToPow = [p](const DynamicMatrix& u) -> std::optional { // U^1 = U (no computation needed) - if (std::abs(p - 1.0) < TOLERANCE) { + if (std::abs(p - 1.0) < PARAMETER_COMPARISON_TOLERANCE) { return u; } // U^0 = I - if (std::abs(p) < TOLERANCE) { + if (std::abs(p) < PARAMETER_COMPARISON_TOLERANCE) { return DynamicMatrix::identity(u.cols()); } @@ -945,7 +951,7 @@ std::optional PowOp::getUnitaryMatrix() { // Single inner unitary (e.g. `pow(p) { h }`, `pow(p) { rz(theta) }`). if (auto bodyUnitary = - utils::getSoleBodyUnitary(*getBody())) { + mqt::getSoleBodyUnitary(*getBody())) { if (const auto targetMatrix = bodyUnitary.getUnitaryMatrix()) { return raiseToPow(*targetMatrix); diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/GPhaseOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/GPhaseOp.cpp index b12a6dc03e..e3930c2ccb 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/GPhaseOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/GPhaseOp.cpp @@ -8,9 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Angles.h" +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -25,7 +27,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -38,7 +40,7 @@ struct RemoveTrivialGPhase final : OpRewritePattern { LogicalResult matchAndRewrite(GPhaseOp op, PatternRewriter& rewriter) const override { if (const auto theta = valueToDouble(op.getTheta()); - !theta || std::abs(*theta) > TOLERANCE) { + !theta || std::abs(*theta) > PARAMETER_COMPARISON_TOLERANCE) { return failure(); } @@ -57,12 +59,7 @@ void GPhaseOp::build(OpBuilder& odsBuilder, OperationState& odsState, } LogicalResult GPhaseOp::verify() { - const auto theta = valueToConstantDouble(getTheta()); - if (theta && std::abs(*theta) > MAX_GLOBAL_PHASE_ANGLE) { - return emitOpError() << "constant angle must have magnitude at most " - << MAX_GLOBAL_PHASE_ANGLE << " radians"; - } - return success(); + return verifyGlobalPhaseAngle(getOperation(), getTheta()); } void GPhaseOp::getCanonicalizationPatterns(RewritePatternSet& results, diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp index 7958e9ff93..90dd0f2723 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/POp.cpp @@ -8,10 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -26,7 +27,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -54,7 +55,7 @@ void POp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, OpFoldResult POp::fold(FoldAdaptor /*adaptor*/) { if (const auto theta = valueToDouble(getTheta()); - theta && std::abs(*theta) <= TOLERANCE) { + theta && std::abs(*theta) <= PARAMETER_COMPARISON_TOLERANCE) { return getInputQubit(0); } return {}; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp index 74302de221..47a719abf7 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/ROp.cpp @@ -8,10 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -29,7 +30,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -42,7 +43,7 @@ struct ReplaceRWithRX final : OpRewritePattern { LogicalResult matchAndRewrite(ROp op, PatternRewriter& rewriter) const override { if (const auto phi = valueToDouble(op.getPhi()); - !phi || std::abs(*phi) > TOLERANCE) { + !phi || std::abs(*phi) > PARAMETER_COMPARISON_TOLERANCE) { return failure(); } rewriter.replaceOpWithNewOp(op, op.getInputQubit(0), op.getTheta()); @@ -59,7 +60,8 @@ struct ReplaceRWithRY final : OpRewritePattern { LogicalResult matchAndRewrite(ROp op, PatternRewriter& rewriter) const override { if (const auto phi = valueToDouble(op.getPhi()); - !phi || std::abs(*phi - (std::numbers::pi / 2.0)) > TOLERANCE) { + !phi || std::abs(*phi - (std::numbers::pi / 2.0)) > + PARAMETER_COMPARISON_TOLERANCE) { return failure(); } rewriter.replaceOpWithNewOp(op, op.getInputQubit(0), op.getTheta()); diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXOp.cpp index b1e8c25784..27a1a90137 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXOp.cpp @@ -8,10 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -26,7 +27,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -54,7 +55,7 @@ void RXOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, OpFoldResult RXOp::fold(FoldAdaptor /*adaptor*/) { if (const auto theta = valueToDouble(getTheta()); - theta && std::abs(*theta) <= TOLERANCE) { + theta && std::abs(*theta) <= PARAMETER_COMPARISON_TOLERANCE) { return getInputQubit(0); } return {}; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp index 26e60fe0c0..b0f631487e 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp @@ -8,10 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -26,7 +27,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -56,7 +57,7 @@ void RXXOp::build(OpBuilder& odsBuilder, OperationState& odsState, LogicalResult RXXOp::fold(FoldAdaptor /*adaptor*/, SmallVectorImpl& results) { if (const auto theta = valueToDouble(getTheta()); - theta && std::abs(*theta) <= TOLERANCE) { + theta && std::abs(*theta) <= PARAMETER_COMPARISON_TOLERANCE) { results.emplace_back(getInputQubit(0)); results.emplace_back(getInputQubit(1)); return success(); diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYOp.cpp index da47368b28..3ae4aab85a 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYOp.cpp @@ -8,10 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -25,7 +26,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -53,7 +54,7 @@ void RYOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, OpFoldResult RYOp::fold(FoldAdaptor /*adaptor*/) { if (const auto theta = valueToDouble(getTheta()); - theta && std::abs(*theta) <= TOLERANCE) { + theta && std::abs(*theta) <= PARAMETER_COMPARISON_TOLERANCE) { return getInputQubit(0); } return {}; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp index e9cb3f505d..bc5373e5b7 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp @@ -8,10 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -26,7 +27,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -56,7 +57,7 @@ void RYYOp::build(OpBuilder& odsBuilder, OperationState& odsState, LogicalResult RYYOp::fold(FoldAdaptor /*adaptor*/, SmallVectorImpl& results) { if (const auto theta = valueToDouble(getTheta()); - theta && std::abs(*theta) <= TOLERANCE) { + theta && std::abs(*theta) <= PARAMETER_COMPARISON_TOLERANCE) { results.emplace_back(getInputQubit(0)); results.emplace_back(getInputQubit(1)); return success(); diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp index 9cc8c65f7f..a0e39f5f59 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZOp.cpp @@ -8,10 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -26,7 +27,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -54,7 +55,7 @@ void RZOp::build(OpBuilder& odsBuilder, OperationState& odsState, Value qubitIn, OpFoldResult RZOp::fold(FoldAdaptor /*adaptor*/) { if (const auto theta = valueToDouble(getTheta()); - theta && std::abs(*theta) <= TOLERANCE) { + theta && std::abs(*theta) <= PARAMETER_COMPARISON_TOLERANCE) { return getInputQubit(0); } return {}; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp index 0f60fb0d7c..5b112b1df9 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp @@ -8,10 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -26,7 +27,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -56,7 +57,7 @@ void RZXOp::build(OpBuilder& odsBuilder, OperationState& odsState, LogicalResult RZXOp::fold(FoldAdaptor /*adaptor*/, SmallVectorImpl& results) { if (const auto theta = valueToDouble(getTheta()); - theta && std::abs(*theta) <= TOLERANCE) { + theta && std::abs(*theta) <= PARAMETER_COMPARISON_TOLERANCE) { results.emplace_back(getInputQubit(0)); results.emplace_back(getInputQubit(1)); return success(); diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp index 0892478b57..e2d65f472b 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp @@ -8,10 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -26,7 +27,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -56,7 +57,7 @@ void RZZOp::build(OpBuilder& odsBuilder, OperationState& odsState, LogicalResult RZZOp::fold(FoldAdaptor /*adaptor*/, SmallVectorImpl& results) { if (const auto theta = valueToDouble(getTheta()); - theta && std::abs(*theta) <= TOLERANCE) { + theta && std::abs(*theta) <= PARAMETER_COMPARISON_TOLERANCE) { results.emplace_back(getInputQubit(0)); results.emplace_back(getInputQubit(1)); return success(); diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/U2Op.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/U2Op.cpp index ed94510008..36f58c57d3 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/U2Op.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/U2Op.cpp @@ -8,9 +8,10 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -26,7 +27,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -40,8 +41,8 @@ struct ReplaceU2WithH final : OpRewritePattern { PatternRewriter& rewriter) const override { const auto phi = valueToDouble(op.getPhi()); const auto lambda = valueToDouble(op.getLambda()); - if (!phi || std::abs(*phi) > TOLERANCE || !lambda || - std::abs(*lambda - std::numbers::pi) > TOLERANCE) { + if (!phi || std::abs(*phi) > PARAMETER_COMPARISON_TOLERANCE || !lambda || + std::abs(*lambda - std::numbers::pi) > PARAMETER_COMPARISON_TOLERANCE) { return failure(); } rewriter.replaceOpWithNewOp(op, op.getInputQubit(0)); @@ -59,8 +60,12 @@ struct ReplaceU2WithRX final : OpRewritePattern { PatternRewriter& rewriter) const override { const auto phi = valueToDouble(op.getPhi()); const auto lambda = valueToDouble(op.getLambda()); - if (!phi || std::abs(*phi + (std::numbers::pi / 2.0)) > TOLERANCE || - !lambda || std::abs(*lambda - (std::numbers::pi / 2.0)) > TOLERANCE) { + if (!phi || + std::abs(*phi + (std::numbers::pi / 2.0)) > + PARAMETER_COMPARISON_TOLERANCE || + !lambda || + std::abs(*lambda - (std::numbers::pi / 2.0)) > + PARAMETER_COMPARISON_TOLERANCE) { return failure(); } rewriter.replaceOpWithNewOp(op, op.getInputQubit(0), @@ -79,8 +84,8 @@ struct ReplaceU2WithRY final : OpRewritePattern { PatternRewriter& rewriter) const override { const auto phi = valueToDouble(op.getPhi()); const auto lambda = valueToDouble(op.getLambda()); - if (!phi || std::abs(*phi) > TOLERANCE || !lambda || - std::abs(*lambda) > TOLERANCE) { + if (!phi || std::abs(*phi) > PARAMETER_COMPARISON_TOLERANCE || !lambda || + std::abs(*lambda) > PARAMETER_COMPARISON_TOLERANCE) { return failure(); } rewriter.replaceOpWithNewOp(op, op.getInputQubit(0), diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/UOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/UOp.cpp index 7d7c362755..d4b43a59bd 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/UOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/UOp.cpp @@ -8,9 +8,10 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -26,7 +27,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -40,8 +41,8 @@ struct ReplaceUWithP final : OpRewritePattern { PatternRewriter& rewriter) const override { const auto theta = valueToDouble(op.getTheta()); const auto phi = valueToDouble(op.getPhi()); - if (!theta || std::abs(*theta) > TOLERANCE || !phi || - std::abs(*phi) > TOLERANCE) { + if (!theta || std::abs(*theta) > PARAMETER_COMPARISON_TOLERANCE || !phi || + std::abs(*phi) > PARAMETER_COMPARISON_TOLERANCE) { return failure(); } rewriter.replaceOpWithNewOp(op, op.getInputQubit(0), op.getLambda()); @@ -59,8 +60,12 @@ struct ReplaceUWithRX final : OpRewritePattern { PatternRewriter& rewriter) const override { const auto phi = valueToDouble(op.getPhi()); const auto lambda = valueToDouble(op.getLambda()); - if (!phi || std::abs(*phi + (std::numbers::pi / 2.0)) > TOLERANCE || - !lambda || std::abs(*lambda - (std::numbers::pi / 2.0)) > TOLERANCE) { + if (!phi || + std::abs(*phi + (std::numbers::pi / 2.0)) > + PARAMETER_COMPARISON_TOLERANCE || + !lambda || + std::abs(*lambda - (std::numbers::pi / 2.0)) > + PARAMETER_COMPARISON_TOLERANCE) { return failure(); } rewriter.replaceOpWithNewOp(op, op.getInputQubit(0), op.getTheta()); @@ -78,8 +83,8 @@ struct ReplaceUWithRY final : OpRewritePattern { PatternRewriter& rewriter) const override { const auto phi = valueToDouble(op.getPhi()); const auto lambda = valueToDouble(op.getLambda()); - if (!phi || std::abs(*phi) > TOLERANCE || !lambda || - std::abs(*lambda) > TOLERANCE) { + if (!phi || std::abs(*phi) > PARAMETER_COMPARISON_TOLERANCE || !lambda || + std::abs(*lambda) > PARAMETER_COMPARISON_TOLERANCE) { return failure(); } rewriter.replaceOpWithNewOp(op, op.getInputQubit(0), op.getTheta()); @@ -96,7 +101,8 @@ struct ReplaceUWithU2 final : OpRewritePattern { LogicalResult matchAndRewrite(UOp op, PatternRewriter& rewriter) const override { const auto theta = valueToDouble(op.getTheta()); - if (!theta || std::abs(*theta - (std::numbers::pi / 2.0)) > TOLERANCE) { + if (!theta || std::abs(*theta - (std::numbers::pi / 2.0)) > + PARAMETER_COMPARISON_TOLERANCE) { return failure(); } rewriter.replaceOpWithNewOp(op, op.getInputQubit(0), op.getPhi(), diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp index f9b010b8dd..6f7c656126 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp @@ -8,10 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -27,7 +28,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -59,7 +60,7 @@ void XXMinusYYOp::build(OpBuilder& odsBuilder, OperationState& odsState, LogicalResult XXMinusYYOp::fold(FoldAdaptor /*adaptor*/, SmallVectorImpl& results) { if (const auto theta = valueToDouble(getTheta()); - theta && std::abs(*theta) <= TOLERANCE) { + theta && std::abs(*theta) <= PARAMETER_COMPARISON_TOLERANCE) { results.emplace_back(getInputQubit(0)); results.emplace_back(getInputQubit(1)); return success(); diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp index b4931b839e..0ad8db3525 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp @@ -8,10 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -27,7 +28,7 @@ using namespace mlir; using namespace mlir::qco; -using namespace mlir::utils; +using namespace mlir::mqt; namespace { @@ -59,7 +60,7 @@ void XXPlusYYOp::build(OpBuilder& odsBuilder, OperationState& odsState, LogicalResult XXPlusYYOp::fold(FoldAdaptor /*adaptor*/, SmallVectorImpl& results) { if (const auto theta = valueToDouble(getTheta()); - theta && std::abs(*theta) <= TOLERANCE) { + theta && std::abs(*theta) <= PARAMETER_COMPARISON_TOLERANCE) { results.emplace_back(getInputQubit(0)); results.emplace_back(getInputQubit(1)); return success(); diff --git a/mlir/lib/Dialect/QCO/IR/Operations/UnitaryOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/UnitaryOp.cpp index 3bc92be201..65ebb08df0 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/UnitaryOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/UnitaryOp.cpp @@ -8,9 +8,9 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/DenseUnitary.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/DenseUnitary.h" #include #include @@ -36,7 +36,7 @@ struct FoldIdentityUnitary final : OpRewritePattern { LogicalResult matchAndRewrite(UnitaryOp op, PatternRewriter& rewriter) const override { - if (!utils::isExactIdentityMatrix(op.getMatrix())) { + if (!mqt::isExactIdentityMatrix(op.getMatrix())) { return failure(); } rewriter.replaceOp(op, op.getQubitsIn()); @@ -57,8 +57,8 @@ LogicalResult UnitaryOp::verify() { if (getQubitsOut().size() != getQubitsIn().size()) { return emitOpError("must return one qubit for every input qubit"); } - return utils::verifyDenseUnitaryMatrix(getOperation(), getMatrix(), - getQubitsIn()); + return mqt::verifyDenseUnitaryMatrix(getOperation(), getMatrix(), + getQubitsIn()); } Value UnitaryOp::getInputForOutput(const Value output) { diff --git a/mlir/lib/Dialect/QCO/IR/QCOOps.cpp b/mlir/lib/Dialect/QCO/IR/QCOOps.cpp index d6e30eda5b..9c1523a470 100644 --- a/mlir/lib/Dialect/QCO/IR/QCOOps.cpp +++ b/mlir/lib/Dialect/QCO/IR/QCOOps.cpp @@ -10,8 +10,9 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" // IWYU pragma: associated -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -51,12 +52,12 @@ static bool isQCOLinearType(Type type) { static ParseResult parseTargetAliasing(OpAsmParser& parser, Region& region, SmallVectorImpl& operands) { - return utils::parseTargetAliasing(parser, region, operands); + return mqt::parseTargetAliasing(parser, region, operands); } static void printTargetAliasing(OpAsmPrinter& printer, Operation* /*op*/, Region& region, OperandRange targetsIn) { - utils::printTargetAliasing(printer, region, targetsIn); + mqt::printTargetAliasing(printer, region, targetsIn); } ParseResult IfOp::parse(::mlir::OpAsmParser& parser, @@ -507,7 +508,7 @@ void QCODialect::initialize() { //===----------------------------------------------------------------------===// LogicalResult mlir::qco::verifyUnitaryOpInterface(Operation* op) { - return utils::verifyFiniteConstantParameters( + return mqt::verifyFiniteConstantParameters( op, cast(op).getParameters()); } diff --git a/mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt b/mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt index 84d620e383..a34feeca0f 100644 --- a/mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt +++ b/mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt @@ -22,6 +22,7 @@ add_mlir_library( MLIRMathDialect MLIRMQTDialect MLIRMQTTransforms + MLIRMQTUtils MLIRSCFUtils DEPENDS MLIRQCOTransformsIncGen) diff --git a/mlir/lib/Dialect/QCO/Transforms/Decomposition/DecomposeMultiControlled.cpp b/mlir/lib/Dialect/QCO/Transforms/Decomposition/DecomposeMultiControlled.cpp index 50875742dc..a8e26f1e34 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Decomposition/DecomposeMultiControlled.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Decomposition/DecomposeMultiControlled.cpp @@ -8,10 +8,12 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" +#include "mlir/Dialect/MQT/Utils/Parameters.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/Utils/Utils.h" #include #include // IWYU pragma: keep (Passes.h.inc) @@ -1273,7 +1275,7 @@ matchControlledTarget(UnitaryOpInterface inner) { .theta = std::nullopt}; } if (auto pOp = dyn_cast(inner.getOperation())) { - if (const auto theta = utils::valueToDouble(pOp.getTheta())) { + if (const auto theta = mlir::mqt::valueToDouble(pOp.getTheta())) { return ControlledGateSpec{.gate = ControlledTarget::Phase, .theta = theta}; } @@ -1326,7 +1328,7 @@ struct DecomposeControlledGatePattern final : OpRewritePattern { } const auto numControls = op.getNumControls(); - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = mqt::getSoleBodyUnitary(*op.getBody()); if (!inner) { return failure(); } @@ -1353,7 +1355,8 @@ struct DecomposeControlledGatePattern final : OpRewritePattern { // multi-controlled-Z path (elementary at 3–4 qubits, relative-phase / Vale // at 5–6 qubits, else HP24). if (gate == ControlledTarget::Phase && spec->theta && - std::abs(std::abs(*spec->theta) - K_PI) <= utils::TOLERANCE) { + std::abs(std::abs(*spec->theta) - K_PI) <= + mqt::PARAMETER_COMPARISON_TOLERANCE) { gate = ControlledTarget::Z; } diff --git a/mlir/lib/Dialect/QCO/Transforms/Decomposition/Euler.cpp b/mlir/lib/Dialect/QCO/Transforms/Decomposition/Euler.cpp index 857c63354f..fb490051a2 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Decomposition/Euler.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Decomposition/Euler.cpp @@ -10,9 +10,9 @@ #include "mlir/Dialect/QCO/Transforms/Decomposition/Euler.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -50,7 +50,7 @@ namespace mlir::qco::decomposition { } double wrapped = r - pi; - if (wrapped >= pi - utils::TOLERANCE) { + if (wrapped >= pi - mqt::PARAMETER_COMPARISON_TOLERANCE) { wrapped = -pi; } @@ -81,7 +81,7 @@ namespace mlir::qco::decomposition { * @return `true` when no rotation gate should be emitted. */ [[nodiscard]] static bool isNearZeroRotationAngle(const double angle) { - return std::abs(angle) <= utils::TOLERANCE; + return std::abs(angle) <= mqt::PARAMETER_COMPARISON_TOLERANCE; } void emitGPhaseIfNeeded(OpBuilder& builder, Location loc, const double phase) { @@ -110,9 +110,9 @@ void emitGPhaseIfNeeded(OpBuilder& builder, Location loc, const double phase) { 2. * std::atan2(std::abs(matrix(1, 0)), std::abs(matrix(0, 0))); const auto ang1 = std::arg(matrix(1, 1)); double ang2 = 0.0; - if (std::abs(matrix(1, 0)) > utils::TOLERANCE) { + if (std::abs(matrix(1, 0)) > mqt::PARAMETER_COMPARISON_TOLERANCE) { ang2 = std::arg(matrix(1, 0)); - } else if (std::abs(matrix(0, 1)) > utils::TOLERANCE) { + } else if (std::abs(matrix(0, 1)) > mqt::PARAMETER_COMPARISON_TOLERANCE) { ang2 = std::arg(matrix(0, 1)); } const auto phi = ang1 + ang2 - detArg; diff --git a/mlir/lib/Dialect/QCO/Transforms/NativeSynthesis/FuseSingleQubitUnitaryRuns.cpp b/mlir/lib/Dialect/QCO/Transforms/NativeSynthesis/FuseSingleQubitUnitaryRuns.cpp index 4e69f2572b..05dca7acdb 100644 --- a/mlir/lib/Dialect/QCO/Transforms/NativeSynthesis/FuseSingleQubitUnitaryRuns.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/NativeSynthesis/FuseSingleQubitUnitaryRuns.cpp @@ -8,13 +8,13 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Transforms/GlobalPhaseNormalization.h" #include "mlir/Dialect/QCO/IR/QCOInterfaces.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Transforms/Decomposition/Euler.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/QCO/Utils/WireIterator.h" -#include "mlir/Dialect/Utils/Transforms/GlobalPhaseNormalization.h" #include #include // IWYU pragma: keep (Passes.h.inc) diff --git a/mlir/lib/Dialect/QCO/Transforms/NativeSynthesis/TargetSynthesis.cpp b/mlir/lib/Dialect/QCO/Transforms/NativeSynthesis/TargetSynthesis.cpp index c153a9cdb6..1a83e1e22e 100644 --- a/mlir/lib/Dialect/QCO/Transforms/NativeSynthesis/TargetSynthesis.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/NativeSynthesis/TargetSynthesis.cpp @@ -9,6 +9,7 @@ */ #include "mlir/Compiler/Target.h" +#include "mlir/Dialect/MQT/Transforms/GlobalPhaseNormalization.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOInterfaces.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" @@ -17,7 +18,6 @@ #include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" #include "mlir/Dialect/QTensor/IR/QTensorOps.h" -#include "mlir/Dialect/Utils/Transforms/GlobalPhaseNormalization.h" #include #include diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/HadamardLifting.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/HadamardLifting.cpp index 69c11af0af..4cb2359c99 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/HadamardLifting.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/HadamardLifting.cpp @@ -8,11 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Transforms/GlobalPhaseNormalization.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.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/Utils/Transforms/GlobalPhaseNormalization.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -165,7 +165,7 @@ struct LiftHadamardAboveCNOTPattern final : OpRewritePattern { return failure(); } if (auto innerUnitary = - utils::getSoleBodyUnitary(*cnotGate.getBody()); + mqt::getSoleBodyUnitary(*cnotGate.getBody()); !innerUnitary || !isa(innerUnitary.getOperation()) || cnotGate.getOutputTarget(0) != inQubitHadamard) { return failure(); diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp index 935d969554..1462bd6594 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MeasurementLifting.cpp @@ -8,10 +8,10 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Modifiers.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/Utils/Utils.h" #include #include @@ -45,7 +45,7 @@ static bool isDiagonal(Operation* op) { return false; } if (isa(op)) { - return isDiagonal(utils::getSoleBodyUnitary( + return isDiagonal(mqt::getSoleBodyUnitary( *op->getRegion(0).getBlocks().begin())); } return isa(op); diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp index 32a1050887..6fd0cd88b6 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp @@ -8,12 +8,13 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Transforms/GlobalPhaseNormalization.h" +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.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/WireIterator.h" -#include "mlir/Dialect/Utils/Transforms/GlobalPhaseNormalization.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -65,7 +66,7 @@ template struct Val { if constexpr (std::is_same_v) { return {x, &rewriter, loc}; } else { - return {utils::constantFromScalar(rewriter, loc, x), &rewriter, loc}; + return {mqt::constantFromScalar(rewriter, loc, x), &rewriter, loc}; } } @@ -370,7 +371,7 @@ static std::optional> gateParam(UnitaryOpInterface op, unsigned i, Location loc) { Value p = op.getParameter(i); if constexpr (std::is_same_v) { - const auto folded = utils::valueToConstantDouble(p); + const auto folded = mlir::mqt::valueToConstantDouble(p); if (!folded) { return std::nullopt; } @@ -736,15 +737,15 @@ struct MergeSingleQubitRotationGatesPattern final for (auto chainOp : llvm::drop_begin(chain)) { rewriter.replaceOp(chainOp, chainOp.getInputQubit(0)); } - if (std::abs(correction.v) > utils::TOLERANCE) { + if (std::abs(correction.v) > mqt::PARAMETER_COMPARISON_TOLERANCE) { GPhaseOp::create(rewriter, loc, - utils::constantFromScalar(rewriter, loc, correction.v)); + mqt::constantFromScalar(rewriter, loc, correction.v)); } rewriter.replaceOpWithNewOp( chain.front(), chain.front().getInputQubit(0), - utils::constantFromScalar(rewriter, loc, theta.v), - utils::constantFromScalar(rewriter, loc, phi.v), - utils::constantFromScalar(rewriter, loc, lambda.v)); + mqt::constantFromScalar(rewriter, loc, theta.v), + mqt::constantFromScalar(rewriter, loc, phi.v), + mqt::constantFromScalar(rewriter, loc, lambda.v)); return success(); } diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/ReplaceClassicalControls.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/ReplaceClassicalControls.cpp index 85c014616b..469ab7bafb 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/ReplaceClassicalControls.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/ReplaceClassicalControls.cpp @@ -8,10 +8,11 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Modifiers.h" +#include "mlir/Dialect/MQT/Utils/Parameters.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/Utils/Utils.h" #include #include @@ -66,8 +67,8 @@ static Value selectScaledAngle(PatternRewriter& rewriter, Location loc, Value theta, Value condition, const double trueScale, const double falseScale) { - const Value trueValue = utils::constantFromScalar(rewriter, loc, trueScale); - const Value falseValue = utils::constantFromScalar(rewriter, loc, falseScale); + const Value trueValue = mqt::constantFromScalar(rewriter, loc, trueScale); + const Value falseValue = mqt::constantFromScalar(rewriter, loc, falseScale); const Value scale = arith::SelectOp::create(rewriter, loc, condition, trueValue, falseValue); return arith::MulFOp::create(rewriter, loc, theta, scale); @@ -198,7 +199,7 @@ static LogicalResult tryReplaceMeasuredRZTarget(CtrlOp op, RZOp rzOp, return success(); } - utils::hoistSupportingOpsBefore(*op.getBody(), rzOp, op, rewriter); + mqt::hoistSupportingOpsBefore(*op.getBody(), rzOp, op, rewriter); rewriter.setInsertionPoint(op); const Value phase = selectScaledAngle(rewriter, op.getLoc(), rzOp.getTheta(), outcome, 0.5, -0.5); @@ -242,7 +243,7 @@ static LogicalResult tryReplaceMeasuredRZZTarget(CtrlOp op, RZZOp rzzOp, return success(); } - utils::hoistSupportingOpsBefore(*op.getBody(), rzzOp, op, rewriter); + mqt::hoistSupportingOpsBefore(*op.getBody(), rzzOp, op, rewriter); rewriter.setInsertionPoint(op); SmallVector controls(op.getControlsIn()); SmallVector targets(op.getTargetsIn()); @@ -330,7 +331,7 @@ struct ReplaceBasisStateControlsWithIfPattern final rewriter.setInsertionPointAfter(ctrlOp); if (auto unitary = - utils::getSoleBodyUnitary(*ctrlOp.getBody()); + mqt::getSoleBodyUnitary(*ctrlOp.getBody()); unitary) { if (auto rzOp = dyn_cast(unitary.getOperation()); rzOp && diff --git a/mlir/lib/Dialect/QCO/Utils/CMakeLists.txt b/mlir/lib/Dialect/QCO/Utils/CMakeLists.txt index ea41ef4820..2c6271e423 100644 --- a/mlir/lib/Dialect/QCO/Utils/CMakeLists.txt +++ b/mlir/lib/Dialect/QCO/Utils/CMakeLists.txt @@ -28,6 +28,7 @@ add_mlir_dialect_library( LINK_LIBS PUBLIC MLIRCBitDialect + MLIRMQTUtils MLIRQCODialect MLIRSCFDialect) @@ -68,7 +69,9 @@ add_mlir_library( MLIRQCODialect MLIRQCOMatrix MLIRFuncDialect - MQT::CoreDD) + MQT::CoreDD + PRIVATE + MLIRMQTUtils) mqt_mlir_target_use_project_options(MLIRQCODDFunctionality) unset(LLVM_REQUIRES_EH) diff --git a/mlir/lib/Dialect/QCO/Utils/DDFunctionality.cpp b/mlir/lib/Dialect/QCO/Utils/DDFunctionality.cpp index 3b3de62a6b..31d443a236 100644 --- a/mlir/lib/Dialect/QCO/Utils/DDFunctionality.cpp +++ b/mlir/lib/Dialect/QCO/Utils/DDFunctionality.cpp @@ -23,11 +23,12 @@ #include "mlir/Dialect/CBit/IR/CBitAttributes.h" #include "mlir/Dialect/CBit/IR/CBitDialect.h" #include "mlir/Dialect/CBit/IR/CBitOps.h" +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" #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/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -199,7 +200,8 @@ decodeStandardGate(UnitaryOpInterface unitary) { DecodedGate decoded{.type = type, .params = {}}; for (Value param : unitary.getParameters()) { - decoded.params.push_back(static_cast(*utils::valueToDouble(param))); + decoded.params.push_back( + static_cast(*mlir::mqt::valueToDouble(param))); } return std::optional{std::move(decoded)}; } @@ -261,7 +263,7 @@ static LogicalResult applyUnitaryMatrix(UnitaryOpInterface unitary, << "unitary must have a compile-time constant matrix"; } if (auto gphase = dyn_cast(op)) { - const auto theta = *utils::valueToDouble(gphase.getTheta()); + const auto theta = *mlir::mqt::valueToDouble(gphase.getTheta()); auto id = dd::Package::makeIdent(); id.w = walk.dd->cn.lookup(std::cos(theta), std::sin(theta)); state = walk.dd->applyOperation(id, state); @@ -757,7 +759,7 @@ static LogicalResult applyOp(Operation& op, WalkState& walk, StateDD& state) { } }) .template Case([&](CtrlOp ctrlOp) -> LogicalResult { - if (auto inner = utils::getSoleBodyUnitary( + if (auto inner = mqt::getSoleBodyUnitary( *ctrlOp.getBody())) { auto decoded = decodeStandardGate(inner); if (failed(decoded)) { diff --git a/mlir/lib/Dialect/Utils/CMakeLists.txt b/mlir/lib/Dialect/Utils/CMakeLists.txt deleted file mode 100644 index 3c339729a9..0000000000 --- a/mlir/lib/Dialect/Utils/CMakeLists.txt +++ /dev/null @@ -1,9 +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 - -add_subdirectory(Transforms) diff --git a/mlir/lib/Support/CMakeLists.txt b/mlir/lib/Support/CMakeLists.txt index 5939383e6b..340f4dfe0f 100644 --- a/mlir/lib/Support/CMakeLists.txt +++ b/mlir/lib/Support/CMakeLists.txt @@ -8,7 +8,6 @@ add_mlir_library( MLIRSupportMQT - IRVerification.cpp Passes.cpp PrettyPrinting.cpp ADDITIONAL_HEADER_DIRS @@ -22,15 +21,11 @@ add_mlir_library( MLIRControlFlowInterfaces MLIRTransformUtils MLIRCBitToMemRef - MLIRLLVMDialect - MLIRFuncDialect - MLIRArithDialect MLIRMQTTransforms MLIRQCTransforms MLIRQCOTransforms MLIRQIRTransforms - MLIRQTensorTransforms - MLIRQTensorDialect) + MLIRQTensorTransforms) mqt_mlir_target_use_project_options(MLIRSupportMQT) diff --git a/mlir/lib/Support/Passes.cpp b/mlir/lib/Support/Passes.cpp index 21fb8506a3..b4262a8c77 100644 --- a/mlir/lib/Support/Passes.cpp +++ b/mlir/lib/Support/Passes.cpp @@ -11,11 +11,11 @@ #include "mlir/Support/Passes.h" #include "mlir/Conversion/CBitToMemRef/CBitToMemRef.h" +#include "mlir/Dialect/MQT/Transforms/Passes.h" #include "mlir/Dialect/QC/Transforms/Passes.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Dialect/QIR/Transforms/Passes.h" #include "mlir/Dialect/QTensor/Transforms/Passes.h" -#include "mlir/Dialect/Utils/Transforms/Passes.h" #include #include diff --git a/mlir/tools/mqt-cc/mqt-cc.cpp b/mlir/tools/mqt-cc/mqt-cc.cpp index 2962b12bce..d3b3053f2e 100644 --- a/mlir/tools/mqt-cc/mqt-cc.cpp +++ b/mlir/tools/mqt-cc/mqt-cc.cpp @@ -18,13 +18,13 @@ #include "mlir/Conversion/QCToQIR/QIRBase/QCToQIRBase.h" #include "mlir/Dialect/CBit/IR/CBitDialect.h" #include "mlir/Dialect/MQT/IR/MQTDialect.h" +#include "mlir/Dialect/MQT/Transforms/Passes.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/Translation/TranslateQASM3ToQC.h" #include "mlir/Dialect/QC/Translation/TranslateQCToOpenQASM3.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QIR/Utils/QIRUtils.h" #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" -#include "mlir/Dialect/Utils/Transforms/Passes.h" #include "mlir/Support/Passes.h" #include diff --git a/mlir/unittests/CMakeLists.txt b/mlir/unittests/CMakeLists.txt index ef4044c644..bf992f35ee 100644 --- a/mlir/unittests/CMakeLists.txt +++ b/mlir/unittests/CMakeLists.txt @@ -8,8 +8,27 @@ set(MQT_MLIR_UNITTESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +add_library(MLIRTestSupport STATIC Support/IRVerification.cpp) +target_include_directories(MLIRTestSupport PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries( + MLIRTestSupport + PUBLIC MLIRAnalysis + MLIRIR + MLIRLLVMDialect + MLIRQCDialect + MLIRQCODialect + MLIRQTensorDialect + MLIRQTensorUtils + MLIRSCFDialect + MLIRSupport + LLVMSupport) +target_sources(MLIRTestSupport PUBLIC FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES + Support/IRVerification.h) +mqt_mlir_target_use_project_options(MLIRTestSupport) + add_library(MLIRTestCaseUtils INTERFACE) -target_link_libraries(MLIRTestCaseUtils INTERFACE GTest::gtest MLIRIR MLIRSupportMQT) +target_link_libraries(MLIRTestCaseUtils INTERFACE GTest::gtest MLIRIR MLIRSupportMQT + MLIRTestSupport) target_sources(MLIRTestCaseUtils INTERFACE FILE_SET HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES TestCaseUtils.h) diff --git a/mlir/unittests/Compiler/test_compiler_pipeline.cpp b/mlir/unittests/Compiler/test_compiler_pipeline.cpp index 3d6aa6cf8f..52b7681467 100644 --- a/mlir/unittests/Compiler/test_compiler_pipeline.cpp +++ b/mlir/unittests/Compiler/test_compiler_pipeline.cpp @@ -8,6 +8,7 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #include "TestCaseUtils.h" #include "mlir/Compiler/Programs.h" #include "mlir/Compiler/Target.h" @@ -22,7 +23,6 @@ #include "mlir/Dialect/QIR/Utils/QIRUtils.h" #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" #include "mlir/Dialect/QTensor/IR/QTensorOps.h" -#include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "qasm_programs.h" #include "qc_programs.h" @@ -129,14 +129,14 @@ class CompilerPipelineTest [[nodiscard]] OwningOpRef buildQCReference(const QCProgramBuilderFn builder) const { - auto module = mqt::test::buildMLIRProgram(context.get(), builder); + auto module = ::mqt::test::buildMLIRProgram(context.get(), builder); EXPECT_TRUE(runQCCleanupPipeline(module.get()).succeeded()); return module; } [[nodiscard]] OwningOpRef buildQIRReference(const QIRProgramBuilderFn builder) const { - auto module = mqt::test::buildMLIRProgram( + auto module = ::mqt::test::buildMLIRProgram( context.get(), builder, QIRProgramBuilder::Profile::Adaptive); EXPECT_TRUE(runQIRCleanupPipeline(module.get(), true).succeeded()); return module; @@ -212,7 +212,7 @@ TEST_P(CompilerPipelineTest, EndToEndPipeline) { ASSERT_TRUE(testCase.qcProgramBuilder); auto module = - mqt::test::buildMLIRProgram(context.get(), testCase.qcProgramBuilder); + ::mqt::test::buildMLIRProgram(context.get(), testCase.qcProgramBuilder); ASSERT_TRUE(module); printer.record(module.get(), "QC Input" + name); EXPECT_TRUE(verify(*module).succeeded()); @@ -1086,7 +1086,7 @@ cx q[0], q[2]; EXPECT_NE(qco.str(), beforeFusion); EXPECT_TRUE(qco.runPassPipeline("mqt-qco-default", true, true)); - auto loopModule = mqt::test::buildMLIRProgram( + auto loopModule = ::mqt::test::buildMLIRProgram( context.get(), MQT_NAMED_BUILDER(qco::simpleForLoop)); ASSERT_TRUE(loopModule); std::string loopIR; @@ -1271,7 +1271,7 @@ TEST_F(CompilerPipelineTest, QCOProgramQubitReuseAPIs) { return StringRef(ir).count("qco.alloc"); }; const auto buildQCO = [this](const QCProgramBuilderFn& builder) { - auto module = mqt::test::buildMLIRProgram(context.get(), builder); + auto module = ::mqt::test::buildMLIRProgram(context.get(), builder); std::string source; llvm::raw_string_ostream stream(source); module->print(stream); diff --git a/mlir/unittests/Conversion/JeffRoundTrip/test_jeff_round_trip.cpp b/mlir/unittests/Conversion/JeffRoundTrip/test_jeff_round_trip.cpp index 268f60c9c6..cabd78977a 100644 --- a/mlir/unittests/Conversion/JeffRoundTrip/test_jeff_round_trip.cpp +++ b/mlir/unittests/Conversion/JeffRoundTrip/test_jeff_round_trip.cpp @@ -8,17 +8,17 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #include "TestCaseUtils.h" #include "mlir/Conversion/JeffToQCO/JeffToQCO.h" #include "mlir/Conversion/QCOToJeff/QCOToJeff.h" #include "mlir/Dialect/CBit/IR/CBitDialect.h" #include "mlir/Dialect/CBit/IR/CBitOps.h" #include "mlir/Dialect/MQT/IR/MQTDialect.h" +#include "mlir/Dialect/MQT/Transforms/Passes.h" #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" -#include "mlir/Dialect/Utils/Transforms/Passes.h" -#include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "qco_programs.h" diff --git a/mlir/unittests/Conversion/QCOToQC/test_qco_to_qc.cpp b/mlir/unittests/Conversion/QCOToQC/test_qco_to_qc.cpp index 9e48ae88aa..83bed48f62 100644 --- a/mlir/unittests/Conversion/QCOToQC/test_qco_to_qc.cpp +++ b/mlir/unittests/Conversion/QCOToQC/test_qco_to_qc.cpp @@ -8,6 +8,7 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #include "TestCaseUtils.h" #include "mlir/Conversion/QCOToQC/QCOToQC.h" #include "mlir/Dialect/MQT/IR/MQTDialect.h" @@ -16,7 +17,6 @@ #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" -#include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "qc_programs.h" #include "qco_programs.h" diff --git a/mlir/unittests/Conversion/QCToQCO/test_qc_to_qco.cpp b/mlir/unittests/Conversion/QCToQCO/test_qc_to_qco.cpp index 8e3b233ef8..72e372d958 100644 --- a/mlir/unittests/Conversion/QCToQCO/test_qc_to_qco.cpp +++ b/mlir/unittests/Conversion/QCToQCO/test_qc_to_qco.cpp @@ -8,6 +8,7 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #include "TestCaseUtils.h" #include "mlir/Conversion/QCToQCO/QCToQCO.h" #include "mlir/Dialect/CBit/IR/CBitAttributes.h" @@ -22,7 +23,6 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" #include "mlir/Dialect/QTensor/IR/QTensorOps.h" -#include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "qc_programs.h" #include "qco_programs.h" diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp index 28623190e8..b57ba1906a 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp @@ -8,14 +8,14 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #include "TestCaseUtils.h" #include "mlir/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.h" +#include "mlir/Dialect/MQT/Transforms/Passes.h" #include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QIR/Builder/QIRProgramBuilder.h" #include "mlir/Dialect/QIR/Utils/QIRUtils.h" -#include "mlir/Dialect/Utils/Transforms/Passes.h" -#include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "qc_programs.h" #include "qir_programs.h" diff --git a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp index 9266e46b35..4cb2b1cfc3 100644 --- a/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp +++ b/mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp @@ -8,14 +8,14 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #include "TestCaseUtils.h" #include "mlir/Conversion/QCToQIR/QIRBase/QCToQIRBase.h" +#include "mlir/Dialect/MQT/Transforms/Passes.h" #include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QIR/Builder/QIRProgramBuilder.h" #include "mlir/Dialect/QIR/Utils/QIRUtils.h" -#include "mlir/Dialect/Utils/Transforms/Passes.h" -#include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "qc_programs.h" #include "qir_programs.h" diff --git a/mlir/unittests/Dialect/CMakeLists.txt b/mlir/unittests/Dialect/CMakeLists.txt index 04bd1922f4..af4c93346b 100644 --- a/mlir/unittests/Dialect/CMakeLists.txt +++ b/mlir/unittests/Dialect/CMakeLists.txt @@ -12,4 +12,3 @@ add_subdirectory(QC) add_subdirectory(QCO) add_subdirectory(QIR) add_subdirectory(QTensor) -add_subdirectory(Utils) diff --git a/mlir/unittests/Dialect/MQT/CMakeLists.txt b/mlir/unittests/Dialect/MQT/CMakeLists.txt index b181a84fed..7b705e9b19 100644 --- a/mlir/unittests/Dialect/MQT/CMakeLists.txt +++ b/mlir/unittests/Dialect/MQT/CMakeLists.txt @@ -7,3 +7,5 @@ # Licensed under the MIT License add_subdirectory(IR) +add_subdirectory(Transforms) +add_subdirectory(Utils) diff --git a/mlir/unittests/Dialect/Utils/CMakeLists.txt b/mlir/unittests/Dialect/MQT/Transforms/CMakeLists.txt similarity index 59% rename from mlir/unittests/Dialect/Utils/CMakeLists.txt rename to mlir/unittests/Dialect/MQT/Transforms/CMakeLists.txt index ad3b2158b7..2a6a400f31 100644 --- a/mlir/unittests/Dialect/Utils/CMakeLists.txt +++ b/mlir/unittests/Dialect/MQT/Transforms/CMakeLists.txt @@ -6,15 +6,14 @@ # # Licensed under the MIT License -add_executable(mqt-core-mlir-unittests-dialect-utils test_global_phase_normalization.cpp - test_utils.cpp) +set(mqt_transforms_target mqt-core-mlir-unittests-mqt-transforms) +add_executable(${mqt_transforms_target} test_global_phase_normalization.cpp) target_link_libraries( - mqt-core-mlir-unittests-dialect-utils + ${mqt_transforms_target} PRIVATE GTest::gtest_main MLIRArithDialect MLIRControlFlowDialect - MLIRDialectUtils MLIRFuncDialect MLIRIR MLIRMemRefDialect @@ -32,7 +31,7 @@ target_link_libraries( LLVMSupport MQT::CoreDD) -mqt_mlir_configure_unittest_target(mqt-core-mlir-unittests-dialect-utils REQUIRES_EH) +mqt_mlir_configure_unittest_target(${mqt_transforms_target} REQUIRES_EH) -gtest_discover_tests(mqt-core-mlir-unittests-dialect-utils PROPERTIES LABELS mqt-mlir-unittests - DISCOVERY_TIMEOUT 60) +gtest_discover_tests(${mqt_transforms_target} PROPERTIES LABELS mqt-mlir-unittests + DISCOVERY_TIMEOUT 60) diff --git a/mlir/unittests/Dialect/Utils/test_global_phase_normalization.cpp b/mlir/unittests/Dialect/MQT/Transforms/test_global_phase_normalization.cpp similarity index 96% rename from mlir/unittests/Dialect/Utils/test_global_phase_normalization.cpp rename to mlir/unittests/Dialect/MQT/Transforms/test_global_phase_normalization.cpp index 7bc07ee427..e7844d4569 100644 --- a/mlir/unittests/Dialect/Utils/test_global_phase_normalization.cpp +++ b/mlir/unittests/Dialect/MQT/Transforms/test_global_phase_normalization.cpp @@ -10,13 +10,15 @@ #include "ExactUnitaryTest.h" #include "mlir/Conversion/QCToQCO/QCToQCO.h" +#include "mlir/Dialect/MQT/Transforms/GlobalPhaseNormalization.h" +#include "mlir/Dialect/MQT/Utils/Angles.h" +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/IR/QCOps.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" -#include "mlir/Dialect/Utils/Transforms/GlobalPhaseNormalization.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -70,11 +72,11 @@ class GlobalPhaseNormalizationTest : public testing::Test { static void expectFoldableGlobalPhase(Value angle, const double expectedAngle) { - const auto value = utils::valueToConstantDouble(angle); + const auto value = mlir::mqt::valueToConstantDouble(angle); ASSERT_TRUE(value.has_value()); - EXPECT_TRUE(utils::isValidGlobalPhaseAngle(*value)); - EXPECT_NEAR(utils::normalizeAngle(*value - expectedAngle), 0.0, - utils::TOLERANCE); + EXPECT_TRUE(mlir::mqt::isValidGlobalPhaseAngle(*value)); + EXPECT_NEAR(mlir::mqt::normalizeAngle(*value - expectedAngle), 0.0, + mlir::mqt::PARAMETER_COMPARISON_TOLERANCE); } static void expectNormalizedUnitary(OwningOpRef& moduleOp, @@ -146,10 +148,10 @@ TEST_F(GlobalPhaseNormalizationTest, constexpr double half = 1.5; constexpr double two = 2.0; const double expected = - utils::normalizeAngle(static_cast(phaseCount) * half * two); + mlir::mqt::normalizeAngle(static_cast(phaseCount) * half * two); for (int i = 0; i < phaseCount; ++i) { - auto lhs = utils::constantFromScalar(builder, loc, half); - auto rhs = utils::constantFromScalar(builder, loc, two); + auto lhs = mlir::mqt::constantFromScalar(builder, loc, half); + auto rhs = mlir::mqt::constantFromScalar(builder, loc, two); auto angle = arith::MulFOp::create(builder, loc, lhs, rhs); qco::GPhaseOp::create(builder, loc, angle.getResult()); } @@ -185,14 +187,14 @@ TEST_F(GlobalPhaseNormalizationTest, constexpr int64_t intFactor = 3; constexpr double floatFactor = 2.0; const double expected = - utils::normalizeAngle(static_cast(phaseCount) * - static_cast(intFactor) * floatFactor); + mlir::mqt::normalizeAngle(static_cast(phaseCount) * + static_cast(intFactor) * floatFactor); for (int i = 0; i < phaseCount; ++i) { auto intConst = arith::ConstantOp::create( builder, loc, builder.getIntegerAttr(builder.getI64Type(), intFactor)); auto lhs = arith::SIToFPOp::create(builder, loc, builder.getF64Type(), intConst.getResult()); - auto rhs = utils::constantFromScalar(builder, loc, floatFactor); + auto rhs = mlir::mqt::constantFromScalar(builder, loc, floatFactor); auto angle = arith::MulFOp::create(builder, loc, lhs.getResult(), rhs); qco::GPhaseOp::create(builder, loc, angle.getResult()); } @@ -633,7 +635,7 @@ TEST_F(GlobalPhaseNormalizationTest, ZeroControlsReleaseAnUnchangedPhase) { builder, loc, "test", builder.getFunctionType({qubitType}, {qubitType})); auto* entry = function.addEntryBlock(); builder.setInsertionPointToStart(entry); - const auto phase = utils::constantFromScalar(builder, loc, 0.417); + const auto phase = mlir::mqt::constantFromScalar(builder, loc, 0.417); auto ctrl = qco::CtrlOp::create( builder, loc, ValueRange{}, entry->getArgument(0), [&](Value target) { const auto out = qco::XOp::create(builder, loc, target).getQubitOut(); @@ -812,7 +814,7 @@ TEST_F(GlobalPhaseNormalizationTest, for (const double angle : {0.0, std::numbers::pi, -std::numbers::pi, 2.0 * std::numbers::pi, -2.0 * std::numbers::pi}) { qco::GPhaseOp::create(builder, loc, - utils::constantFromScalar(builder, loc, angle)); + mlir::mqt::constantFromScalar(builder, loc, angle)); } func::ReturnOp::create(builder, loc); @@ -856,7 +858,7 @@ TEST_F(GlobalPhaseNormalizationTest, VerifiesPracticalConstantAngleRange) { builder.getFunctionType({}, {})); auto* entry = function.addEntryBlock(); builder.setInsertionPointToStart(entry); - const auto value = utils::constantFromScalar(builder, loc, angle); + const auto value = mlir::mqt::constantFromScalar(builder, loc, angle); if (useQCO) { qco::GPhaseOp::create(builder, loc, value); } else { @@ -868,11 +870,12 @@ TEST_F(GlobalPhaseNormalizationTest, VerifiesPracticalConstantAngleRange) { for (const bool useQCO : {false, true}) { SCOPED_TRACE(useQCO ? "QCO" : "QC"); - EXPECT_TRUE(succeeded(verifyAngle(utils::MAX_GLOBAL_PHASE_ANGLE, useQCO))); + EXPECT_TRUE( + succeeded(verifyAngle(mlir::mqt::MAX_GLOBAL_PHASE_ANGLE, useQCO))); for (const double angle : - {std::nextafter(utils::MAX_GLOBAL_PHASE_ANGLE, + {std::nextafter(mlir::mqt::MAX_GLOBAL_PHASE_ANGLE, std::numeric_limits::infinity()), - -std::nextafter(utils::MAX_GLOBAL_PHASE_ANGLE, + -std::nextafter(mlir::mqt::MAX_GLOBAL_PHASE_ANGLE, std::numeric_limits::infinity()), std::numeric_limits::quiet_NaN(), std::numeric_limits::infinity()}) { diff --git a/mlir/unittests/Dialect/MQT/Utils/CMakeLists.txt b/mlir/unittests/Dialect/MQT/Utils/CMakeLists.txt new file mode 100644 index 0000000000..3096dc4013 --- /dev/null +++ b/mlir/unittests/Dialect/MQT/Utils/CMakeLists.txt @@ -0,0 +1,15 @@ +# 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(mqt_utils_target mqt-core-mlir-unittests-mqt-utils) +add_executable(${mqt_utils_target} test_constant_folding.cpp test_gate_powering.cpp) +target_link_libraries(${mqt_utils_target} PRIVATE GTest::gtest_main MLIRArithDialect + MLIRFuncDialect MLIRIR MLIRMQTUtils) +mqt_mlir_configure_unittest_target(${mqt_utils_target}) + +gtest_discover_tests(${mqt_utils_target} PROPERTIES LABELS mqt-mlir-unittests DISCOVERY_TIMEOUT 60) diff --git a/mlir/unittests/Dialect/Utils/test_utils.cpp b/mlir/unittests/Dialect/MQT/Utils/test_constant_folding.cpp similarity index 74% rename from mlir/unittests/Dialect/Utils/test_utils.cpp rename to mlir/unittests/Dialect/MQT/Utils/test_constant_folding.cpp index 1074fbd75e..41724cf5da 100644 --- a/mlir/unittests/Dialect/Utils/test_utils.cpp +++ b/mlir/unittests/Dialect/MQT/Utils/test_constant_folding.cpp @@ -8,7 +8,7 @@ * Licensed under the MIT License */ -#include "mlir/Dialect/Utils/Utils.h" +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" #include #include @@ -32,7 +32,7 @@ using namespace mlir; namespace { -class UtilsTest : public ::testing::Test { +class ConstantFoldingTest : public ::testing::Test { protected: MLIRContext context; OwningOpRef module; @@ -50,109 +50,109 @@ class UtilsTest : public ::testing::Test { } // namespace -TEST_F(UtilsTest, valueToDouble) { +TEST_F(ConstantFoldingTest, valueToDouble) { constexpr double expectedValue = 1.234; auto op = arith::ConstantOp::create(*builder, builder->getF64FloatAttr(expectedValue)); - const auto stdValue = utils::valueToDouble(op.getResult()); + const auto stdValue = mlir::mqt::valueToDouble(op.getResult()); ASSERT_TRUE(stdValue.has_value()); EXPECT_DOUBLE_EQ(*stdValue, expectedValue); } -TEST_F(UtilsTest, valueToDoubleCastFromInteger) { +TEST_F(ConstantFoldingTest, valueToDoubleCastFromInteger) { auto op = arith::ConstantOp::create(*builder, builder->getI32IntegerAttr(42)); - const auto stdValue = utils::valueToDouble(op.getResult()); + const auto stdValue = mlir::mqt::valueToDouble(op.getResult()); ASSERT_TRUE(stdValue.has_value()); EXPECT_DOUBLE_EQ(*stdValue, 42.0); } -TEST_F(UtilsTest, valueToDoubleCastFromNegativeInteger) { +TEST_F(ConstantFoldingTest, valueToDoubleCastFromNegativeInteger) { auto op = arith::ConstantOp::create(*builder, builder->getSI32IntegerAttr(-123)); - const auto stdValue = utils::valueToDouble(op.getResult()); + const auto stdValue = mlir::mqt::valueToDouble(op.getResult()); ASSERT_TRUE(stdValue.has_value()); EXPECT_DOUBLE_EQ(*stdValue, -123.0); } -TEST_F(UtilsTest, valueToDoubleCastFromMaxUnsignedInteger) { +TEST_F(ConstantFoldingTest, valueToDoubleCastFromMaxUnsignedInteger) { constexpr auto bitCount = 64; auto op = arith::ConstantOp::create( *builder, builder->getIntegerAttr(builder->getIntegerType(bitCount, false), APInt::getMaxValue(bitCount))); - const auto stdValue = utils::valueToDouble(op.getResult()); + const auto stdValue = mlir::mqt::valueToDouble(op.getResult()); ASSERT_TRUE(stdValue.has_value()); EXPECT_DOUBLE_EQ(*stdValue, static_cast(std::numeric_limits::max())); } -TEST_F(UtilsTest, valueToDoubleWrongType) { +TEST_F(ConstantFoldingTest, valueToDoubleWrongType) { auto op = arith::ConstantOp::create(*builder, builder->getStringAttr("test")); - EXPECT_FALSE(utils::valueToDouble(op.getResult()).has_value()); + EXPECT_FALSE(mlir::mqt::valueToDouble(op.getResult()).has_value()); } -TEST_F(UtilsTest, valueToDoubleNonStaticValue) { +TEST_F(ConstantFoldingTest, valueToDoubleNonStaticValue) { auto lhs = arith::ConstantOp::create(*builder, builder->getF64FloatAttr(9.5)); auto rhs = arith::ConstantOp::create(*builder, builder->getF64FloatAttr(21.5)); auto op = arith::AddFOp::create(*builder, lhs, rhs); - EXPECT_FALSE(utils::valueToDouble(op.getResult()).has_value()); + EXPECT_FALSE(mlir::mqt::valueToDouble(op.getResult()).has_value()); } -TEST_F(UtilsTest, attributeToDoubleSignedI128) { +TEST_F(ConstantFoldingTest, attributeToDoubleSignedI128) { constexpr unsigned bitWidth = 128; const auto attr = builder->getIntegerAttr( builder->getIntegerType(bitWidth, /*isSigned=*/true), APInt::getAllOnes(bitWidth)); - const auto asDouble = utils::attributeToDouble(attr); + const auto asDouble = mlir::mqt::attributeToDouble(attr); ASSERT_TRUE(asDouble.has_value()); EXPECT_DOUBLE_EQ(*asDouble, -1.0); } -TEST_F(UtilsTest, attributeToDoubleUnsignedI128) { +TEST_F(ConstantFoldingTest, attributeToDoubleUnsignedI128) { constexpr unsigned bitWidth = 128; APInt bits(bitWidth, 0); bits.setBit(127); const auto attr = builder->getIntegerAttr( builder->getIntegerType(bitWidth, /*isSigned=*/false), bits); - const auto asDouble = utils::attributeToDouble(attr); + const auto asDouble = mlir::mqt::attributeToDouble(attr); ASSERT_TRUE(asDouble.has_value()); EXPECT_DOUBLE_EQ(*asDouble, std::ldexp(1.0, 127)); } -TEST_F(UtilsTest, valueToConstantDoubleNestedFold) { +TEST_F(ConstantFoldingTest, valueToConstantDoubleNestedFold) { auto lhs = arith::ConstantOp::create(*builder, builder->getF64FloatAttr(5.0)); auto num = arith::ConstantOp::create(*builder, builder->getF64FloatAttr(1.0)); auto den = arith::ConstantOp::create(*builder, builder->getF64FloatAttr(2.0)); auto quot = arith::DivFOp::create(*builder, num, den); auto op = arith::SubFOp::create(*builder, lhs, quot); - const auto stdValue = utils::valueToConstantDouble(op.getResult()); + const auto stdValue = mlir::mqt::valueToConstantDouble(op.getResult()); ASSERT_TRUE(stdValue.has_value()); EXPECT_DOUBLE_EQ(*stdValue, 4.5); } -TEST_F(UtilsTest, valueToConstantDoubleNegF) { +TEST_F(ConstantFoldingTest, valueToConstantDoubleNegF) { auto operand = arith::ConstantOp::create(*builder, builder->getF64FloatAttr(2.25)); auto op = arith::NegFOp::create(*builder, operand); - const auto stdValue = utils::valueToConstantDouble(op.getResult()); + const auto stdValue = mlir::mqt::valueToConstantDouble(op.getResult()); ASSERT_TRUE(stdValue.has_value()); EXPECT_DOUBLE_EQ(*stdValue, -2.25); } -TEST_F(UtilsTest, valueToConstantDoubleUIToFP) { +TEST_F(ConstantFoldingTest, valueToConstantDoubleUIToFP) { constexpr uint64_t expectedValue = 7; auto intConst = arith::ConstantOp::create( *builder, builder->getIntegerAttr(builder->getIntegerType(64, false), expectedValue)); auto op = arith::UIToFPOp::create(*builder, builder->getF64Type(), intConst.getResult()); - const auto stdValue = utils::valueToConstantDouble(op.getResult()); + const auto stdValue = mlir::mqt::valueToConstantDouble(op.getResult()); ASSERT_TRUE(stdValue.has_value()); EXPECT_DOUBLE_EQ(*stdValue, static_cast(expectedValue)); } -TEST_F(UtilsTest, valueToConstantDoubleSIToFP) { +TEST_F(ConstantFoldingTest, valueToConstantDoubleSIToFP) { constexpr int64_t expectedValue = -7; auto intConst = arith::ConstantOp::create( *builder, @@ -160,12 +160,12 @@ TEST_F(UtilsTest, valueToConstantDoubleSIToFP) { expectedValue)); auto op = arith::SIToFPOp::create(*builder, builder->getF64Type(), intConst.getResult()); - const auto stdValue = utils::valueToConstantDouble(op.getResult()); + const auto stdValue = mlir::mqt::valueToConstantDouble(op.getResult()); ASSERT_TRUE(stdValue.has_value()); EXPECT_DOUBLE_EQ(*stdValue, static_cast(expectedValue)); } -TEST_F(UtilsTest, valueToConstantDoubleDynamicOperand) { +TEST_F(ConstantFoldingTest, valueToConstantDoubleDynamicOperand) { auto func = func::FuncOp::create(*builder, "dyn", FunctionType::get(&context, {builder->getF64Type()}, @@ -175,28 +175,28 @@ TEST_F(UtilsTest, valueToConstantDoubleDynamicOperand) { builder->setInsertionPointToStart(entry); auto lhs = arith::ConstantOp::create(*builder, builder->getF64FloatAttr(5.0)); auto op = arith::SubFOp::create(*builder, lhs, entry->getArgument(0)); - EXPECT_FALSE(utils::valueToConstantDouble(op.getResult()).has_value()); + EXPECT_FALSE(mlir::mqt::valueToConstantDouble(op.getResult()).has_value()); } -TEST_F(UtilsTest, valueToConstantAttrFoldFailure) { +TEST_F(ConstantFoldingTest, valueToConstantAttrFoldFailure) { // Pure op whose fold fails (shift >= bitwidth). auto lhs = arith::ConstantOp::create(*builder, builder->getI32IntegerAttr(1)); auto rhs = arith::ConstantOp::create(*builder, builder->getI32IntegerAttr(32)); auto op = arith::ShLIOp::create(*builder, lhs, rhs); - EXPECT_FALSE(utils::valueToConstantAttr(op.getResult()).has_value()); + EXPECT_FALSE(mlir::mqt::valueToConstantAttr(op.getResult()).has_value()); } -TEST_F(UtilsTest, valueToConstantAttrMultiResultFold) { +TEST_F(ConstantFoldingTest, valueToConstantAttrMultiResultFold) { // addui_extended folds to two results; valueToConstantAttr requires exactly // one OpFoldResult. auto lhs = arith::ConstantOp::create(*builder, builder->getI32IntegerAttr(1)); auto rhs = arith::ConstantOp::create(*builder, builder->getI32IntegerAttr(2)); auto op = arith::AddUIExtendedOp::create(*builder, lhs, rhs); - EXPECT_FALSE(utils::valueToConstantAttr(op.getSum()).has_value()); + EXPECT_FALSE(mlir::mqt::valueToConstantAttr(op.getSum()).has_value()); } -TEST_F(UtilsTest, valueToConstantAttrIdentityFold) { +TEST_F(ConstantFoldingTest, valueToConstantAttrIdentityFold) { // select(true, x, y) folds to an existing SSA value, not an Attribute. constexpr double expectedValue = 3.25; auto cond = arith::ConstantOp::create(*builder, builder->getBoolAttr(true)); @@ -204,12 +204,12 @@ TEST_F(UtilsTest, valueToConstantAttrIdentityFold) { builder->getF64FloatAttr(expectedValue)); auto y = arith::ConstantOp::create(*builder, builder->getF64FloatAttr(9.0)); auto op = arith::SelectOp::create(*builder, cond, x, y); - const auto attr = utils::valueToConstantAttr(op.getResult()); + const auto attr = mlir::mqt::valueToConstantAttr(op.getResult()); ASSERT_TRUE(attr.has_value()); - EXPECT_DOUBLE_EQ(*utils::attributeToDouble(*attr), expectedValue); + EXPECT_DOUBLE_EQ(*mlir::mqt::attributeToDouble(*attr), expectedValue); } -TEST_F(UtilsTest, valueToConstantDoubleSharedOperandsSuccess) { +TEST_F(ConstantFoldingTest, valueToConstantDoubleSharedOperandsSuccess) { // Repeated doubling reuses the same SSA value as both operands. Without // memoization this is exponential in `depth`. constexpr int depth = 40; @@ -217,12 +217,12 @@ TEST_F(UtilsTest, valueToConstantDoubleSharedOperandsSuccess) { for (int i = 0; i < depth; ++i) { v = arith::AddFOp::create(*builder, v, v); } - const auto stdValue = utils::valueToConstantDouble(v); + const auto stdValue = mlir::mqt::valueToConstantDouble(v); ASSERT_TRUE(stdValue.has_value()); EXPECT_DOUBLE_EQ(*stdValue, static_cast(1ULL << depth)); } -TEST_F(UtilsTest, valueToConstantDoubleSharedOperandsFailure) { +TEST_F(ConstantFoldingTest, valueToConstantDoubleSharedOperandsFailure) { // Same doubling shape rooted at a dynamic value; failures must be cached // too or evaluation is exponential. constexpr int depth = 40; @@ -243,7 +243,7 @@ TEST_F(UtilsTest, valueToConstantDoubleSharedOperandsFailure) { } llvm::DenseMap> cache; - EXPECT_FALSE(utils::valueToConstantAttr(v, cache).has_value()); + EXPECT_FALSE(mlir::mqt::valueToConstantAttr(v, cache).has_value()); ASSERT_EQ(cache.size(), nodes.size()); for (const Value node : nodes) { const auto it = cache.find(node); diff --git a/mlir/unittests/Dialect/MQT/Utils/test_gate_powering.cpp b/mlir/unittests/Dialect/MQT/Utils/test_gate_powering.cpp new file mode 100644 index 0000000000..4a1a95ffe0 --- /dev/null +++ b/mlir/unittests/Dialect/MQT/Utils/test_gate_powering.cpp @@ -0,0 +1,32 @@ +/* + * 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/MQT/Utils/GatePowering.h" + +#include + +#include + +TEST(GatePoweringTest, recognizesIntegerExponents) { + EXPECT_TRUE(mlir::mqt::isIntegerExponent(-2.0)); + EXPECT_TRUE(mlir::mqt::isIntegerExponent(0.0)); + EXPECT_FALSE(mlir::mqt::isIntegerExponent(0.5)); + EXPECT_FALSE( + mlir::mqt::isIntegerExponent(std::numeric_limits::infinity())); +} + +TEST(GatePoweringTest, recognizesEvenIntegerExponents) { + EXPECT_TRUE(mlir::mqt::isEvenExponent(-2.0)); + EXPECT_TRUE(mlir::mqt::isEvenExponent(0.0)); + EXPECT_FALSE(mlir::mqt::isEvenExponent(3.0)); + EXPECT_FALSE(mlir::mqt::isEvenExponent(2.5)); + EXPECT_FALSE( + mlir::mqt::isEvenExponent(std::numeric_limits::infinity())); +} diff --git a/mlir/unittests/Dialect/QC/IR/test_qc_ir.cpp b/mlir/unittests/Dialect/QC/IR/test_qc_ir.cpp index 751b68bbb2..bb19edadbd 100644 --- a/mlir/unittests/Dialect/QC/IR/test_qc_ir.cpp +++ b/mlir/unittests/Dialect/QC/IR/test_qc_ir.cpp @@ -8,18 +8,18 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #include "TestCaseUtils.h" #include "mlir/Dialect/CBit/IR/CBitAttributes.h" #include "mlir/Dialect/CBit/IR/CBitDialect.h" #include "mlir/Dialect/CBit/IR/CBitOps.h" #include "mlir/Dialect/MQT/IR/MQTDialect.h" +#include "mlir/Dialect/MQT/Transforms/Passes.h" +#include "mlir/Dialect/MQT/Utils/DenseUnitary.h" #include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/IR/QCInterfaces.h" #include "mlir/Dialect/QC/IR/QCOps.h" -#include "mlir/Dialect/Utils/DenseUnitary.h" -#include "mlir/Dialect/Utils/Transforms/Passes.h" -#include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "qc_programs.h" @@ -448,11 +448,11 @@ TEST_F(QCTest, DenseUnitaryVerifierRejectsUnsupportedArityAndAttributes) { EXPECT_TRUE(failed(sparseUnitary.verify())); EXPECT_TRUE(failed(realUnitary.verify())); - EXPECT_FALSE(utils::isExactIdentityMatrix(sparseMatrix)); + EXPECT_FALSE(mlir::mqt::isExactIdentityMatrix(sparseMatrix)); const auto rankOneMatrix = DenseElementsAttr::get( RankedTensorType::get({2}, complexType), std::complex{0.0, 0.0}); - EXPECT_FALSE(utils::isExactIdentityMatrix(rankOneMatrix)); - EXPECT_FALSE(utils::isExactIdentityMatrix(realMatrix)); + EXPECT_FALSE(mlir::mqt::isExactIdentityMatrix(rankOneMatrix)); + EXPECT_FALSE(mlir::mqt::isExactIdentityMatrix(realMatrix)); zeroQubitUnitary.erase(); sparseUnitary.erase(); diff --git a/mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp b/mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp index 2a51a5ff3e..7f1c5f73c1 100644 --- a/mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp +++ b/mlir/unittests/Dialect/QC/Translation/test_qasm3_translation.cpp @@ -8,6 +8,7 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #include "TestCaseUtils.h" #include "mlir/Conversion/QCToQCO/QCToQCO.h" #include "mlir/Dialect/CBit/IR/CBitAttributes.h" @@ -21,7 +22,6 @@ #include "mlir/Dialect/QC/Translation/TranslateQASM3ToQC.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" -#include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "qasm_programs.h" #include "qc_programs.h" diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp index 7ddfa81cdc..2a5c915017 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp @@ -8,19 +8,19 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #include "TestCaseUtils.h" #include "mlir/Dialect/CBit/IR/CBitAttributes.h" #include "mlir/Dialect/CBit/IR/CBitDialect.h" #include "mlir/Dialect/CBit/IR/CBitOps.h" #include "mlir/Dialect/MQT/IR/MQTDialect.h" +#include "mlir/Dialect/MQT/Transforms/Passes.h" #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOInterfaces.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" #include "mlir/Dialect/QTensor/IR/QTensorOps.h" -#include "mlir/Dialect/Utils/Transforms/Passes.h" -#include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "qco_programs.h" diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir_matrix.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir_matrix.cpp index f6797324c7..b1257986cd 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir_matrix.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir_matrix.cpp @@ -18,12 +18,12 @@ #include "ir/operations/CompoundOperation.hpp" #include "ir/operations/OpType.hpp" #include "ir/operations/StandardOperation.hpp" +#include "mlir/Dialect/MQT/Utils/GatePowering.h" #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/QCOUtils.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/UGateUtils.h" #include "mlir/Support/Passes.h" #include "qco_programs.h" @@ -759,8 +759,9 @@ TEST_F(QCOMatrixTest, IntegralPowUFoldsPreserveFullMatrixUnderControl) { {std::tuple{0.1, 0.2, 0.3, 2.0}, std::tuple{1.7, -2.1, 0.4, 3.0}, std::tuple{std::numbers::pi, 0.7, -1.2, 8.0}, std::tuple{0.0, 0.3, 0.8, 17.0}, - std::tuple{0.1, 0.2, 0.3, - static_cast(utils::MAX_SAFE_U_POWER_EXPONENT)}}) { + std::tuple{ + 0.1, 0.2, 0.3, + static_cast(mlir::mqt::MAX_SAFE_U_POWER_EXPONENT)}}) { auto moduleOp = QCOProgramBuilder::build(context.get(), [&](auto& b) { auto controlIn = b.staticQubit(0); auto targetIn = b.staticQubit(1); @@ -787,7 +788,7 @@ TEST_F(QCOMatrixTest, IntegralPowUFoldsPreserveFullMatrixUnderControl) { TEST_F(QCOMatrixTest, PowUBeyondSafeExponentRemainsUnchanged) { constexpr double exponent = - static_cast(utils::MAX_SAFE_U_POWER_EXPONENT) + 1.0; + static_cast(mlir::mqt::MAX_SAFE_U_POWER_EXPONENT) + 1.0; auto moduleOp = QCOProgramBuilder::build(context.get(), [&](auto& b) { auto controlIn = b.staticQubit(0); auto targetIn = b.staticQubit(1); diff --git a/mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_euler_decomposition.cpp b/mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_euler_decomposition.cpp index c6e2e1bb7f..ac463b97a1 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_euler_decomposition.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_euler_decomposition.cpp @@ -8,6 +8,7 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/MQT/Utils/Parameters.h" #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/IR/QCOInterfaces.h" @@ -15,7 +16,6 @@ #include "mlir/Dialect/QCO/Transforms/Decomposition/Euler.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Dialect/QCO/Utils/Matrix.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -359,57 +359,59 @@ TEST_P(ZSXXShortcutTest, SynthesisMatchesGateCount) { INSTANTIATE_TEST_SUITE_P( ZSXXShortcuts, ZSXXShortcutTest, - testing::Values(ZSXXShortcutCase{"Identity", - [](MLIRContext*) -> Matrix2x2 { - return Matrix2x2::identity(); - }, - 0, 0, 0}, - ZSXXShortcutCase{"PauliX", - [](MLIRContext*) -> Matrix2x2 { - return XOp::getUnitaryMatrix(); - }, - 0, 0, 1}, - ZSXXShortcutCase{"PureZ", - [](MLIRContext*) -> Matrix2x2 { - return RZOp::unitaryMatrix(0.3) * - RZOp::unitaryMatrix(0.7); - }, - 1, 0, 0}, - ZSXXShortcutCase{"ZYZNearZeroTheta", - [](MLIRContext*) -> Matrix2x2 { - constexpr double tol = - 0.5 * utils::TOLERANCE; - return RZOp::unitaryMatrix(0.4) * - RYOp::unitaryMatrix(tol) * - RZOp::unitaryMatrix(0.3); - }, - 1, 0, 0}, - ZSXXShortcutCase{"RYHalfPi", - [](MLIRContext* ctx) -> Matrix2x2 { - return rotationMatrix( - ctx, std::numbers::pi / 2.0); - }, - 2, 1, 0}, - ZSXXShortcutCase{"RYNearHalfPi", - [](MLIRContext* ctx) -> Matrix2x2 { - return rotationMatrix( - ctx, (std::numbers::pi / 2.0) + - (0.5 * utils::TOLERANCE)); - }, - 2, 1, 0}, - ZSXXShortcutCase{"RYNearZero", - [](MLIRContext* ctx) -> Matrix2x2 { - return rotationMatrix( - ctx, 0.5 * utils::TOLERANCE); - }, - 0, 0, 0}, - ZSXXShortcutCase{"RYNearPi", - [](MLIRContext* ctx) -> Matrix2x2 { - return rotationMatrix( - ctx, std::numbers::pi - - (0.5 * utils::TOLERANCE)); - }, - 1, 0, 1}), + testing::Values( + ZSXXShortcutCase{ + "Identity", + [](MLIRContext*) -> Matrix2x2 { return Matrix2x2::identity(); }, 0, + 0, 0}, + ZSXXShortcutCase{ + "PauliX", + [](MLIRContext*) -> Matrix2x2 { return XOp::getUnitaryMatrix(); }, + 0, 0, 1}, + ZSXXShortcutCase{"PureZ", + [](MLIRContext*) -> Matrix2x2 { + return RZOp::unitaryMatrix(0.3) * + RZOp::unitaryMatrix(0.7); + }, + 1, 0, 0}, + ZSXXShortcutCase{"ZYZNearZeroTheta", + [](MLIRContext*) -> Matrix2x2 { + constexpr double tol = + 0.5 * mlir::mqt::PARAMETER_COMPARISON_TOLERANCE; + return RZOp::unitaryMatrix(0.4) * + RYOp::unitaryMatrix(tol) * + RZOp::unitaryMatrix(0.3); + }, + 1, 0, 0}, + ZSXXShortcutCase{"RYHalfPi", + [](MLIRContext* ctx) -> Matrix2x2 { + return rotationMatrix(ctx, + std::numbers::pi / 2.0); + }, + 2, 1, 0}, + ZSXXShortcutCase{ + "RYNearHalfPi", + [](MLIRContext* ctx) -> Matrix2x2 { + return rotationMatrix( + ctx, (std::numbers::pi / 2.0) + + (0.5 * mlir::mqt::PARAMETER_COMPARISON_TOLERANCE)); + }, + 2, 1, 0}, + ZSXXShortcutCase{"RYNearZero", + [](MLIRContext* ctx) -> Matrix2x2 { + return rotationMatrix( + ctx, + 0.5 * mlir::mqt::PARAMETER_COMPARISON_TOLERANCE); + }, + 0, 0, 0}, + ZSXXShortcutCase{ + "RYNearPi", + [](MLIRContext* ctx) -> Matrix2x2 { + return rotationMatrix( + ctx, std::numbers::pi - + (0.5 * mlir::mqt::PARAMETER_COMPARISON_TOLERANCE)); + }, + 1, 0, 1}), [](const testing::TestParamInfo& info) { return std::string(info.param.label); }); @@ -471,7 +473,7 @@ TEST(EulerSynthesisTest, RandomReconstructionAllBases) { TEST(EulerAnglesCoverageTest, ParamsZYZUsesOffDiagonal01When10IsNearZero) { Matrix2x2 matrix = RXOp::unitaryMatrix(0.4); matrix(1, 0) = Complex{0.0, 0.0}; - ASSERT_GT(std::abs(matrix(0, 1)), utils::TOLERANCE); + ASSERT_GT(std::abs(matrix(0, 1)), mlir::mqt::PARAMETER_COMPARISON_TOLERANCE); const EulerAngles angles = anglesFromUnitary(matrix, ZYZ); EXPECT_TRUE(std::isfinite(angles.theta)); EXPECT_TRUE(std::isfinite(angles.phi)); @@ -485,9 +487,9 @@ TEST(EulerAnglesCoverageTest, PhaseOnlyDecompositionSkipsRotationGates) { const Matrix2x2 matrix = Matrix2x2::fromElements(scale, 0, 0, scale); ASSERT_FALSE(matrix.isApprox(Matrix2x2::identity())); const EulerAngles angles = anglesFromUnitary(matrix, ZYZ); - EXPECT_LE(std::abs(angles.theta), utils::TOLERANCE); - EXPECT_LE(std::abs(angles.phi), utils::TOLERANCE); - EXPECT_LE(std::abs(angles.lambda), utils::TOLERANCE); + EXPECT_LE(std::abs(angles.theta), mlir::mqt::PARAMETER_COMPARISON_TOLERANCE); + EXPECT_LE(std::abs(angles.phi), mlir::mqt::PARAMETER_COMPARISON_TOLERANCE); + EXPECT_LE(std::abs(angles.lambda), mlir::mqt::PARAMETER_COMPARISON_TOLERANCE); const auto circuit = synthesizeMatrix(fx.ctx(), matrix, ZYZ); ASSERT_TRUE(succeeded(verify(*circuit.mlirModule))); EXPECT_EQ(countZYZGates(circuit.func), 0U); @@ -509,7 +511,7 @@ TEST(EulerAnglesCoverageTest, UBasisNonzeroThetaEmitsSingleUGate) { fx.setUp(); const Matrix2x2 matrix = RYOp::unitaryMatrix(1.2); const EulerAngles angles = anglesFromUnitary(matrix, U); - ASSERT_GT(std::abs(angles.theta), utils::TOLERANCE); + ASSERT_GT(std::abs(angles.theta), mlir::mqt::PARAMETER_COMPARISON_TOLERANCE); expectSynthesizedMatrix(fx.ctx(), matrix, U, [](func::FuncOp funcOp, const Matrix2x2& /*matrix*/) { EXPECT_EQ(countOps(funcOp), 1U); @@ -522,7 +524,7 @@ TEST(EulerAnglesCoverageTest, RBasisNonzeroThetaEmitsThreeRGates) { fx.setUp(); const Matrix2x2 matrix = HOp::getUnitaryMatrix(); const EulerAngles angles = anglesFromUnitary(matrix, R); - ASSERT_GT(std::abs(angles.theta), utils::TOLERANCE); + ASSERT_GT(std::abs(angles.theta), mlir::mqt::PARAMETER_COMPARISON_TOLERANCE); expectSynthesizedMatrix(fx.ctx(), matrix, R, [](func::FuncOp funcOp, const Matrix2x2& /*matrix*/) { EXPECT_EQ(countOps(funcOp), 3U); @@ -532,7 +534,7 @@ TEST(EulerAnglesCoverageTest, RBasisNonzeroThetaEmitsThreeRGates) { TEST(EulerAnglesCoverageTest, Mod2PiMapsPiBoundaryThroughSynthesis) { TestFixture fx; fx.setUp(); - constexpr double eps = 0.5 * utils::TOLERANCE; + constexpr double eps = 0.5 * mlir::mqt::PARAMETER_COMPARISON_TOLERANCE; const Complex global = std::polar(1.0, std::numbers::pi - eps); const Matrix2x2 matrix = Matrix2x2::fromElements(global, 0, 0, global); expectSynthesizedMatrix(fx.ctx(), matrix, U, diff --git a/mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_multi_controlled_decomposition.cpp b/mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_multi_controlled_decomposition.cpp index 03a9ea7131..a222b1c648 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_multi_controlled_decomposition.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Decomposition/test_multi_controlled_decomposition.cpp @@ -16,13 +16,13 @@ #include "ir/Definitions.hpp" #include "ir/QuantumComputation.hpp" #include "ir/operations/Control.hpp" +#include "mlir/Dialect/MQT/Utils/Modifiers.h" #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #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/DDFunctionality.h" -#include "mlir/Dialect/Utils/Utils.h" #include #include @@ -745,7 +745,8 @@ TEST_F(MultiControlledDecompositionTest, size_t controlledSwap = 0; moduleOp->walk([&](CtrlOp op) { if (op.getNumControls() == 1 && op.getNumTargets() == 2) { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = + mlir::mqt::getSoleBodyUnitary(*op.getBody()); if (inner && isa(inner.getOperation())) { ++controlledSwap; } @@ -798,7 +799,8 @@ TEST_F(MultiControlledDecompositionTest, LeavesUnsupportedCtrlUntouched) { size_t controlledDcx = 0; moduleOp->walk([&](CtrlOp op) { if (op.getNumTargets() == 2) { - auto inner = utils::getSoleBodyUnitary(*op.getBody()); + auto inner = + mlir::mqt::getSoleBodyUnitary(*op.getBody()); if (inner && isa(inner.getOperation())) { ++controlledDcx; } 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 index 7e4921c5e1..01868510f3 100644 --- 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 @@ -38,7 +38,7 @@ def normalize_angle(a: float) -> float: def normalize_global_phase(a: float) -> float: - """Normalize to (-pi, pi], matching utils::normalizeAngle. + """Normalize to (-pi, pi], matching mqt::normalizeAngle. Returns: Angle in the range (-pi, pi]. diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_hadamard_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_hadamard_lifting.cpp index 71dcc68646..56e38eff1c 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_hadamard_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_hadamard_lifting.cpp @@ -8,11 +8,11 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" +#include "mlir/Dialect/MQT/Transforms/GlobalPhaseNormalization.h" #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" -#include "mlir/Dialect/Utils/Transforms/GlobalPhaseNormalization.h" -#include "mlir/Support/IRVerification.h" #include #include diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp index 8ae6e3e136..d5888e4602 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_measurement_lifting.cpp @@ -8,10 +8,10 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" -#include "mlir/Support/IRVerification.h" #include #include 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 d25fb021f0..ae93590b2b 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 @@ -9,11 +9,12 @@ */ #include "ExactUnitaryTest.h" +#include "mlir/Dialect/MQT/Utils/Angles.h" +#include "mlir/Dialect/MQT/Utils/ConstantFolding.h" #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 "mlir/Dialect/Utils/Utils.h" #include #include @@ -192,7 +193,7 @@ class MergeSingleQubitRotationGatesTest : public ::testing::Test { * parameter is equal to the expected one. */ void expectGPhaseParam(double expected, double tolerance = 1e-8) { - expected = utils::normalizeAngle(expected); + expected = mlir::mqt::normalizeAngle(expected); auto param = getGPhaseParam(); if (expected == 0.0) { EXPECT_FALSE(param.has_value()); @@ -955,8 +956,8 @@ TEST_F(MergeSingleQubitRotationGatesTest, auto phase = getGPhaseParam(); ASSERT_TRUE(phase.has_value()); - EXPECT_TRUE(utils::isValidGlobalPhaseAngle(*phase)); - EXPECT_NEAR(*phase, utils::normalizeAngle(*phase), 1e-8); + EXPECT_TRUE(mlir::mqt::isValidGlobalPhaseAngle(*phase)); + EXPECT_NEAR(*phase, mlir::mqt::normalizeAngle(*phase), 1e-8); } TEST_F(MergeSingleQubitRotationGatesTest, @@ -1002,10 +1003,11 @@ TEST_F(MergeSingleQubitRotationGatesTest, ASSERT_TRUE(gOp); // Still SSA in the angles / gphase before binding concrete values. - EXPECT_FALSE(utils::valueToConstantDouble(uOp.getPhi()).has_value()); + EXPECT_FALSE(mlir::mqt::valueToConstantDouble(uOp.getPhi()).has_value()); EXPECT_TRUE(valueDependsOn(uOp.getPhi(), funcOp.getArgument(0))); EXPECT_TRUE(valueDependsOn(uOp.getPhi(), funcOp.getArgument(1))); - EXPECT_FALSE(utils::valueToConstantDouble(gOp.getParameter(0)).has_value()); + EXPECT_FALSE( + mlir::mqt::valueToConstantDouble(gOp.getParameter(0)).has_value()); EXPECT_TRUE(valueDependsOn(gOp.getParameter(0), funcOp.getArgument(0))); EXPECT_TRUE(valueDependsOn(gOp.getParameter(0), funcOp.getArgument(1))); @@ -1019,19 +1021,19 @@ TEST_F(MergeSingleQubitRotationGatesTest, // Bind controlled values and check the folded RZ(a);RZ(b) formulas: // U(0, wrap(a+b), 0), gphase = normalize(-(phi+lambda)/2). bindLeadingArgs(funcOp, {angleA, angleB}); - const auto theta = utils::valueToConstantDouble(uOp.getTheta()); - const auto phi = utils::valueToConstantDouble(uOp.getPhi()); - const auto lambda = utils::valueToConstantDouble(uOp.getLambda()); - const auto phase = utils::valueToConstantDouble(gOp.getParameter(0)); + const auto theta = mlir::mqt::valueToConstantDouble(uOp.getTheta()); + const auto phi = mlir::mqt::valueToConstantDouble(uOp.getPhi()); + const auto lambda = mlir::mqt::valueToConstantDouble(uOp.getLambda()); + const auto phase = mlir::mqt::valueToConstantDouble(gOp.getParameter(0)); ASSERT_TRUE(theta.has_value()); ASSERT_TRUE(phi.has_value()); ASSERT_TRUE(lambda.has_value()); ASSERT_TRUE(phase.has_value()); EXPECT_NEAR(*theta, 0.0, 1e-6); - EXPECT_NEAR(*phi, utils::normalizeAngle(angleA + angleB), 1e-6); + EXPECT_NEAR(*phi, mlir::mqt::normalizeAngle(angleA + angleB), 1e-6); EXPECT_NEAR(*lambda, 0.0, 1e-6); - EXPECT_NEAR(*phase, utils::normalizeAngle(-(*phi + *lambda) / 2.0), 1e-6); - EXPECT_TRUE(utils::isValidGlobalPhaseAngle(*phase)); + EXPECT_NEAR(*phase, mlir::mqt::normalizeAngle(-(*phi + *lambda) / 2.0), 1e-6); + EXPECT_TRUE(mlir::mqt::isValidGlobalPhaseAngle(*phase)); EXPECT_FALSE(std::isnan(*theta)); EXPECT_FALSE(std::isnan(*phi)); EXPECT_FALSE(std::isnan(*lambda)); @@ -1069,15 +1071,15 @@ TEST_F(MergeSingleQubitRotationGatesTest, valueDependsOn(uOp.getLambda(), funcOp.getArgument(0))); bindLeadingArgs(funcOp, {angle}); - const auto theta = utils::valueToConstantDouble(uOp.getTheta()); - const auto phi = utils::valueToConstantDouble(uOp.getPhi()); - const auto lambda = utils::valueToConstantDouble(uOp.getLambda()); + const auto theta = mlir::mqt::valueToConstantDouble(uOp.getTheta()); + const auto phi = mlir::mqt::valueToConstantDouble(uOp.getPhi()); + const auto lambda = mlir::mqt::valueToConstantDouble(uOp.getLambda()); ASSERT_TRUE(theta.has_value()); ASSERT_TRUE(phi.has_value()); ASSERT_TRUE(lambda.has_value()); double globalPhase = 0.0; module->walk([&](GPhaseOp op) { - const auto phase = utils::valueToConstantDouble(op.getParameter(0)); + const auto phase = mlir::mqt::valueToConstantDouble(op.getParameter(0)); ASSERT_TRUE(phase.has_value()); globalPhase += *phase; }); @@ -1143,17 +1145,17 @@ TEST_F(MergeSingleQubitRotationGatesTest, // P(a);P(b) → U(0, wrap(a+b), 0) with inputPhase (a+b)/2 cancelling the U // intrinsic phase, so gphase folds to ~0 under controlled values. bindLeadingArgs(funcOp, {angleA, angleB}); - const auto theta = utils::valueToConstantDouble(uOp.getTheta()); - const auto phi = utils::valueToConstantDouble(uOp.getPhi()); - const auto lambda = utils::valueToConstantDouble(uOp.getLambda()); - const auto phase = utils::valueToConstantDouble(gOp.getParameter(0)); + const auto theta = mlir::mqt::valueToConstantDouble(uOp.getTheta()); + const auto phi = mlir::mqt::valueToConstantDouble(uOp.getPhi()); + const auto lambda = mlir::mqt::valueToConstantDouble(uOp.getLambda()); + const auto phase = mlir::mqt::valueToConstantDouble(gOp.getParameter(0)); ASSERT_TRUE(theta.has_value()); ASSERT_TRUE(phi.has_value()); ASSERT_TRUE(lambda.has_value()); ASSERT_TRUE(phase.has_value()); EXPECT_NEAR(*theta, 0.0, 1e-6); - EXPECT_NEAR(*phi, utils::normalizeAngle(angleA + angleB), 1e-6); + EXPECT_NEAR(*phi, mlir::mqt::normalizeAngle(angleA + angleB), 1e-6); EXPECT_NEAR(*lambda, 0.0, 1e-6); EXPECT_NEAR(*phase, 0.0, 1e-6); - EXPECT_TRUE(utils::isValidGlobalPhaseAngle(*phase)); + EXPECT_TRUE(mlir::mqt::isValidGlobalPhaseAngle(*phase)); } diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_remove_dead_gates.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_remove_dead_gates.cpp index 773c9fe8a0..e585a8f921 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_remove_dead_gates.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_remove_dead_gates.cpp @@ -8,13 +8,13 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #include "TestCaseUtils.h" #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 "mlir/Dialect/QTensor/IR/QTensorDialect.h" -#include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "programs/qco_programs.h" diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_replace_classical_controls.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_replace_classical_controls.cpp index fe5bd3a75f..09e97c9dbb 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_replace_classical_controls.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_replace_classical_controls.cpp @@ -8,11 +8,11 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #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 "mlir/Support/IRVerification.h" #include #include diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_reuse_qubits.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_reuse_qubits.cpp index 1abf58abe2..1dd26a078e 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_reuse_qubits.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_reuse_qubits.cpp @@ -8,11 +8,11 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #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 "mlir/Support/IRVerification.h" #include #include diff --git a/mlir/unittests/Dialect/QIR/IR/test_qir_ir.cpp b/mlir/unittests/Dialect/QIR/IR/test_qir_ir.cpp index 4f5da126fb..8d275d629d 100644 --- a/mlir/unittests/Dialect/QIR/IR/test_qir_ir.cpp +++ b/mlir/unittests/Dialect/QIR/IR/test_qir_ir.cpp @@ -8,10 +8,10 @@ * Licensed under the MIT License */ +#include "Support/IRVerification.h" #include "TestCaseUtils.h" #include "mlir/Dialect/QIR/Builder/QIRProgramBuilder.h" #include "mlir/Dialect/QIR/Utils/QIRUtils.h" -#include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "qir_programs.h" @@ -46,18 +46,18 @@ namespace { struct QIRTestCase { std::string name; - mqt::test::NamedMLIRBuilder programBuilder; - mqt::test::NamedMLIRBuilder referenceBuilder; + ::mqt::test::NamedMLIRBuilder programBuilder; + ::mqt::test::NamedMLIRBuilder referenceBuilder; friend std::ostream& operator<<(std::ostream& os, const QIRTestCase& info); }; // NOLINTNEXTLINE(llvm-prefer-static-over-anonymous-namespace) std::ostream& operator<<(std::ostream& os, const QIRTestCase& info) { - return os << "QIR{" << info.name - << ", original=" << mqt::test::displayName(info.programBuilder.name) + return os << "QIR{" << info.name << ", original=" + << ::mqt::test::displayName(info.programBuilder.name) << ", reference=" - << mqt::test::displayName(info.referenceBuilder.name) << "}"; + << ::mqt::test::displayName(info.referenceBuilder.name) << "}"; } class QIRTest : public testing::TestWithParam { @@ -78,9 +78,9 @@ class QIRTest : public testing::TestWithParam { TEST_P(QIRTest, ProgramEquivalence) { const auto& [_, programBuilder, referenceBuilder] = GetParam(); const auto name = " (" + GetParam().name + ")"; - mqt::test::DeferredPrinter printer; + ::mqt::test::DeferredPrinter printer; - auto program = mqt::test::buildMLIRProgram( + auto program = ::mqt::test::buildMLIRProgram( context.get(), programBuilder, QIRProgramBuilder::Profile::Adaptive); ASSERT_TRUE(program); printer.record(program.get(), "Original QIR IR" + name); @@ -90,7 +90,7 @@ TEST_P(QIRTest, ProgramEquivalence) { printer.record(program.get(), "Canonicalized QIR IR" + name); EXPECT_TRUE(verify(*program).succeeded()); - auto reference = mqt::test::buildMLIRProgram( + auto reference = ::mqt::test::buildMLIRProgram( context.get(), referenceBuilder, QIRProgramBuilder::Profile::Adaptive); ASSERT_TRUE(reference); printer.record(reference.get(), "Reference QIR IR" + name); diff --git a/mlir/unittests/Dialect/QTensor/IR/test_qtensor_ir.cpp b/mlir/unittests/Dialect/QTensor/IR/test_qtensor_ir.cpp index ded8914d6c..61e9ec0ea6 100644 --- a/mlir/unittests/Dialect/QTensor/IR/test_qtensor_ir.cpp +++ b/mlir/unittests/Dialect/QTensor/IR/test_qtensor_ir.cpp @@ -13,6 +13,7 @@ * @brief Dedicated unit-test suite for the QTensor MLIR dialect. */ +#include "Support/IRVerification.h" #include "TestCaseUtils.h" #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" @@ -20,7 +21,6 @@ #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" #include "mlir/Dialect/QTensor/IR/QTensorOps.h" #include "mlir/Dialect/QTensor/IR/QTensorUtils.h" -#include "mlir/Support/IRVerification.h" #include "mlir/Support/Passes.h" #include "qco_programs.h" @@ -520,8 +520,8 @@ TEST_F(QTensorTest, ResetAfterExtractThroughSameIndexInsertIsNotEliminated) { struct QTensorIntegrationTestCase { std::string name; - mqt::test::NamedMLIRBuilder programBuilder; - mqt::test::NamedMLIRBuilder referenceBuilder; + ::mqt::test::NamedMLIRBuilder programBuilder; + ::mqt::test::NamedMLIRBuilder referenceBuilder; friend std::ostream& operator<<(std::ostream& os, const QTensorIntegrationTestCase& info); @@ -551,9 +551,9 @@ class QTensorIntegrationTest TEST_P(QTensorIntegrationTest, ProgramEquivalence) { const auto& [_, programBuilder, referenceBuilder] = GetParam(); const auto name = " (" + GetParam().name + ")"; - mqt::test::DeferredPrinter printer; + ::mqt::test::DeferredPrinter printer; - auto program = mqt::test::buildMLIRProgram(context.get(), programBuilder); + auto program = ::mqt::test::buildMLIRProgram(context.get(), programBuilder); ASSERT_TRUE(program); printer.record(program.get(), "Original QTensor IR" + name); EXPECT_TRUE(verify(*program).succeeded()); @@ -562,7 +562,8 @@ TEST_P(QTensorIntegrationTest, ProgramEquivalence) { printer.record(program.get(), "Canonicalized QTensor IR" + name); EXPECT_TRUE(verify(*program).succeeded()); - auto reference = mqt::test::buildMLIRProgram(context.get(), referenceBuilder); + auto reference = + ::mqt::test::buildMLIRProgram(context.get(), referenceBuilder); ASSERT_TRUE(reference); printer.record(reference.get(), "Reference QTensor IR" + name); EXPECT_TRUE(verify(*reference).succeeded()); diff --git a/mlir/lib/Support/IRVerification.cpp b/mlir/unittests/Support/IRVerification.cpp similarity index 99% rename from mlir/lib/Support/IRVerification.cpp rename to mlir/unittests/Support/IRVerification.cpp index c7cbd6da84..5d83feda89 100644 --- a/mlir/lib/Support/IRVerification.cpp +++ b/mlir/unittests/Support/IRVerification.cpp @@ -8,7 +8,7 @@ * Licensed under the MIT License */ -#include "mlir/Support/IRVerification.h" +#include "Support/IRVerification.h" #include "mlir/Dialect/QC/IR/QCOps.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" diff --git a/mlir/include/mlir/Support/IRVerification.h b/mlir/unittests/Support/IRVerification.h similarity index 100% rename from mlir/include/mlir/Support/IRVerification.h rename to mlir/unittests/Support/IRVerification.h