diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index f62963f382..ee8c4cb710 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -53,4 +53,4 @@ jobs: uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0 with: subject-path: dist/* - - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9543f3040b..f993e71948 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -164,7 +164,7 @@ jobs: build-project: true files-changed-only: true setup-python: true - install-pkgs: "nanobind==2.11.0" + install-pkgs: "nanobind==2.12.0" cpp-linter-extra-args: "-std=c++20" setup-mlir: true llvm-version: 22.1.0 diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 0417a60d54..9b42c2b327 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -15,4 +15,4 @@ jobs: contents: write runs-on: ubuntu-slim steps: - - uses: release-drafter/release-drafter@139054aeaa9adc52ab36ddf67437541f039b88e2 # v7.1.1 + - uses: release-drafter/release-drafter@5de93583980a40bd78603b6dfdcda5b4df377b32 # v7.2.0 diff --git a/.github/workflows/templating.yml b/.github/workflows/templating.yml index 27f294d062..44fa9b65ff 100644 --- a/.github/workflows/templating.yml +++ b/.github/workflows/templating.yml @@ -22,7 +22,7 @@ jobs: JSON_CONTENT=$(cat .github/workflow_inputs/release_drafter_categories.json | jq -c .) echo "release_drafter_categories=$JSON_CONTENT" >> $GITHUB_OUTPUT - id: create-token - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 + uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 with: app-id: ${{ secrets.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} diff --git a/.license-tools-config.json b/.license-tools-config.json index cccb855b53..09e2044147 100644 --- a/.license-tools-config.json +++ b/.license-tools-config.json @@ -34,6 +34,7 @@ ".*\\.tex", "uv\\.lock", "py\\.typed", - ".*build.*" + ".*build.*", + "(^|/)LICENSE$" ] } diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9c0176901e..808508ee12 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,8 +15,6 @@ ci: skip: [ty-check] repos: - # Priority 0: Fast validation and independent fixers - ## Standard hooks - repo: https://github.com/pre-commit/pre-commit-hooks rev: v6.0.0 @@ -26,14 +24,7 @@ repos: - id: end-of-file-fixer priority: 1 - id: trailing-whitespace - priority: 1 - - ## Check the pyproject.toml file - - repo: https://github.com/henryiii/validate-pyproject-schema-store - rev: 2026.04.03 - hooks: - - id: validate-pyproject - priority: 0 + priority: 2 ## Check JSON schemata - repo: https://github.com/python-jsonschema/check-jsonschema @@ -44,7 +35,29 @@ repos: - id: check-readthedocs priority: 0 - ## Catch common capitalization mistakes + ## Check best practices for scientific Python code + - repo: https://github.com/scientific-python/cookie + rev: 2026.04.04 + hooks: + - id: sp-repo-review + additional_dependencies: ["repo-review[cli]"] + priority: 0 + + ## Check pyproject.toml file + - repo: https://github.com/henryiii/validate-pyproject-schema-store + rev: 2026.04.17 + hooks: + - id: validate-pyproject + priority: 0 + + ## Ensure uv.lock is up to date + - repo: https://github.com/astral-sh/uv-pre-commit + rev: 0.11.7 + hooks: + - id: uv-lock + priority: 0 + + ## Check for common capitalization mistakes - repo: local hooks: - id: disallow-caps @@ -54,36 +67,21 @@ repos: exclude: ^(\.pre-commit-config\.yaml)$ priority: 0 - ## Check for spelling + ## Check for typos - repo: https://github.com/adhtruong/mirrors-typos - rev: v1.45.0 + rev: v1.45.1 hooks: - id: typos - priority: 0 - - ## Check best practices for scientific Python code - - repo: https://github.com/scientific-python/cookie - rev: 2026.04.04 - hooks: - - id: sp-repo-review - additional_dependencies: ["repo-review[cli]"] - priority: 0 + priority: 3 - ## Check for license headers + ## Check license headers - repo: https://github.com/emzeat/mz-lictools rev: v2.9.0 hooks: - id: license-tools - priority: 0 + priority: 4 - ## Ensure uv lock file is up-to-date - - repo: https://github.com/astral-sh/uv-pre-commit - rev: 0.11.3 - hooks: - - id: uv-lock - priority: 0 - - ## Tidy up BibTeX files + ## Format BibTeX files with bibtex-tidy - repo: https://github.com/FlamingTempura/bibtex-tidy rev: v1.14.0 hooks: @@ -100,24 +98,17 @@ repos: "--trailing-commas", "--remove-empty-fields", ] - priority: 0 + priority: 5 - # Priority 1: Second-pass fixers - - ## Format C++ files with clang-format - - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v22.1.2 + ## Format configuration files with prettier + - repo: https://github.com/rbubley/mirrors-prettier + rev: v3.8.3 hooks: - - id: clang-format - types_or: [c++, c, cuda] - priority: 1 - - id: clang-format - name: clang-format (TableGen) - types_or: [file] - files: \.td$ - priority: 1 + - id: prettier + types_or: [yaml, markdown, html, css, scss, javascript, json, json5] + priority: 5 - ## Format CMakeLists.txt files + ## Format CMake files with cmake-format - repo: https://github.com/cheshirekow/cmake-format-precommit rev: v0.6.13 hooks: @@ -125,38 +116,33 @@ repos: additional_dependencies: [pyyaml] types: [file] files: (\.cmake|CMakeLists.txt)(.in)?$ - priority: 1 + priority: 5 - ## Format configuration files with prettier - - repo: https://github.com/rbubley/mirrors-prettier - rev: v3.8.1 + ## Format C++ files with clang-format + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v22.1.3 hooks: - - id: prettier - types_or: [yaml, markdown, html, css, scss, javascript, json, json5] - priority: 1 + - id: clang-format + types_or: [c++, c, cuda] + priority: 5 + - id: clang-format + name: clang-format (TableGen) + types_or: [file] + files: \.td$ + priority: 5 - ## Python linting using ruff + ## Format and lint Python files with ruff - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.15.9 + rev: v0.15.11 hooks: - id: ruff-format - priority: 1 + types_or: [python, pyi, jupyter, markdown] + priority: 6 - id: ruff-check require_serial: true - priority: 2 - - # Priority 2+: Final checks and fixers + priority: 7 - ## Also run Black on examples in the documentation (needs to run after ruff format) - - repo: https://github.com/adamchainz/blacken-docs - rev: 1.20.0 - hooks: - - id: blacken-docs - language: python - additional_dependencies: [black==26.*] - priority: 2 - - ## Static type checking using ty (needs to run after lockfile update/ruff format, and ruff lint) + ## Check Python types with ty - repo: local hooks: - id: ty-check @@ -167,4 +153,4 @@ repos: types_or: [python, pyi, jupyter] exclude: ^(docs/|mlir/|eval/) pass_filenames: false - priority: 3 + priority: 8 diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cf9efb0be..bf954c9e9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,26 +11,46 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Added -- ✨ Add Sampler and Estimator Primitives to the QDMI-Qiskit Interface ([#1507]) ([**@marcelwa**]) +- ✨ Add a `merge-single-qubit-rotation-gates` pass for merging consecutive rotation gates using quaternions ([#1407]) ([**@J4MMlE**]) - ✨ Add conversions between `jeff` and QCO ([#1479], [#1548], [#1565], [#1637]) ([**@denialhaag**]) -- ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588]) ([**@MatthiasReumann**]) +- ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588], [#1664]) ([**@MatthiasReumann**], [**@burgholzer**]) - ✨ Add initial infrastructure for new QC and QCO MLIR dialects - ([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635]) + ([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521], [#1542], [#1548], [#1550], [#1554], [#1567], [#1569], [#1570], [#1572], [#1573], [#1580], [#1602], [#1620], [#1623], [#1624], [#1626], [#1627], [#1635], [#1673], [#1675]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**], [**@simon1hofmann**]) ### Changed -- 📦 Switch to component-based installation for the MQT Core Python package ([#1596]) ([**@burgholzer**]) -- ⬆️ Update QDMI to latest version from stable `v1.2.x` branch ([#1593]) ([**@burgholzer**]) -- ⬆️ Update `clang-tidy` to version 22 ([#1564]) ([**@denialhaag**], [**@burgholzer**]) -- 👷 Build on `macos-26`/`macos-26-intel` by default and `macos-15`/`macos-15-intel` for extensive tests ([#1571]) ([**@denialhaag**]) - ⬆️ Require LLVM 22.1 for C++ library builds ([#1549]) ([**@burgholzer**], [**@denialhaag**]) -- 📦 Build MLIR by default for C++ library builds ([#1356]) ([**@burgholzer**], [**@denialhaag**]) +- � Build MLIR by default for C++ library builds ([#1356]) ([**@burgholzer**], [**@denialhaag**]) ### Removed -- 🔥 Remove the density matrix support from the MQT Core DD package ([#1466]) ([**@burgholzer**]) -- 🔥 Remove `datastructures` (`ds`) (sub)library from MQT Core ([#1458]) ([**@burgholzer**]) +- � Remove the density matrix support from the MQT Core DD package ([#1466]) ([**@burgholzer**]) +- � Remove `datastructures` (`ds`) (sub)library from MQT Core ([#1458]) ([**@burgholzer**]) + +## [3.5.1] - 2026-04-23 + +### Fixed + +- � Fix malformed include directories in exported `nlohmann_json` CMake targets for component-based installs ([#1662]) ([**@burgholzer**]) + +## [3.5.0] - 2026-04-21 + +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#350)._ + +### Added + +- ✨ Add support for multi-controlled gates to ZX package ([#1380]) ([**@keefehuang**]) +- ✨ Add Sampler and Estimator primitives to the QDMI-Qiskit interface ([#1507]) ([**@marcelwa**]) + +### Changed + +- ⬆️ Update `nanobind` to version 2.12.0 ([#1528]) +- ⬆️ Update QDMI to `v1.3.0` ([#1652]) ([**@burgholzer**]) +- � Switch to component-based installation for the MQT Core Python package ([#1596]) ([**@burgholzer**]) +- ⬆️ Update QDMI to latest version from stable `v1.2.x` branch ([#1593]) ([**@burgholzer**]) +- ⬆️ Update `clang-tidy` to version 22 ([#1564]) ([**@denialhaag**], [**@burgholzer**]) +- � Build on `macos-26`/`macos-26-intel` by default and `macos-15`/`macos-15-intel` for extensive tests ([#1571]) ([**@denialhaag**]) ## [3.4.1] - 2026-02-01 @@ -42,14 +62,16 @@ This project adheres to [Semantic Versioning], with the exception that minor rel - ⬆️ Update `spdlog` to version 1.17.0 ([#1453]) ([**@burgholzer**]) - ♻️ Use `llc` instead of random `clang` for compiling QIR test circuits to improve robustness and handle opaque pointers correctly across LLVM versions ([#1447]) ([**@burgholzer**]) - ♻️ Extract singleton pattern into reusable template base class for QDMI devices and driver ([#1444]) ([**@ystade**], [**@burgholzer**]) -- 🚚 Reorganize QDMI code structure by moving devices into dedicated subdirectories and separating driver and common utilities ([#1444]) ([**@ystade**]) +- � Reorganize QDMI code structure by moving devices into dedicated subdirectories and separating driver and common utilities ([#1444]) ([**@ystade**]) ### Removed -- 🔥 No longer actively type check Python code with `mypy` and solely rely on `ty` ([#1437]) ([**@burgholzer**]) +- � No longer actively type check Python code with `mypy` and solely rely on `ty` ([#1437]) ([**@burgholzer**]) ## [3.4.0] - 2026-01-08 +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#340)._ + ### Added - ✨ Return device handle from `add_dynamic_device_library` for direct backend creation ([#1381]) ([**@marcelwa**]) @@ -65,44 +87,44 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Changed -- 📦🏁 Build Windows x86 wheels on `windows-2025` runner for newer compiler ([#1415]) ([**@burgholzer**]) -- 👷 Build on `macos-15`/`windows-2025` by default and `macos-14`/`windows-2022` for extensive tests ([#1414]) ([**@burgholzer**]) -- 📦🍎 Build macOS arm64 wheels on macos-15 runner for newer compiler ([#1413]) ([**@burgholzer**]) +- �� Build Windows x86 wheels on `windows-2025` runner for newer compiler ([#1415]) ([**@burgholzer**]) +- � Build on `macos-15`/`windows-2025` by default and `macos-14`/`windows-2022` for extensive tests ([#1414]) ([**@burgholzer**]) +- �� Build macOS arm64 wheels on macos-15 runner for newer compiler ([#1413]) ([**@burgholzer**]) - ⚡ Improve uv build caching by removing unconditional `reinstall-package` and configuring dedicated `cache-keys` ([#1412]) ([**@burgholzer**]) -- 👨‍💻📦 Build `spdlog` and QDMI generators as shared libraries in Python package builds ([#1411], [#1403]) ([**@burgholzer**]) -- ♻️🏁 Remove Windows-specific restrictions for dynamic QDMI device library handling ([#1406]) ([**@burgholzer**]) +- �‍�� Build `spdlog` and QDMI generators as shared libraries in Python package builds ([#1411], [#1403]) ([**@burgholzer**]) +- ♻️� Remove Windows-specific restrictions for dynamic QDMI device library handling ([#1406]) ([**@burgholzer**]) - ♻️ Migrate Python bindings from `pybind11` to `nanobind` ([#1383]) ([**@denialhaag**], [**@burgholzer**]) -- 📦️ Provide Stable ABI wheels for Python 3.12+ ([#1383]) ([**@burgholzer**], [**@denialhaag**]) -- 🚚 Create dedicated `mqt.core.na` submodule to closely follow the structure of other submodules ([#1383]) ([**@burgholzer**]) +- �️ Provide Stable ABI wheels for Python 3.12+ ([#1383]) ([**@burgholzer**], [**@denialhaag**]) +- � Create dedicated `mqt.core.na` submodule to closely follow the structure of other submodules ([#1383]) ([**@burgholzer**]) - ✨ Add common definitions and utilities for QDMI ([#1355]) ([**@burgholzer**]) -- 🚚 Move `NA` QDMI device in its right place next to other QDMI devices ([#1355]) ([**@burgholzer**]) +- � Move `NA` QDMI device in its right place next to other QDMI devices ([#1355]) ([**@burgholzer**]) - ♻️ Allow repeated loading of QDMI device library with potentially different session configurations ([#1355]) ([**@burgholzer**]) - ♻️ Enable thread-safe reference counting for QDMI devices singletons ([#1355]) ([**@burgholzer**]) - ♻️ Refactor `FoMaC` singleton to instantiable `Session` class with configurable authentication parameters ([#1355]) ([**@marcelwa**]) -- 👷 Stop testing on `ubuntu-22.04` and `ubuntu-22.04-arm` runners ([#1359]) ([**@denialhaag**], [**@burgholzer**]) -- 👷 Stop testing with `clang-19` and start testing with `clang-21` ([#1359]) ([**@denialhaag**], [**@burgholzer**]) -- 👷 Fix macOS tests with Homebrew Clang via new `munich-quantum-toolkit/workflows` version ([#1359]) ([**@denialhaag**], [**@burgholzer**]) -- 👷 Re-enable macOS tests with GCC by disabling module scanning ([#1359]) ([**@denialhaag**], [**@burgholzer**]) +- � Stop testing on `ubuntu-22.04` and `ubuntu-22.04-arm` runners ([#1359]) ([**@denialhaag**], [**@burgholzer**]) +- � Stop testing with `clang-19` and start testing with `clang-21` ([#1359]) ([**@denialhaag**], [**@burgholzer**]) +- � Fix macOS tests with Homebrew Clang via new `munich-quantum-toolkit/workflows` version ([#1359]) ([**@denialhaag**], [**@burgholzer**]) +- � Re-enable macOS tests with GCC by disabling module scanning ([#1359]) ([**@denialhaag**], [**@burgholzer**]) - ♻️ Group circuit operations into scheduling units for MLIR routing ([#1301]) ([**@MatthiasReumann**]) -- 👷 Use `munich-quantum-software/setup-mlir` to set up MLIR ([#1294]) ([**@denialhaag**]) +- � Use `munich-quantum-software/setup-mlir` to set up MLIR ([#1294]) ([**@denialhaag**]) - ♻️ Preserve tuple structure and improve site type clarity of the MQT NA Default QDMI Device ([#1299]) ([**@marcelwa**]) - ♻️ Move DD package evaluation module to standalone script ([#1327]) ([**@burgholzer**]) - ⬆️ Bump QDMI version to 1.2.0 ([#1243]) ([**@marcelwa**], [**@burgholzer**]) ### Fixed -- 🔧 Install all available QDMI device targets in Python package builds ([#1403]) ([**@burgholzer**]) -- 🐛 Fix operation validation in Qiskit backend to handle device-specific gate naming conventions ([#1384]) ([**@marcelwa**]) -- 🐛 Fix conditional branch handling when importing MLIR from `QuantumComputation` ([#1378]) ([**@lirem101**]) -- 🐛 Fix custom QDMI property and parameter handling in SC and NA devices ([#1355]) ([**@burgholzer**]) -- 🚨 Fix argument naming of `QuantumComputation` and `CompoundOperation` dunder methods for properly implementing the `MutableSequence` protocol ([#1338]) ([**@burgholzer**]) -- 🐛 Fix memory management in dynamic QDMI device by making it explicit ([#1336]) ([**@ystade**]) +- � Install all available QDMI device targets in Python package builds ([#1403]) ([**@burgholzer**]) +- � Fix operation validation in Qiskit backend to handle device-specific gate naming conventions ([#1384]) ([**@marcelwa**]) +- � Fix conditional branch handling when importing MLIR from `QuantumComputation` ([#1378]) ([**@lirem101**]) +- � Fix custom QDMI property and parameter handling in SC and NA devices ([#1355]) ([**@burgholzer**]) +- � Fix argument naming of `QuantumComputation` and `CompoundOperation` dunder methods for properly implementing the `MutableSequence` protocol ([#1338]) ([**@burgholzer**]) +- � Fix memory management in dynamic QDMI device by making it explicit ([#1336]) ([**@ystade**]) ### Removed -- 🔥 Remove wheel builds for Python 3.13t ([#1371]) ([**@burgholzer**]) -- 🔥 Remove the `evaluation` extra from the MQT Core Python package ([#1327]) ([**@burgholzer**]) -- 🔥 Remove the `mqt-core-dd-compare` entry point from the MQT Core Python package ([#1327]) ([**@burgholzer**]) +- � Remove wheel builds for Python 3.13t ([#1371]) ([**@burgholzer**]) +- � Remove the `evaluation` extra from the MQT Core Python package ([#1327]) ([**@burgholzer**]) +- � Remove the `mqt-core-dd-compare` entry point from the MQT Core Python package ([#1327]) ([**@burgholzer**]) ## [3.3.3] - 2025-11-10 @@ -112,7 +134,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Fixed -- 🐛 Revert change to `opTypeFromString()` signature made in [#1283] ([#1300]) ([**@denialhaag**]) +- � Revert change to `opTypeFromString()` signature made in [#1283] ([#1300]) ([**@denialhaag**]) ## [3.3.2] - 2025-11-04 @@ -126,14 +148,14 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Fixed -- 🐛 Fix edge-case in validation of `NAComputation` ([#1276]) ([**@ystade**]) -- 🐛 Allow integer QASM version declarations ([#1269]) ([**@denialhaag**]) +- � Fix edge-case in validation of `NAComputation` ([#1276]) ([**@ystade**]) +- � Allow integer QASM version declarations ([#1269]) ([**@denialhaag**]) ## [3.3.1] - 2025-10-14 ### Fixed -- 🐛 Ensure `spdlog` dependency can be found from `mqt-core` install ([#1263]) ([**@burgholzer**]) +- � Ensure `spdlog` dependency can be found from `mqt-core` install ([#1263]) ([**@burgholzer**]) ## [3.3.0] - 2025-10-13 @@ -141,7 +163,7 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#330)._ ### Added -- 👷 Enable testing on Python 3.14 ([#1246]) ([**@denialhaag**]) +- � Enable testing on Python 3.14 ([#1246]) ([**@denialhaag**]) - ✨ Add dedicated `PlacementPass` to MLIR transpilation routines ([#1232]) ([**@MatthiasReumann**]) - ✨ Add an NA-specific FoMaC implementation ([#1223], [#1236]) ([**@ystade**], [**@burgholzer**]) - ✨ Enable import of BarrierOp into MQTRef ([#1224]) ([**@denialhaag**]) @@ -149,14 +171,14 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#330)._ - ✨ Add QIR runtime using DD-based simulation ([#1210]) ([**@ystade**], [**@burgholzer**]) - ✨ Add SWAP reconstruction patterns to the newly-named `SwapReconstructionAndElision` MLIR pass ([#1207]) ([**@taminob**], [**@burgholzer**]) - ✨ Add two-way conversions between MQTRef and QIR ([#1091]) ([**@li-mingbao**]) -- 🚸 Define custom assembly formats for MLIR operations ([#1209]) ([**@denialhaag**]) +- � Define custom assembly formats for MLIR operations ([#1209]) ([**@denialhaag**]) - ✨ Add support for translating `IfElseOperation`s to the `MQTRef` MLIR dialect ([#1164]) ([**@denialhaag**], [**@burgholzer**]) - ✨ Add MQT's implementation of a generic FoMaC with Python bindings ([#1150], [#1186], [#1223]) ([**@ystade**]) - ✨ Add new MLIR pass `ElidePermutations` for SWAP gate elimination ([#1151]) ([**@taminob**]) - ✨ Add new pattern to MLIR pass `GateElimination` for identity gate removal ([#1140]) ([**@taminob**]) - ✨ Add Clifford block collection pass to `CircuitOptimizer` module ([#885]) ([**jannikpflieger**], [**@burgholzer**]) - ✨ Add `isControlled()` method to the `UnitaryInterface` MLIR class ([#1157]) ([**@taminob**], [**@burgholzer**]) -- 📝 Integrate generated MLIR documentation ([#1147]) ([**@denialhaag**], [**@burgholzer**]) +- � Integrate generated MLIR documentation ([#1147]) ([**@denialhaag**], [**@burgholzer**]) - ✨ Add `IfElseOperation` to C++ library and Python package to support Qiskit's `IfElseOp` ([#1117]) ([**@denialhaag**], [**@burgholzer**], [**@lavanya-m-k**]) - ✨ Add `allocQubit` and `deallocQubit` operations for dynamically working with single qubits to the MLIR dialects ([#1139]) ([**@DRovara**], [**@burgholzer**]) - ✨ Add `qubit` operation for static qubit addressing to the MLIR dialects ([#1098], [#1116]) ([**@MatthiasReumann**]) @@ -168,41 +190,43 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#330)._ ### Changed - ♻️ Replace custom `AllocOp`, `DeallocOp`, `ExtractOp`, and `InsertOp` with MLIR-native `memref` operations ([#1211]) ([**@denialhaag**]) -- 🚚 Rename MLIR pass `ElidePermutations` to `SwapReconstructionAndElision` ([#1207]) ([**@taminob**]) +- � Rename MLIR pass `ElidePermutations` to `SwapReconstructionAndElision` ([#1207]) ([**@taminob**]) - ⬆️ Require LLVM 21 for building the MLIR library ([#1180]) ([**@denialhaag**]) - ⬆️ Update to version 21 of `clang-tidy` ([#1180]) ([**@denialhaag**]) -- 🚚 Rename MLIR pass `CancelConsecutiveInverses` to `GateElimination` ([#1140]) ([**@taminob**]) -- 🚚 Rename `xxminusyy` to `xx_minus_yy` and `xxplusyy` to `xx_plus_yy` in MLIR dialects ([#1071]) ([**@BertiFlorea**], [**@denialhaag**]) -- 🚸 Add custom assembly format for operations in the MLIR dialects ([#1139]) ([**@burgholzer**]) -- 🚸 Enable `InferTypeOpInterface` in the MLIR dialects to reduce explicit type information ([#1139]) ([**@burgholzer**]) -- 🚚 Rename `check-quantum-opt` test target to `mqt-core-mlir-lit-test` ([#1139]) ([**@burgholzer**]) +- � Rename MLIR pass `CancelConsecutiveInverses` to `GateElimination` ([#1140]) ([**@taminob**]) +- � Rename `xxminusyy` to `xx_minus_yy` and `xxplusyy` to `xx_plus_yy` in MLIR dialects ([#1071]) ([**@BertiFlorea**], [**@denialhaag**]) +- � Add custom assembly format for operations in the MLIR dialects ([#1139]) ([**@burgholzer**]) +- � Enable `InferTypeOpInterface` in the MLIR dialects to reduce explicit type information ([#1139]) ([**@burgholzer**]) +- � Rename `check-quantum-opt` test target to `mqt-core-mlir-lit-test` ([#1139]) ([**@burgholzer**]) - ♻️ Update the `measure` operations in the MLIR dialects to no longer support more than one qubit being measured at once ([#1106]) ([**@DRovara**]) -- 🚚 Rename `XXminusYY` to `XXminusYYOp` and `XXplusYY` to `XXplusYYOp` in MLIR dialects ([#1099]) ([**@denialhaag**]) -- 🚚 Rename `MQTDyn` MLIR dialect to `MQTRef` ([#1098]) ([**@MatthiasReumann**]) +- � Rename `XXminusYY` to `XXminusYYOp` and `XXplusYY` to `XXplusYYOp` in MLIR dialects ([#1099]) ([**@denialhaag**]) +- � Rename `MQTDyn` MLIR dialect to `MQTRef` ([#1098]) ([**@MatthiasReumann**]) ### Removed -- 🔥 Drop support for Python 3.9 ([#1181]) ([**@denialhaag**]) -- 🔥 Remove `ClassicControlledOperation` from C++ library and Python package ([#1117]) ([**@denialhaag**]) +- � Drop support for Python 3.9 ([#1181]) ([**@denialhaag**]) +- � Remove `ClassicControlledOperation` from C++ library and Python package ([#1117]) ([**@denialhaag**]) ### Fixed -- 🐛 Fix CMake installation to make `find_package(mqt-core CONFIG)` succeed ([#1247]) ([**@burgholzer**], [**@denialhaag**]) -- 🏁 Fix stack overflows in OpenQASM layout parsing on Windows for large circuits ([#1235]) ([**@burgholzer**]) +- � Fix CMake installation to make `find_package(mqt-core CONFIG)` succeed ([#1247]) ([**@burgholzer**], [**@denialhaag**]) +- � Fix stack overflows in OpenQASM layout parsing on Windows for large circuits ([#1235]) ([**@burgholzer**]) - ✨ Add missing `StandardOperation` conversions in MLIR roundtrip pass ([#1071]) ([**@BertiFlorea**], [**@denialhaag**]) ## [3.2.1] - 2025-08-01 ### Fixed -- 🐛 Fix usage of `std::accumulate` by changing accumulator parameter from reference to value ([#1089]) ([**@denialhaag**]) -- 🐛 Fix erroneous `contains` check in DD package ([#1088]) ([**@denialhaag**]) +- � Fix usage of `std::accumulate` by changing accumulator parameter from reference to value ([#1089]) ([**@denialhaag**]) +- � Fix erroneous `contains` check in DD package ([#1088]) ([**@denialhaag**]) ## [3.2.0] - 2025-07-31 +_If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#320)._ + ### Added -- 🐍 Build Python 3.14 wheels ([#1076]) ([**@denialhaag**]) +- � Build Python 3.14 wheels ([#1076]) ([**@denialhaag**]) - ✨ Add MQT-internal MLIR dialect conversions ([#1001]) ([**@li-mingbao**]) ### Changed @@ -219,17 +243,17 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#310)._ - ✨ Add MLIR pass for merging rotation gates ([#1019]) ([**@denialhaag**]) - ✨ Add functions to generate random vector DDs ([#975]) ([**@MatthiasReumann**]) - ✨ Add function to approximate decision diagrams ([#908]) ([**@MatthiasReumann**]) -- 📦 Add Windows ARM64 wheels ([#926]) ([**@burgholzer**]) -- 📝 Add documentation page for MLIR ([#931]) ([**@ystade**]) +- � Add Windows ARM64 wheels ([#926]) ([**@burgholzer**]) +- � Add documentation page for MLIR ([#931]) ([**@ystade**]) - ✨ Initial implementation of the mqtdyn Dialect ([#900]) ([**@DRovara**], [**@ystade**]) ### Fixed -- 🐛 Fix bug in MLIR roundtrip passes caused by accessing an invalidated iterator after erasure in a loop ([#932]) ([**@flowerthrower**]) -- 🐛 Add missing support for `sxdg` gates in Qiskit circuit import ([#930]) ([**@burgholzer**]) -- 🐛 Fix bug related to initialization of operations with duplicate operands ([#964]) ([**@ystade**]) -- 🐛 Open issue for Qiskit upstream test only when the test is actually failing not when it was cancelled ([#973]) ([**@ystade**]) -- 🐛 Fix parsing of `GPhase` in the `MQTOpt` MLIR dialect ([#1042]) ([**@ystade**], [**@DRovara**]) +- � Fix bug in MLIR roundtrip passes caused by accessing an invalidated iterator after erasure in a loop ([#932]) ([**@flowerthrower**]) +- � Add missing support for `sxdg` gates in Qiskit circuit import ([#930]) ([**@burgholzer**]) +- � Fix bug related to initialization of operations with duplicate operands ([#964]) ([**@ystade**]) +- � Open issue for Qiskit upstream test only when the test is actually failing not when it was cancelled ([#973]) ([**@ystade**]) +- � Fix parsing of `GPhase` in the `MQTOpt` MLIR dialect ([#1042]) ([**@ystade**], [**@DRovara**]) ### Changed @@ -248,18 +272,18 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#310)._ ### Added -- 📝 Add JOSS journal reference and citation information ([#913]) ([**@burgholzer**]) -- 📝 Add new links to Python package metadata ([#911]) ([**@burgholzer**]) +- � Add JOSS journal reference and citation information ([#913]) ([**@burgholzer**]) +- � Add new links to Python package metadata ([#911]) ([**@burgholzer**]) ### Fixed -- 📝 Fix old links in Python package metadata ([#911]) ([**@burgholzer**]) +- � Fix old links in Python package metadata ([#911]) ([**@burgholzer**]) ## [3.0.1] - 2025-04-07 ### Fixed -- 🐛 Fix doxygen build on RtD to include C++ API docs ([#912]) ([**@burgholzer**]) +- � Fix doxygen build on RtD to include C++ API docs ([#912]) ([**@burgholzer**]) ## [3.0.0] - 2025-04-06 @@ -273,15 +297,15 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#300)._ - ✨ Support for Qiskit 2.0+ ([#860]) ([**@burgholzer**]) - ✨ Add initial infrastructure for MLIR within the MQT ([#878], [#879], [#892], [#893], [#895]) ([**@burgholzer**], [**@ystade**], [**@DRovara**], [**@flowerthrower**], [**@BertiFlorea**]) - ✨ Add State Preparation Algorithm ([#543]) ([**@M-J-Hochreiter**]) -- 🚸 Add support for indexed identifiers to OpenQASM 3 parser ([#832]) ([**@burgholzer**]) -- 🚸 Allow indexed registers as operation arguments ([#839]) ([**@burgholzer**]) -- 📝 Add documentation for the DD package ([#831]) ([**@burgholzer**]) -- 📝 Add documentation for the ZX package ([#817]) ([**@pehamTom**]) -- 📝 Add C++ API docs setup ([#817]) ([**@pehamTom**], [**@burgholzer**]) +- � Add support for indexed identifiers to OpenQASM 3 parser ([#832]) ([**@burgholzer**]) +- � Allow indexed registers as operation arguments ([#839]) ([**@burgholzer**]) +- � Add documentation for the DD package ([#831]) ([**@burgholzer**]) +- � Add documentation for the ZX package ([#817]) ([**@pehamTom**]) +- � Add C++ API docs setup ([#817]) ([**@pehamTom**], [**@burgholzer**]) ### Changed -- **Breaking**: 🚚 MQT Core has moved to the [munich-quantum-toolkit] GitHub organization +- **Breaking**: � MQT Core has moved to the [munich-quantum-toolkit] GitHub organization - **Breaking**: ✨ Adopt [PEP 735] dependency groups ([#762]) ([**@burgholzer**]) - **Breaking**: ♻️ Encapsulate the OpenQASM parser in its own library ([#822]) ([**@burgholzer**]) - **Breaking**: ♻️ Replace `Config` template from DD package with constructor argument ([#886]) ([**@burgholzer**]) @@ -292,33 +316,35 @@ _If you are upgrading: please see [`UPGRADING.md`](UPGRADING.md#300)._ - **Breaking**: ♻️ Refactor `NAComputation` class hierarchy ([#846], [#877]) ([**@ystade**]) - **Breaking**: ⬆️ Bump minimum required CMake version to `3.24.0` ([#879]) ([**@burgholzer**]) - **Breaking**: ⬆️ Bump minimum required `uv` version to `0.5.20` ([#802]) ([**@burgholzer**]) -- 📝 Rework existing project documentation ([#789], [#842]) ([**@burgholzer**]) -- 📄 Use [PEP 639] license expressions ([#847]) ([**@burgholzer**]) +- � Rework existing project documentation ([#789], [#842]) ([**@burgholzer**]) +- � Use [PEP 639] license expressions ([#847]) ([**@burgholzer**]) ### Removed -- **Breaking**: 🔥 Remove the `Teleportation` gate from the IR ([#882]) ([**@burgholzer**]) -- **Breaking**: 🔥 Remove parsers for `.real`, `.qc`, `.tfc`, and `GRCS` files ([#822]) ([**@burgholzer**]) -- **Breaking**: 🔥 Remove tensor dump functionality ([#798]) ([**@burgholzer**]) -- **Breaking**: 🔥 Remove `extract_probability_vector` functionality ([#883]) ([**@burgholzer**]) +- **Breaking**: � Remove the `Teleportation` gate from the IR ([#882]) ([**@burgholzer**]) +- **Breaking**: � Remove parsers for `.real`, `.qc`, `.tfc`, and `GRCS` files ([#822]) ([**@burgholzer**]) +- **Breaking**: � Remove tensor dump functionality ([#798]) ([**@burgholzer**]) +- **Breaking**: � Remove `extract_probability_vector` functionality ([#883]) ([**@burgholzer**]) ### Fixed -- 🐛 Fix Qiskit layout import and handling ([#849], [#858]) ([**@burgholzer**]) -- 🐛 Properly handle timing literals in QASM parser ([#724]) ([**@burgholzer**]) -- 🐛 Fix stripping of idle qubits ([#763]) ([**@burgholzer**]) -- 🐛 Fix permutation handling in OpenQASM dump ([#810]) ([**@burgholzer**]) -- 🐛 Fix out-of-bounds error in ZX `EdgeIterator` ([#758]) ([**@burgholzer**]) -- 🐛 Fix endianness in DCX and XX_minus_YY gate matrix definition ([#741]) ([**@burgholzer**]) -- 🐛 Fix needless dummy register in empty circuit construction ([#758]) ([**@burgholzer**]) +- � Fix Qiskit layout import and handling ([#849], [#858]) ([**@burgholzer**]) +- � Properly handle timing literals in QASM parser ([#724]) ([**@burgholzer**]) +- � Fix stripping of idle qubits ([#763]) ([**@burgholzer**]) +- � Fix permutation handling in OpenQASM dump ([#810]) ([**@burgholzer**]) +- � Fix out-of-bounds error in ZX `EdgeIterator` ([#758]) ([**@burgholzer**]) +- � Fix endianness in DCX and XX_minus_YY gate matrix definition ([#741]) ([**@burgholzer**]) +- � Fix needless dummy register in empty circuit construction ([#758]) ([**@burgholzer**]) ## [2.7.0] - 2024-10-08 -_📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-toolkit/core/releases) for previous changelogs._ +_� Refer to the [GitHub Release Notes](https://github.com/munich-quantum-toolkit/core/releases) for previous changelogs._ -[unreleased]: https://github.com/munich-quantum-toolkit/core/compare/v3.4.1...HEAD +[unreleased]: https://github.com/munich-quantum-toolkit/core/compare/v3.5.1...HEAD +[3.5.1]: https://github.com/munich-quantum-toolkit/core/releases/tag/v3.5.1 +[3.5.0]: https://github.com/munich-quantum-toolkit/core/releases/tag/v3.5.0 [3.4.1]: https://github.com/munich-quantum-toolkit/core/releases/tag/v3.4.1 [3.4.0]: https://github.com/munich-quantum-toolkit/core/releases/tag/v3.4.0 [3.3.3]: https://github.com/munich-quantum-toolkit/core/releases/tag/v3.3.3 @@ -335,6 +361,11 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool +[#1675]: https://github.com/munich-quantum-toolkit/core/pull/1675 +[#1673]: https://github.com/munich-quantum-toolkit/core/pull/1673 +[#1664]: https://github.com/munich-quantum-toolkit/core/pull/1664 +[#1662]: https://github.com/munich-quantum-toolkit/core/pull/1662 +[#1652]: https://github.com/munich-quantum-toolkit/core/pull/1652 [#1637]: https://github.com/munich-quantum-toolkit/core/pull/1637 [#1635]: https://github.com/munich-quantum-toolkit/core/pull/1635 [#1627]: https://github.com/munich-quantum-toolkit/core/pull/1627 @@ -365,6 +396,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [#1547]: https://github.com/munich-quantum-toolkit/core/pull/1547 [#1542]: https://github.com/munich-quantum-toolkit/core/pull/1542 [#1537]: https://github.com/munich-quantum-toolkit/core/pull/1537 +[#1528]: https://github.com/munich-quantum-toolkit/core/pull/1528 [#1521]: https://github.com/munich-quantum-toolkit/core/pull/1521 [#1513]: https://github.com/munich-quantum-toolkit/core/pull/1513 [#1510]: https://github.com/munich-quantum-toolkit/core/pull/1510 @@ -395,6 +427,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [#1413]: https://github.com/munich-quantum-toolkit/core/pull/1413 [#1412]: https://github.com/munich-quantum-toolkit/core/pull/1412 [#1411]: https://github.com/munich-quantum-toolkit/core/pull/1411 +[#1407]: https://github.com/munich-quantum-toolkit/core/pull/1407 [#1406]: https://github.com/munich-quantum-toolkit/core/pull/1406 [#1403]: https://github.com/munich-quantum-toolkit/core/pull/1403 [#1402]: https://github.com/munich-quantum-toolkit/core/pull/1402 @@ -403,6 +436,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [#1383]: https://github.com/munich-quantum-toolkit/core/pull/1383 [#1382]: https://github.com/munich-quantum-toolkit/core/pull/1382 [#1381]: https://github.com/munich-quantum-toolkit/core/pull/1381 +[#1380]: https://github.com/munich-quantum-toolkit/core/pull/1380 [#1378]: https://github.com/munich-quantum-toolkit/core/pull/1378 [#1375]: https://github.com/munich-quantum-toolkit/core/pull/1375 [#1371]: https://github.com/munich-quantum-toolkit/core/pull/1371 @@ -551,6 +585,8 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [**@lirem101**]: https://github.com/lirem101 [**@Ectras**]: https://github.com/Ectras [**@simon1hofmann**]: https://github.com/simon1hofmann +[**@keefehuang**]: https://github.com/keefehuang +[**@J4MMlE**]: https://github.com/J4MMlE diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE diff --git a/UPGRADING.md b/UPGRADING.md index cb92968af9..b1182b0b35 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -37,8 +37,30 @@ The `datastructures` (sub)library has been removed from the MQT Core repository. Its functionality has only ever been used in [MQT QMAP] since its inception. As a consequence, the code shall be moved to [MQT QMAP] once QMAP adopts an MQT Core version that includes this change. +## [3.5.1] + +No breaking changes. + +### Component-based CMake installs + +Fixed exported `nlohmann_json` CMake metadata so `find_package(mqt-core CONFIG)` no longer propagates an invalid `.../COMPONENT` include directory in component-based installations. +Anyone relying on an installed version of `mqt-core` shall update from `3.5.0` to `3.5.1`. + +## [3.5.0] + +The shared library ABI version (`SOVERSION`) is increased from `3.4` to `3.5`. +Thus, consuming libraries need to update their wheel repair configuration for `cibuildwheel` to ensure the `mqt-core` libraries are properly skipped in the wheel repair step. + +### `nanobind` updated to version 2.12.0 + +This release updates the `nanobind` dependency to version 2.12.0, which includes an ABI bump. +Any existing code that uses the `mqt-core` Python bindings will need to be recompiled with the new `nanobind` version. + ## [3.4.0] +The shared library ABI version (`SOVERSION`) is increased from `3.3` to `3.4`. +Thus, consuming libraries need to update their wheel repair configuration for `cibuildwheel` to ensure the `mqt-core` libraries are properly skipped in the wheel repair step. + ### Python wheels This release contains two changes to the distributed wheels. @@ -217,7 +239,9 @@ It also requires the `uv` library version 0.5.20 or higher. -[unreleased]: https://github.com/munich-quantum-toolkit/core/compare/v3.4.0...HEAD +[unreleased]: https://github.com/munich-quantum-toolkit/core/compare/v3.5.1...HEAD +[3.5.1]: https://github.com/munich-quantum-toolkit/core/compare/v3.5.0...v3.5.1 +[3.5.0]: https://github.com/munich-quantum-toolkit/core/compare/v3.4.0...v3.5.0 [3.4.0]: https://github.com/munich-quantum-toolkit/core/compare/v3.3.0...v3.4.0 [3.3.0]: https://github.com/munich-quantum-toolkit/core/compare/v3.2.0...v3.3.0 [3.2.0]: https://github.com/munich-quantum-toolkit/core/compare/v3.1.0...v3.2.0 diff --git a/cmake/ExternalDependencies.cmake b/cmake/ExternalDependencies.cmake index 2d17ff04d5..f1d2077336 100644 --- a/cmake/ExternalDependencies.cmake +++ b/cmake/ExternalDependencies.cmake @@ -98,13 +98,13 @@ if(BUILD_MQT_CORE_TESTS) endif() # cmake-format: off -set(QDMI_VERSION 1.2.2 +set(QDMI_VERSION 1.3.0 CACHE STRING "QDMI version") -set(QDMI_REV "5bcf32f57158beea34d2839a41d218ed46a41516" # v1.2.x +set(QDMI_REV "0f7e08c58b72800d1022a01cfb618af67b9a9c30" # v1.3.0 CACHE STRING "QDMI identifier (tag, branch or commit hash)") set(QDMI_REPO_OWNER "Munich-Quantum-Software-Stack" CACHE STRING "QDMI repository owner (change when using a fork)") -cmake_dependent_option(QDMI_INSTALL "Install QDMI library" ON "MQT_CORE_INSTALL" OFF) +cmake_dependent_option(INSTALL_QDMI "Install QDMI library" ON "MQT_CORE_INSTALL" OFF) # cmake-format: on FetchContent_Declare( qdmi @@ -191,12 +191,7 @@ if(MQT_CORE_JSON_INSTALL AND TARGET nlohmann_json) DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT ${MQT_CORE_TARGET_NAME}_Development) - install( - TARGETS nlohmann_json - EXPORT ${MQT_CORE_JSON_TARGETS_EXPORT_NAME} - INCLUDES - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - COMPONENT ${MQT_CORE_TARGET_NAME}_Development) + install(TARGETS nlohmann_json EXPORT ${MQT_CORE_JSON_TARGETS_EXPORT_NAME}) install( EXPORT ${MQT_CORE_JSON_TARGETS_EXPORT_NAME} diff --git a/docs/qdmi/qdmi_backend.md b/docs/qdmi/qdmi_backend.md index c50110ae89..3b89fac043 100644 --- a/docs/qdmi/qdmi_backend.md +++ b/docs/qdmi/qdmi_backend.md @@ -93,9 +93,7 @@ print(f"Qubits: {backend.target.num_qubits}") ```python # Filter backends by name substring -filtered_qdmi = provider.backends( - name="QDMI" -) # Matches all backends with "QDMI" in name +filtered_qdmi = provider.backends(name="QDMI") # Matches all backends with "QDMI" in name filtered_ddsim = provider.backends(name="DDSIM") # Matches "MQT Core DDSIM QDMI Device" # Filter by full name also works @@ -175,9 +173,7 @@ Associate your session with a specific project or organization: ```python # Specify a project ID -provider = QDMIProvider( - token="your_api_token", project_id="quantum-research-project-2024" -) +provider = QDMIProvider(token="your_api_token", project_id="quantum-research-project-2024") ``` ### Combining Authentication Parameters diff --git a/include/mqt-core/zx/FunctionalityConstruction.hpp b/include/mqt-core/zx/FunctionalityConstruction.hpp index b22c3e12e5..ab4a9e6f14 100644 --- a/include/mqt-core/zx/FunctionalityConstruction.hpp +++ b/include/mqt-core/zx/FunctionalityConstruction.hpp @@ -85,8 +85,14 @@ class FunctionalityConstruction { EdgeType type = EdgeType::Simple); static void addCphase(ZXDiagram& diag, const PiExpression& phase, Qubit ctrl, Qubit target, std::vector& qubits); - static void addSwap(ZXDiagram& diag, Qubit target, Qubit target2, + static void addMcphase(ZXDiagram& diag, const PiExpression& phase, + const std::vector& controls, Qubit target, + std::vector& qubits); + static void addSwap(ZXDiagram& diag, Qubit target1, Qubit target2, std::vector& qubits); + static void addMcswap(ZXDiagram& diag, const std::vector& controls, + Qubit target1, Qubit target2, + std::vector& qubits); static void addRzz(ZXDiagram& diag, const PiExpression& phase, Qubit target, Qubit target2, std::vector& qubits, @@ -99,6 +105,11 @@ class FunctionalityConstruction { addRzx(ZXDiagram& diag, const PiExpression& phase, Qubit target, Qubit target2, std::vector& qubits, const std::optional& unconvertedPhase = std::nullopt); + static void + addMcrzz(ZXDiagram& diag, const PiExpression& phase, + const std::vector& controls, Qubit target, Qubit target2, + std::vector& qubits, + const std::optional& unconvertedPhase = std::nullopt); static void addDcx(ZXDiagram& diag, Qubit qubit1, Qubit qubit2, std::vector& qubits); static void @@ -113,6 +124,22 @@ class FunctionalityConstruction { const std::optional& unconvertedBeta = std::nullopt); static void addCcx(ZXDiagram& diag, Qubit ctrl0, Qubit ctrl1, Qubit target, std::vector& qubits); + static void addCcz(ZXDiagram& diag, Qubit ctrl0, Qubit ctrl1, Qubit target, + std::vector& qubits); + static void addCrx(ZXDiagram& diag, const PiExpression& phase, Qubit control, + Qubit target, std::vector& qubits); + static void addMcrx(ZXDiagram& diag, const PiExpression& phase, + const std::vector& controls, Qubit target, + std::vector& qubits); + static void addCrz(ZXDiagram& diag, const PiExpression& phase, Qubit control, + Qubit target, std::vector& qubits); + static void addMcrz(ZXDiagram& diag, const PiExpression& phase, + std::vector controls, Qubit target, + std::vector& qubits); + static void addMcx(ZXDiagram& diag, std::vector controls, Qubit target, + std::vector& qubits); + static void addMcz(ZXDiagram& diag, const std::vector& controls, + Qubit target, std::vector& qubits); static op_it parseOp(ZXDiagram& diag, op_it it, op_it end, std::vector& qubits, const qc::Permutation& p); static op_it parseCompoundOp(ZXDiagram& diag, op_it it, op_it end, diff --git a/mlir/include/mlir/Compiler/CompilerPipeline.h b/mlir/include/mlir/Compiler/CompilerPipeline.h index f43bac99d7..d15585827f 100644 --- a/mlir/include/mlir/Compiler/CompilerPipeline.h +++ b/mlir/include/mlir/Compiler/CompilerPipeline.h @@ -40,6 +40,9 @@ struct QuantumCompilerConfig { /// Print IR after each stage bool printIRAfterAllStages = false; + + /// Disable quaternion-based single-qubit rotation gate merging + bool disableMergeSingleQubitRotationGates = false; }; /** diff --git a/mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h b/mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h index 65d07ebe73..1cb1ad0394 100644 --- a/mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h +++ b/mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h @@ -10,22 +10,24 @@ #pragma once -#include -#include -#include #include -#include -#include #include #include -#include #include #include #include #include -namespace mlir::qc { +namespace mlir { + +// Forward declarations +class MLIRContext; +class ModuleOp; +class Operation; +class ValueRange; + +namespace qc { /** * @brief Builder API for constructing quantum programs in the QC dialect @@ -113,12 +115,7 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder { * @param index The index of the qubit to access * @return The specified qubit value */ - Value operator[](size_t index) const { - if (index >= qubits.size()) { - llvm::report_fatal_error("Qubit index out of bounds"); - } - return qubits[index]; - } + Value operator[](size_t index) const; /** * @brief Conversion to the backing MemRef value @@ -201,16 +198,7 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder { * @param index The index of the bit to access (must be less than size) * @return A Bit structure representing the specified bit */ - Bit operator[](const int64_t index) const { - if (index < 0 || index >= size) { - const std::string msg = "Bit index " + std::to_string(index) + - " out of bounds for register '" + name + - "' of size " + std::to_string(size); - llvm::reportFatalUsageError(msg.c_str()); - } - return { - .registerName = name, .registerSize = size, .registerIndex = index}; - } + Bit operator[](const int64_t index) const; }; /** @@ -891,8 +879,7 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder { * } : !qc.qubit * ``` */ - QCProgramBuilder& ctrl(ValueRange controls, - const llvm::function_ref& body); + QCProgramBuilder& ctrl(ValueRange controls, const function_ref& body); /** * @brief Apply an inverse (i.e., adjoint) operation. @@ -911,7 +898,7 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder { * } * ``` */ - QCProgramBuilder& inv(const llvm::function_ref& body); + QCProgramBuilder& inv(const function_ref& body); //===--------------------------------------------------------------------===// // Deallocation @@ -965,19 +952,19 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder { */ static OwningOpRef build(MLIRContext* context, - const llvm::function_ref& buildFunc); + const function_ref& buildFunc); private: enum class AllocationMode : uint8_t { Unset, Static, Dynamic }; MLIRContext* ctx{}; - ModuleOp module; + Operation* module; /// Track allocated qubits for automatic deallocation - llvm::DenseSet allocatedQubits; + DenseSet allocatedQubits; /// Track allocated MemRefs for automatic deallocation - llvm::DenseSet allocatedMemrefs; + DenseSet allocatedMemrefs; /// Check if the builder has been finalized void checkFinalized() const; @@ -988,4 +975,5 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder { /// Ensure static and dynamic qubit allocation modes are not mixed. void ensureAllocationMode(AllocationMode requestedMode); }; -} // namespace mlir::qc +} // namespace qc +} // namespace mlir diff --git a/mlir/include/mlir/Dialect/QC/IR/QCDialect.h b/mlir/include/mlir/Dialect/QC/IR/QCDialect.h index 69d11e8471..96fbb7f724 100644 --- a/mlir/include/mlir/Dialect/QC/IR/QCDialect.h +++ b/mlir/include/mlir/Dialect/QC/IR/QCDialect.h @@ -10,7 +10,6 @@ #pragma once -#include #include #define DIALECT_NAME_QC "qc" diff --git a/mlir/include/mlir/Dialect/QC/IR/QCInterfaces.h b/mlir/include/mlir/Dialect/QC/IR/QCInterfaces.h index fce8cd8dc3..94421a6fed 100644 --- a/mlir/include/mlir/Dialect/QC/IR/QCInterfaces.h +++ b/mlir/include/mlir/Dialect/QC/IR/QCInterfaces.h @@ -10,9 +10,7 @@ #pragma once -#include -#include -#include +#include #include diff --git a/mlir/include/mlir/Dialect/QC/IR/QCOps.td b/mlir/include/mlir/Dialect/QC/IR/QCOps.td index 2b15a305ed..8e76c9c3ba 100644 --- a/mlir/include/mlir/Dialect/QC/IR/QCOps.td +++ b/mlir/include/mlir/Dialect/QC/IR/QCOps.td @@ -960,7 +960,7 @@ def CtrlOp }]; let builders = [OpBuilder<(ins "ValueRange":$controls, - "const llvm::function_ref&":$bodyBuilder)>]; + "const function_ref&":$bodyBuilder)>]; let hasCanonicalizer = 1; let hasVerifier = 1; @@ -1002,8 +1002,7 @@ def InvOp : QCOp<"inv", static StringRef getBaseSymbol() { return "inv"; } }]; - let builders = [OpBuilder<(ins - "const llvm::function_ref&":$bodyBuilder)>]; + let builders = [OpBuilder<(ins "const function_ref&":$bodyBuilder)>]; let hasCanonicalizer = 1; let hasVerifier = 1; diff --git a/mlir/include/mlir/Dialect/QC/Transforms/Passes.h b/mlir/include/mlir/Dialect/QC/Transforms/Passes.h index 435be783d7..18c6a600e2 100644 --- a/mlir/include/mlir/Dialect/QC/Transforms/Passes.h +++ b/mlir/include/mlir/Dialect/QC/Transforms/Passes.h @@ -10,8 +10,6 @@ #pragma once -#include "mlir/Dialect/QC/IR/QCDialect.h" - #include #include diff --git a/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h b/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h index 757ffec61e..94b1f85eda 100644 --- a/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h +++ b/mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h @@ -10,16 +10,9 @@ #pragma once -#include -#include -#include -#include #include -#include -#include #include #include -#include #include #include @@ -27,7 +20,15 @@ #include #include -namespace mlir::qco { +namespace mlir { + +// Forward declarations +class MLIRContext; +class ModuleOp; +class Operation; +class ValueRange; + +namespace qco { /** * @brief Builder API for constructing quantum programs in the QCO dialect @@ -122,12 +123,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { * @param index The index of the qubit to access * @return The specified qubit value */ - Value& operator[](size_t index) { - if (index >= qubits.size()) { - llvm::reportFatalUsageError("Qubit index out of bounds"); - } - return qubits[index]; - } + Value& operator[](size_t index); /** * @brief Conversion to the backing QTensor value @@ -210,16 +206,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { * @param index The index of the bit to access (must be less than size) * @return A Bit structure representing the specified bit */ - Bit operator[](const int64_t index) const { - if (index < 0 || index >= size) { - const std::string msg = "Bit index " + std::to_string(index) + - " out of bounds for register '" + name + - "' of size " + std::to_string(size); - llvm::reportFatalUsageError(msg.c_str()); - } - return { - .registerName = name, .registerSize = size, .registerIndex = index}; - } + Bit operator[](int64_t index) const; }; /** @@ -1171,7 +1158,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { */ std::pair ctrl(ValueRange controls, ValueRange targets, - llvm::function_ref(ValueRange)> body); + function_ref(ValueRange)> body); /** * @brief Apply an inverse operation @@ -1196,7 +1183,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { * ``` */ ValueRange inv(ValueRange qubits, - llvm::function_ref(ValueRange)> body); + function_ref(ValueRange)> body); //===--------------------------------------------------------------------===// // Deallocation @@ -1263,8 +1250,8 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { */ ValueRange qcoIf(const std::variant& condition, ValueRange qubits, - llvm::function_ref(ValueRange)> thenBody, - llvm::function_ref(ValueRange)> elseBody = nullptr); + function_ref(ValueRange)> thenBody, + function_ref(ValueRange)> elseBody = nullptr); //===--------------------------------------------------------------------===// // Finalization @@ -1294,13 +1281,13 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { */ static OwningOpRef build(MLIRContext* context, - const llvm::function_ref& buildFunc); + const function_ref& buildFunc); private: enum class AllocationMode : uint8_t { Unset, Static, Dynamic }; MLIRContext* ctx{}; - ModuleOp module; + Operation* module; /// Check if the builder has been finalized void checkFinalized() const; @@ -1340,7 +1327,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { /// Only values present in this map are valid for use in operations. /// When an operation consumes a qubit and produces a new one, the old value /// is removed and the new output is added. - llvm::DenseMap validQubits; + DenseMap validQubits; /** * @brief Validate that a tensor value is valid and unconsumed. This also @@ -1370,7 +1357,7 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { /// Only values present in this map are valid for use in operations. /// When an operation consumes a tensor and produces a new one, the old value /// is removed and the new output is added. - llvm::DenseMap validTensors; + DenseMap validTensors; /// Track whether static or dynamic qubit allocation is used. AllocationMode allocationMode = AllocationMode::Unset; @@ -1378,4 +1365,5 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder { /// Ensure static and dynamic qubit allocation modes are not mixed. void ensureAllocationMode(AllocationMode requestedMode); }; -} // namespace mlir::qco +} // namespace qco +} // namespace mlir diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h index a5286162b7..60632679a0 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCODialect.h @@ -10,8 +10,6 @@ #pragma once -#include -#include #include #define DIALECT_NAME_QCO "qco" diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.h b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.h index df69bd2082..035b0b5268 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.h +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOInterfaces.h @@ -11,11 +11,9 @@ #pragma once #include -#include #include #include -#include -#include +#include #include #include diff --git a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td index 20c809aaf7..c96dedf718 100644 --- a/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td +++ b/mlir/include/mlir/Dialect/QCO/IR/QCOOps.td @@ -1132,7 +1132,7 @@ def CtrlOp build($_builder, $_state, controls.getTypes(), targets.getTypes(), controls, targets); }]>, OpBuilder<(ins "ValueRange":$controls, "ValueRange":$targets, - "llvm::function_ref(ValueRange)" + "function_ref(ValueRange)" ">":$bodyBuilder)>]; let hasCanonicalizer = 1; @@ -1202,7 +1202,7 @@ def InvOp build($_builder, $_state, qubits.getTypes(), qubits); }]>, OpBuilder<(ins "ValueRange":$qubits, - "llvm::function_ref(ValueRange)" + "function_ref(ValueRange)" ">":$bodyBuilder)>]; let hasCanonicalizer = 1; @@ -1256,15 +1256,14 @@ def IfOp `{` type($results) `}` }]; - let builders = - [OpBuilder<(ins "Value":$condition, "ValueRange":$qubits), [{ + let builders = [OpBuilder<(ins "Value":$condition, "ValueRange":$qubits), [{ build($_builder, $_state, qubits.getTypes(), condition, qubits); }]>, - OpBuilder<(ins "Value":$condition, "ValueRange":$qubits, - "llvm::function_ref(ValueRange)" - ">":$thenBuilder, - CArg<"llvm::function_ref(ValueRange)>", - "nullptr">:$elseBuilder)>]; + OpBuilder<(ins "Value":$condition, "ValueRange":$qubits, + "function_ref(ValueRange)" + ">":$thenBuilder, + CArg<"function_ref(ValueRange)>", + "nullptr">:$elseBuilder)>]; let extraClassDeclaration = [{ Block *thenBlock() { diff --git a/mlir/include/mlir/Dialect/QCO/QCOUtils.h b/mlir/include/mlir/Dialect/QCO/QCOUtils.h index f888509129..489fceb00e 100644 --- a/mlir/include/mlir/Dialect/QCO/QCOUtils.h +++ b/mlir/include/mlir/Dialect/QCO/QCOUtils.h @@ -10,10 +10,9 @@ #pragma once -#include "mlir/Dialect/Utils/Utils.h" - #include #include +#include namespace mlir::qco { @@ -27,11 +26,10 @@ namespace mlir::qco { * @return LogicalResult Success or failure of the removal. */ template -mlir::LogicalResult +LogicalResult removeInversePairOneTargetZeroParameter(OpType op, PatternRewriter& rewriter) { // Check if the successor is the inverse operation - auto nextOp = - llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } @@ -52,11 +50,10 @@ removeInversePairOneTargetZeroParameter(OpType op, PatternRewriter& rewriter) { * @return LogicalResult Success or failure of the removal. */ template -mlir::LogicalResult +LogicalResult removeInversePairTwoTargetZeroParameter(OpType op, PatternRewriter& rewriter) { // Check if the successor is the inverse operation - auto nextOp = - llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } @@ -82,11 +79,11 @@ removeInversePairTwoTargetZeroParameter(OpType op, PatternRewriter& rewriter) { * @return LogicalResult Success or failure of the removal. */ template -mlir::LogicalResult +LogicalResult removeTwoTargetZeroParameterPairWithSwappedTargets(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation - auto nextOp = llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } @@ -118,10 +115,10 @@ removeTwoTargetZeroParameterPairWithSwappedTargets(OpType op, * @return LogicalResult Success or failure of the merge. */ template -mlir::LogicalResult mergeOneTargetZeroParameter(OpType op, - PatternRewriter& rewriter) { +LogicalResult mergeOneTargetZeroParameter(OpType op, + PatternRewriter& rewriter) { // Check if the successor is the same operation - auto nextOp = llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } @@ -148,10 +145,9 @@ mlir::LogicalResult mergeOneTargetZeroParameter(OpType op, * @return LogicalResult Success or failure of the merge. */ template -mlir::LogicalResult mergeOneTargetOneParameter(OpType op, - PatternRewriter& rewriter) { +LogicalResult mergeOneTargetOneParameter(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation - auto nextOp = llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } @@ -179,10 +175,9 @@ mlir::LogicalResult mergeOneTargetOneParameter(OpType op, * @return LogicalResult Success or failure of the merge. */ template -mlir::LogicalResult mergeTwoTargetOneParameter(OpType op, - PatternRewriter& rewriter) { +LogicalResult mergeTwoTargetOneParameter(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation - auto nextOp = llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } @@ -217,11 +212,11 @@ mlir::LogicalResult mergeTwoTargetOneParameter(OpType op, * @return LogicalResult Success or failure of the merge. */ template -mlir::LogicalResult +LogicalResult mergeTwoTargetOneParameterWithSwappedTargets(OpType op, PatternRewriter& rewriter) { // Check if the successor is the same operation - auto nextOp = llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Mapping/Architecture.h b/mlir/include/mlir/Dialect/QCO/Transforms/Mapping/Architecture.h index ca32d645c4..d0207b6921 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Mapping/Architecture.h +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Mapping/Architecture.h @@ -12,8 +12,6 @@ #include #include -#include -#include #include #include @@ -27,15 +25,15 @@ namespace mlir::qco { */ class [[nodiscard]] Architecture { public: - using CouplingSet = mlir::DenseSet>; - using NeighbourVector = mlir::SmallVector>; + using CouplingSet = DenseSet>; + using NeighbourVector = SmallVector>; explicit Architecture(std::string name, std::size_t nqubits, CouplingSet couplingSet) : name_(std::move(name)), nqubits_(nqubits), couplingSet_(std::move(couplingSet)), neighbours_(nqubits), - dist_(nqubits, mlir::SmallVector(nqubits, UINT64_MAX)), - prev_(nqubits, mlir::SmallVector(nqubits, UINT64_MAX)) { + dist_(nqubits, SmallVector(nqubits, UINT64_MAX)), + prev_(nqubits, SmallVector(nqubits, UINT64_MAX)) { floydWarshallWithPathReconstruction(); collectNeighbours(); } @@ -63,8 +61,7 @@ class [[nodiscard]] Architecture { /** * @brief Collect all neighbours of @p u. */ - [[nodiscard]] mlir::SmallVector - neighboursOf(std::size_t u) const; + [[nodiscard]] SmallVector neighboursOf(std::size_t u) const; /** * @brief Return the maximum degree (connectivity) of any qubit in the @@ -73,7 +70,7 @@ class [[nodiscard]] Architecture { [[nodiscard]] std::size_t maxDegree() const; private: - using Matrix = mlir::SmallVector, 0>; + using Matrix = SmallVector, 0>; /** * @brief Find all shortest paths in the coupling map between two qubits. diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.h b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.h index c3589793e6..bcaaeb1fe1 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.h +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.h @@ -10,9 +10,6 @@ #pragma once -#include "mlir/Dialect/QCO/IR/QCODialect.h" - -#include #include #include diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index 5d37e25612..276d0ba294 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -11,6 +11,35 @@ include "mlir/Pass/PassBase.td" +def MergeSingleQubitRotationGates + : Pass<"merge-single-qubit-rotation-gates", "mlir::ModuleOp"> { + let dependentDialects = ["mlir::qco::QCODialect", + "::mlir::arith::ArithDialect", + "::mlir::math::MathDialect"]; + let summary = "Merge rotation gates using quaternion-based fusion"; + let description = [{ + Merges consecutive single-qubit rotation gates acting on the same qubit into a single equivalent U gate, reducing circuit depth and gate count. + + Supported gate types: `rx`, `ry`, `rz`, `p`, `r`, `u2`, `u`. + + The pass greedily collects the longest possible chain of consecutive mergeable gates. + Each gate is converted to a unit quaternion: + + - `rx`, `ry`, `rz`, `p`: single-axis rotations via half-angle formulas. + - `r(theta, phi)`: rotation by `theta` around axis `(cos(phi), sin(phi), 0)`. + - `u2(phi, lambda) = u(pi / 2, phi, lambda)`. + - `u(theta, phi, lambda)`: ZYZ decomposition `rz(phi) * ry(theta) * rz(lambda)`, each factor converted to a quaternion and merged via the Hamilton product. + + The gates are then folded one by one via the Hamilton product into a single quaternion, which is decomposed back into ZYZ Euler angles and emitted as a single `UOp`, representing the same rotation as the chain of single gates. + The global phase of each gate is tracked alongside and combined together. + + The emitted `UOp` is defined by $U = \exp [i (\phi + \lambda) / 2] R_z (\phi) R_y (\theta) R_z (\lambda)$. + Each merge emits a `GPhaseOp` carrying the accumulated input phase of the chain. + Because the synthesized `UOp` introduces an additional intrinsic phase of $(\phi + \lambda) / 2$, the `GPhaseOp` must compensate for it. + This applies even to chains composed entirely of $\mathrm{SU} (2)$ gates (`rx`, `ry`, `rz`, `r`) because the synthesis into a `UOp` still produces the intrinsic phase term. + }]; +} + //===----------------------------------------------------------------------===// // Transpilation Passes //===----------------------------------------------------------------------===// @@ -72,4 +101,12 @@ def MappingPass : Pass<"place-and-route", "mlir::ModuleOp"> { "The number of inserted SWAPs">]; } +def SwapAbsorb : Pass<"absorb-swaps", "mlir::ModuleOp"> { + let dependentDialects = ["mlir::qco::QCODialect"]; + let summary = ""; + let options = []; + let statistics = [Statistic<"numSwaps", "num-inserted-swaps", + "The number of absorbed SWAPs">]; +} + #endif // MLIR_DIALECT_QCO_TRANSFORMS_PASSES_TD diff --git a/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h b/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h index 1a901882dc..0f56355ec6 100644 --- a/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h +++ b/mlir/include/mlir/Dialect/QCO/Utils/Drivers.h @@ -11,86 +11,57 @@ #pragma once #include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Utils/Qubits.h" +#include "mlir/Dialect/QCO/Utils/WireIterator.h" +#include "mlir/Dialect/QTensor/IR/QTensorOps.h" #include #include #include +#include #include #include +#include +#include +#include #include namespace mlir::qco { -class Qubits { - /** - * @brief Specifies the qubit "location" (hardware or program). - */ - enum class QubitLocation : std::uint8_t { Hardware, Program }; - -public: - /** - * @brief Add qubit with automatically assigned dynamic index. - */ - [[maybe_unused]] void add(TypedValue q); - - /** - * @brief Add qubit with static index. - */ - void add(TypedValue q, std::size_t hw); - - /** - * @brief Remap the qubit value from prev to next. - */ - void remap(TypedValue prev, TypedValue next); - - /** - * @brief Remove the qubit value. - */ - void remove(TypedValue q); - - /** - * @returns the qubit value assigned to a program index. - */ - [[maybe_unused]] TypedValue getProgramQubit(std::size_t index); - - /** - * @returns the qubit value assigned to a hardware index. - */ - TypedValue getHardwareQubit(std::size_t index); - -private: - DenseMap> programToValue_; - DenseMap> hardwareToValue_; - DenseMap, std::pair> - valueToIndex_; -}; + +using WalkProgramFn = function_ref; /** * @brief Perform top-down non-recursive walk of all operations within a - * region and apply callback function. + * region of a quantum program and apply a callback function. * @details The signature of the callback function is: * * (Operation*, Qubits& q) -> WalkResult * * where the Qubits object tracks the front of qubit SSA values. + * Depending on the template parameter, the callback is executed before or after + * updating the Qubits state. * @param region The targeted region. * @param fn The callback function. + * @returns success(), if all operations have been visited. */ -template void walkUnit(Region& region, Fn&& fn) { - const auto ffn = std::forward(fn); - +template +LogicalResult walkProgram(Region& region, const WalkProgramFn& fn) { Qubits qubits; for (Operation& curr : region.getOps()) { - if (ffn(&curr, qubits).wasInterrupted()) { - break; - }; + if constexpr (Order == WalkOrder::PreOrder) { + if (fn(&curr, qubits).wasInterrupted()) { + return failure(); + } + } TypeSwitch(&curr) .template Case( [&](StaticOp op) { qubits.add(op.getQubit(), op.getIndex()); }) .template Case([&](AllocOp op) { qubits.add(op.getResult()); }) - .template Case([&](UnitaryOpInterface op) { + .template Case([&](UnitaryOpInterface& op) { for (const auto& [prevV, nextV] : llvm::zip(op.getInputQubits(), op.getOutputQubits())) { const auto prevQ = cast>(prevV); @@ -106,6 +77,157 @@ template void walkUnit(Region& region, Fn&& fn) { }) .template Case( [&](SinkOp op) { qubits.remove(op.getQubit()); }); + + if constexpr (Order == WalkOrder::PostOrder) { + if (fn(&curr, qubits).wasInterrupted()) { + return failure(); + } + } } + + return success(); +} + +using ReleasedOps = SmallVector; +using PendingWiresMap = + DenseMap>; + +struct IsReady { + bool operator()(PendingWiresMap::value_type& kv) const { + return kv.second.size() == kv.first.getNumQubits(); + } +}; + +using ReadyRange = + decltype(make_filter_range(std::declval(), IsReady{})); + +using WalkProgramGraphFn = + function_ref; + +/** + * @brief Walk the graph-like circuit IR of QCO dialect programs. + * @details + * Depending on the template parameter, the function collects the + * layers in forward or backward direction, respectively. Towards that end, + * the function traverses the def-use chain of each qubit until a multi-qubit + * gate (including barriers) is found. If each input qubit of a multi-qubit gate + * is visited, it is considered ready. This process is repeated until no more + * multi-qubit gates are found anymore. + * + * The signature of the callback function is: + * + * (const ReadyRange& ready, ReleasedOps& released) -> WalkResult + * + * The operations inserted into the parameter "released" determine which + * multi-qubit gates are released in next iteration. + * If the callback returns WalkResult::skip(), all ready operations will be + * released. + * + * @param wires A mutable array-ref of circuit wires (wire iterators). + * @param fn The callback function. + * + * @returns success(), if all operations have been visited. + */ +template +LogicalResult walkProgramGraph(MutableArrayRef wires, + WalkProgramGraphFn fn) { + using Traits = WireTraversalTraits; + + ReleasedOps released; + + PendingWiresMap pending; + pending.reserve(wires.size()); + + SmallVector curr(wires.size()); + std::iota(curr.begin(), curr.end(), 0UL); + + SmallVector next; + next.reserve(wires.size()); + + while (!curr.empty()) { + for (std::size_t i : curr) { + auto& it = wires[i]; + while (Traits::isActive(it)) { + const auto res = + TypeSwitch(it.operation()) + .template Case([&](UnitaryOpInterface& op) { + // If there are fewer wires than the qubit requires inputs, + // it's impossible to release the operation. Hence, fail. + if (op.getNumQubits() > wires.size()) { + return WalkResult::interrupt(); + } + + if (op.getNumQubits() == 1) { + std::ranges::advance(it, Traits::stride()); + return WalkResult::advance(); + } + + // Insert the unitary to the pending map. + // The caller decides if this op should be released. + const auto [mapIt, inserted] = pending.try_emplace(op); + auto& indices = mapIt->second; + + if (inserted) { + indices.reserve(op.getNumQubits()); + } + + indices.emplace_back(i); + + return WalkResult::skip(); // Stop at multi-qubit gate. + }) + // AllocOp, StaticOp, and qtensor::ExtractOp are only reachable + // on the forward path; backward isActive() halts before + // reaching them (decrementing at a source op is a no-op). + .template Case([&](auto) { + std::ranges::advance(it, Traits::stride()); + return WalkResult::advance(); + }) + .Default([&](Operation* op) -> WalkResult { + const auto name = op->getName().getStringRef(); + report_fatal_error("unknown op encountered: " + name); + }); + + if (res.wasSkipped()) { + break; + } + + if (res.wasInterrupted()) { + return failure(); + } + } + } + + released.clear(); + const auto ready = make_filter_range(pending, IsReady{}); + const auto res = std::invoke(fn, ready, released); + if (res.wasInterrupted()) { + return failure(); + } + + if (res.wasSkipped()) { + released.clear(); + for (const auto& [op, _] : ready) { + released.emplace_back(op); + } + } + + for (const UnitaryOpInterface& op : released) { + const auto mapIt = pending.find(op); + assert(mapIt != pending.end()); + + for (std::size_t i : mapIt->second) { + std::ranges::advance(wires[i], Traits::stride()); + next.emplace_back(i); + } + + pending.erase(mapIt); + } + + curr.swap(next); + next.clear(); + } + + return success(); } -} // namespace mlir::qco +} // namespace mlir::qco \ No newline at end of file diff --git a/mlir/include/mlir/Dialect/QCO/Utils/Qubits.h b/mlir/include/mlir/Dialect/QCO/Utils/Qubits.h new file mode 100644 index 0000000000..40cbeb1f68 --- /dev/null +++ b/mlir/include/mlir/Dialect/QCO/Utils/Qubits.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include "mlir/Dialect/QCO/IR/QCODialect.h" + +#include +#include + +#include +#include +#include + +namespace mlir::qco { +class Qubits { + /** + * @brief Specifies the qubit "location" (hardware or program). + */ + enum class QubitLocation : std::uint8_t { Hardware, Program }; + +public: + /** + * @brief Add qubit with automatically assigned dynamic index. + */ + void add(TypedValue q); + + /** + * @brief Add qubit with static index. + */ + void add(TypedValue q, std::size_t hw); + + /** + * @brief Remap the qubit value from prev to next. + */ + void remap(TypedValue prev, TypedValue next); + + /** + * @brief Remove the qubit value. + */ + void remove(TypedValue q); + + /** + * @returns the qubit value assigned to a program index. + */ + [[nodiscard]] TypedValue getProgramQubit(std::size_t index) const; + + /** + * @returns the qubit value assigned to a hardware index. + */ + [[nodiscard]] TypedValue getHardwareQubit(std::size_t index) const; + +private: + DenseMap> programToValue_; + DenseMap> hardwareToValue_; + DenseMap, std::pair> + valueToIndex_; +}; +} // namespace mlir::qco diff --git a/mlir/include/mlir/Dialect/QCO/Utils/WireIterator.h b/mlir/include/mlir/Dialect/QCO/Utils/WireIterator.h index 7ff92276fb..70ba4b26ff 100644 --- a/mlir/include/mlir/Dialect/QCO/Utils/WireIterator.h +++ b/mlir/include/mlir/Dialect/QCO/Utils/WireIterator.h @@ -10,8 +10,12 @@ #pragma once +#include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QTensor/IR/QTensorOps.h" + #include +#include #include namespace mlir::qco { @@ -26,20 +30,20 @@ class [[nodiscard]] WireIterator { public: using iterator_category = std::bidirectional_iterator_tag; using difference_type = std::ptrdiff_t; - using value_type = mlir::Operation*; + using value_type = Operation*; WireIterator() : op_(nullptr), qubit_(nullptr), isSentinel_(false) {} - explicit WireIterator(mlir::Value qubit) + explicit WireIterator(Value qubit) : op_(qubit.getDefiningOp()), qubit_(qubit), isSentinel_(false) {} /// @returns the operation the iterator points to. - [[nodiscard]] mlir::Operation* operation() const { return op_; } + [[nodiscard]] Operation* operation() const { return op_; } /// @returns the operation the iterator points to. - [[nodiscard]] mlir::Operation* operator*() const { return operation(); } + [[nodiscard]] Operation* operator*() const { return operation(); } /// @returns the qubit the iterator points to. - [[nodiscard]] mlir::Value qubit() const; + [[nodiscard]] Value qubit() const; WireIterator& operator++() { forward(); @@ -79,8 +83,37 @@ class [[nodiscard]] WireIterator { /// @brief Move to the previous operation on the qubit wire. void backward(); - mlir::Operation* op_; - mlir::Value qubit_; + Operation* op_; + Value qubit_; bool isSentinel_; }; + +/** + * @brief Categorizes the current traversal direction. + */ +enum class WireDirection : std::uint8_t { Forward, Backward }; + +template struct WireTraversalTraits {}; + +template <> struct WireTraversalTraits { + /// @returns the forward increment stride size. + static constexpr std::ptrdiff_t stride() { return 1; } + + /// @returns true if the wire iterator can continue forward. + static bool isActive(const WireIterator& it) { + return it != std::default_sentinel; + } +}; + +template <> struct WireTraversalTraits { + /// @returns the backward increment stride size. + static constexpr std::ptrdiff_t stride() { return -1; } + + /// @returns true if the wire iterator can continue backward. + static bool isActive(const WireIterator& it) { + return it.operation() == nullptr + ? false + : !isa(it.operation()); + } +}; } // namespace mlir::qco diff --git a/mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h b/mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h index 5121cfd06d..97095e47ac 100644 --- a/mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h +++ b/mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h @@ -12,22 +12,14 @@ #include "mlir/Dialect/QIR/Utils/QIRMetadata.h" -#include -#include -#include -#include #include -#include #include -#include #include #include #include -#include #include #include -#include -#include +#include #include #include @@ -35,11 +27,14 @@ #include namespace mlir { +// Forward declarations class Block; +class MLIRContext; +class ModuleOp; class Operation; -} // namespace mlir +class ValueRange; -namespace mlir::qir { +namespace qir { /** * @brief Builder API for constructing QIR (Quantum Intermediate @@ -196,7 +191,7 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { * %q2 = llvm.load %ptr2 : !llvm.ptr -> !llvm.ptr * ``` */ - llvm::SmallVector allocQubitRegister(int64_t size); + SmallVector allocQubitRegister(int64_t size); /** * @brief A small structure representing a single classical bit within a @@ -204,7 +199,7 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { */ struct Bit { /// Name of the register containing this bit - llvm::StringRef registerName; + StringRef registerName; /// Size of the register containing this bit int64_t registerSize{}; /// Index of this bit within the register @@ -225,16 +220,7 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { * @param index The index of the bit to access (must be less than size) * @return A Bit structure representing the specified bit */ - Bit operator[](const int64_t index) const { - if (index < 0 || index >= size) { - const std::string msg = "Bit index " + std::to_string(index) + - " out of bounds for register '" + name + - "' of size " + std::to_string(size); - llvm::reportFatalUsageError(msg.c_str()); - } - return { - .registerName = name, .registerSize = size, .registerIndex = index}; - } + Bit operator[](const int64_t index) const; }; /** @@ -889,13 +875,13 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { */ static OwningOpRef build(MLIRContext* context, - const llvm::function_ref& buildFunc); + const function_ref& buildFunc); private: enum class AllocationMode : uint8_t { Unset, Static, Dynamic }; /// The main module - ModuleOp module; + Operation* module{}; /// The main function Operation* mainFunc{}; @@ -917,22 +903,22 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { Value exitCode; /// Cache static qubit pointers for reuse - llvm::DenseMap staticQubits; + DenseMap staticQubits; /// Set of qubit pointers - llvm::DenseSet qubits; + DenseSet qubits; /// Set of qubit-array pointers - llvm::DenseSet qubitArrays; + DenseSet qubitArrays; /// Map from register name to result-array pointer llvm::StringMap resultArrays; /// Map from (register name, index) to loaded result - llvm::DenseMap, Value> loadedResults; + DenseMap, Value> loadedResults; /// Map from result index to result pointer for non-register results - llvm::DenseMap resultPtrs; + DenseMap resultPtrs; /// Track qubit and result counts for QIR metadata QIRMetadata metadata_; @@ -951,10 +937,9 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { * @param targets Target qubits * @param fnName Name of the QIR function to call */ - void - createCallOp(const llvm::SmallVector>& parameters, - ValueRange controls, const SmallVector& targets, - llvm::StringRef fnName); + void createCallOp(const SmallVector>& parameters, + ValueRange controls, const SmallVector& targets, + StringRef fnName); /** * @brief Generate array-based output recording in the output block @@ -977,4 +962,5 @@ class QIRProgramBuilder final : public ImplicitLocOpBuilder { void ensureAllocationMode(AllocationMode requestedMode); }; -} // namespace mlir::qir +} // namespace qir +} // namespace mlir diff --git a/mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h b/mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h index 6a28995c83..d594e3df7a 100644 --- a/mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h +++ b/mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h @@ -12,7 +12,6 @@ #include "mlir/Dialect/QIR/Utils/QIRMetadata.h" -#include #include #include #include diff --git a/mlir/include/mlir/Dialect/QTensor/IR/QTensorOps.td b/mlir/include/mlir/Dialect/QTensor/IR/QTensorOps.td index 1ac3aa1885..7a0e26ea27 100644 --- a/mlir/include/mlir/Dialect/QTensor/IR/QTensorOps.td +++ b/mlir/include/mlir/Dialect/QTensor/IR/QTensorOps.td @@ -86,8 +86,8 @@ def FromElementsOp TypesMatchWith< "operand types match result element type", "result", "elements", "SmallVector(" - "::llvm::cast($_self).getNumElements(), " - "::llvm::cast($_self).getElementType())">]> { + "cast($_self).getNumElements(), " + "cast($_self).getElementType())">]> { let summary = "Create a tensor from a range of values"; let description = [{ The `qtensor.from_elements` operation is a wrapper operation of the `tensor.from_elements` operation. @@ -110,14 +110,13 @@ def FromElementsOp } def ExtractOp - : QTensorOp< - "extract", - [Pure, - TypesMatchWith<"result type matches element type of tensor", - "tensor", "result", - "::llvm::cast($_self).getElementType()">, - TypesMatchWith<"returned tensor type matches input tensor", "tensor", - "out_tensor", "$_self">]> { + : QTensorOp<"extract", + [Pure, + TypesMatchWith<"result type matches element type of tensor", + "tensor", "result", + "cast($_self).getElementType()">, + TypesMatchWith<"returned tensor type matches input tensor", + "tensor", "out_tensor", "$_self">]> { let summary = "Extract element from tensor"; let description = [{ The `qtensor.extract` operation is the modified version of the standard `tensor.extract` @@ -138,14 +137,13 @@ def ExtractOp } def InsertOp - : QTensorOp< - "insert", - [Pure, - TypesMatchWith<"result type matches type of dest", "dest", "result", - "$_self">, - TypesMatchWith< - "scalar type matches element type of dest", "dest", "scalar", - "::llvm::cast($_self).getElementType()">]> { + : QTensorOp<"insert", [Pure, + TypesMatchWith<"result type matches type of dest", + "dest", "result", "$_self">, + TypesMatchWith< + "scalar type matches element type of dest", + "dest", "scalar", + "cast($_self).getElementType()">]> { let summary = "Insert element into tensor"; let description = [{ The `qtensor.insert` operation is a wrapper operation for the `tensor.insert` operation of the tensor dialect. diff --git a/mlir/include/mlir/Dialect/QTensor/IR/QTensorUtils.h b/mlir/include/mlir/Dialect/QTensor/IR/QTensorUtils.h index 594be34481..71a321e9fc 100644 --- a/mlir/include/mlir/Dialect/QTensor/IR/QTensorUtils.h +++ b/mlir/include/mlir/Dialect/QTensor/IR/QTensorUtils.h @@ -12,9 +12,9 @@ #include "mlir/Dialect/QTensor/IR/QTensorOps.h" -#include #include #include +#include namespace mlir::qtensor { @@ -40,17 +40,17 @@ inline bool areEquivalentIndices(Value lhs, Value rhs) { * @brief Tensor-transforming ops in a scalar extract/insert chain. */ inline bool isTensorChainOp(Operation* op) { - return llvm::isa(op); + return isa(op); } /** * @brief Returns the tensor input of a tensor-transforming op. */ inline Value getTensorChainInput(Operation* op) { - if (auto insertOp = llvm::dyn_cast(op)) { + if (auto insertOp = dyn_cast(op)) { return insertOp.getDest(); } - if (auto extractOp = llvm::dyn_cast(op)) { + if (auto extractOp = dyn_cast(op)) { return extractOp.getTensor(); } return nullptr; @@ -60,10 +60,10 @@ inline Value getTensorChainInput(Operation* op) { * @brief Returns the tensor output of a tensor-transforming op. */ inline Value getTensorChainOutput(Operation* op) { - if (auto insertOp = llvm::dyn_cast(op)) { + if (auto insertOp = dyn_cast(op)) { return insertOp.getResult(); } - if (auto extractOp = llvm::dyn_cast(op)) { + if (auto extractOp = dyn_cast(op)) { return extractOp.getOutTensor(); } return nullptr; @@ -73,11 +73,11 @@ inline Value getTensorChainOutput(Operation* op) { * @brief Rewire the tensor input of a tensor-transforming op. */ inline void setTensorChainInput(Operation* op, Value tensor) { - if (llvm::isa(op)) { + if (isa(op)) { op->setOperand(1, tensor); return; } - if (llvm::isa(op)) { + if (isa(op)) { op->setOperand(0, tensor); } } diff --git a/mlir/include/mlir/Dialect/QTensor/Transforms/Passes.h b/mlir/include/mlir/Dialect/QTensor/Transforms/Passes.h index e33924b5a5..8e53bea5a6 100644 --- a/mlir/include/mlir/Dialect/QTensor/Transforms/Passes.h +++ b/mlir/include/mlir/Dialect/QTensor/Transforms/Passes.h @@ -10,8 +10,6 @@ #pragma once -#include "mlir/Dialect/QTensor/IR/QTensorDialect.h" - #include #include diff --git a/mlir/include/mlir/Support/Passes.h b/mlir/include/mlir/Support/Passes.h index f780be87f9..9a7ec7a2e8 100644 --- a/mlir/include/mlir/Support/Passes.h +++ b/mlir/include/mlir/Support/Passes.h @@ -10,7 +10,7 @@ #pragma once -#include "mlir/Support/LogicalResult.h" +#include namespace mlir { class ModuleOp; diff --git a/mlir/include/mlir/Support/PrettyPrinting.h b/mlir/include/mlir/Support/PrettyPrinting.h index 9dd1fd4fca..06cc0f783b 100644 --- a/mlir/include/mlir/Support/PrettyPrinting.h +++ b/mlir/include/mlir/Support/PrettyPrinting.h @@ -10,10 +10,8 @@ #pragma once -#include -#include -#include #include +#include namespace mlir { @@ -29,7 +27,7 @@ class ModuleOp; * @param str The string to measure * @return The display width in columns */ -int calculateDisplayWidth(llvm::StringRef str); +int calculateDisplayWidth(StringRef str); /** * @brief Wrap a long line into multiple lines that fit within the box @@ -45,30 +43,29 @@ int calculateDisplayWidth(llvm::StringRef str); * @param indent Number of spaces to indent wrapped lines * @param result Output vector to store wrapped lines */ -void wrapLine(llvm::StringRef line, int maxWidth, - llvm::SmallVectorImpl>& result, - int indent = 0); +void wrapLine(StringRef line, int maxWidth, + SmallVectorImpl>& result, int indent = 0); /** * @brief Print top border of a box * * @param os Output stream to write to */ -void printBoxTop(llvm::raw_ostream& os = llvm::errs()); +void printBoxTop(raw_ostream& os = llvm::errs()); /** * @brief Print middle separator of a box * * @param os Output stream to write to */ -void printBoxMiddle(llvm::raw_ostream& os = llvm::errs()); +void printBoxMiddle(raw_ostream& os = llvm::errs()); /** * @brief Print bottom border of a box * * @param os Output stream to write to */ -void printBoxBottom(llvm::raw_ostream& os = llvm::errs()); +void printBoxBottom(raw_ostream& os = llvm::errs()); /** * @brief Print a box line with text and proper padding @@ -80,8 +77,8 @@ void printBoxBottom(llvm::raw_ostream& os = llvm::errs()); * @param indent Number of spaces to indent the text (0 for left-aligned) * @param os Output stream to write to */ -void printBoxLine(llvm::StringRef text, int indent = 0, - llvm::raw_ostream& os = llvm::errs()); +void printBoxLine(StringRef text, int indent = 0, + raw_ostream& os = llvm::errs()); /** * @brief Print multiple lines of text within the box, with line wrapping @@ -94,8 +91,8 @@ void printBoxLine(llvm::StringRef text, int indent = 0, * @param indent Number of spaces to indent the text * @param os Output stream to write to */ -void printBoxText(llvm::StringRef text, int indent = 0, - llvm::raw_ostream& os = llvm::errs()); +void printBoxText(StringRef text, int indent = 0, + raw_ostream& os = llvm::errs()); /** * @brief Pretty print an MLIR module with a header and box formatting @@ -104,7 +101,7 @@ void printBoxText(llvm::StringRef text, int indent = 0, * @param header Optional header text to display above the module * @param os Output stream to write to */ -void printProgram(ModuleOp module, llvm::StringRef header = "", - llvm::raw_ostream& os = llvm::errs()); +void printProgram(ModuleOp module, StringRef header = "", + raw_ostream& os = llvm::errs()); } // namespace mlir diff --git a/mlir/lib/Compiler/CMakeLists.txt b/mlir/lib/Compiler/CMakeLists.txt index 66afb3515a..1dc45532fa 100644 --- a/mlir/lib/Compiler/CMakeLists.txt +++ b/mlir/lib/Compiler/CMakeLists.txt @@ -20,6 +20,7 @@ add_mlir_library( MLIRQCToQCO MLIRQCOToQC MLIRQCToQIR + MLIRQCOTransforms MQT::MLIRSupport) mqt_mlir_target_use_project_options(MQTCompilerPipeline) diff --git a/mlir/lib/Compiler/CompilerPipeline.cpp b/mlir/lib/Compiler/CompilerPipeline.cpp index 0bab524e53..06eb87bd89 100644 --- a/mlir/lib/Compiler/CompilerPipeline.cpp +++ b/mlir/lib/Compiler/CompilerPipeline.cpp @@ -13,14 +13,14 @@ #include "mlir/Conversion/QCOToQC/QCOToQC.h" #include "mlir/Conversion/QCToQCO/QCToQCO.h" #include "mlir/Conversion/QCToQIR/QCToQIR.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Support/Passes.h" #include "mlir/Support/PrettyPrinting.h" -#include #include #include #include -#include +#include #include @@ -34,7 +34,7 @@ namespace mlir { * @param stageNumber Current stage number * @param totalStages Total number of stages (for progress indication) */ -static void prettyPrintStage(ModuleOp module, const llvm::StringRef stageName, +static void prettyPrintStage(ModuleOp module, const StringRef stageName, const int stageNumber, const int totalStages) { llvm::errs() << "\n"; @@ -136,9 +136,11 @@ QuantumCompilerPipeline::runPipeline(ModuleOp module, } } // Stage 5: Optimization passes - // TODO: Add optimization passes - if (failed( - runStage([&](PassManager& pm) { populateQCOCleanupPipeline(pm); }))) { + if (failed(runStage([&](PassManager& pm) { + if (!config_.disableMergeSingleQubitRotationGates) { + pm.addPass(qco::createMergeSingleQubitRotationGates()); + } + }))) { return failure(); } if (record != nullptr && config_.recordIntermediates) { diff --git a/mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp b/mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp index 425f2a8c35..7cb25e3bbd 100644 --- a/mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp +++ b/mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp @@ -19,8 +19,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -30,9 +29,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -62,10 +59,10 @@ using namespace qco; * returns its results */ template -static void createModified( - JeffOpType& op, ConversionPatternRewriter& rewriter, ValueRange controls, - ValueRange targets, - llvm::function_ref(ValueRange)> lambda) { +static void +createModified(JeffOpType& op, ConversionPatternRewriter& rewriter, + ValueRange controls, ValueRange targets, + function_ref(ValueRange)> lambda) { auto loc = op.getLoc(); if (op.getNumCtrls() != 0) { CtrlOp ctrlOp; @@ -74,12 +71,12 @@ static void createModified( } else { ctrlOp = CtrlOp::create( rewriter, loc, controls, targets, - [&](ValueRange ctrlTargets) -> llvm::SmallVector { + [&](ValueRange ctrlTargets) -> SmallVector { auto invOp = InvOp::create(rewriter, loc, ctrlTargets, lambda); return invOp.getQubitsOut(); }); } - llvm::SmallVector results; + SmallVector results; llvm::append_range(results, ctrlOp.getTargetsOut()); llvm::append_range(results, ctrlOp.getControlsOut()); rewriter.replaceOp(op, results); @@ -122,7 +119,7 @@ createGateFromJeff(JeffOpType& op, ConversionPatternRewriter& rewriter, return success(); } - auto lambda = [&](ValueRange innerTargets) -> llvm::SmallVector { + auto lambda = [&](ValueRange innerTargets) -> SmallVector { auto qcoOp = QCOOpType::create(rewriter, op.getLoc(), innerTargets[TargetIndices]..., parameters[ParamIndices]...); @@ -166,7 +163,7 @@ static void createBarrierOp(jeff::CustomOp& op, jeff::CustomOpAdaptor& adaptor, if (op.getNumCtrls() == 0 && !op.getIsAdjoint()) { rewriter.replaceOpWithNewOp(op, targets); } else { - auto lambda = [&](ValueRange innerTargets) -> llvm::SmallVector { + auto lambda = [&](ValueRange innerTargets) -> SmallVector { auto qcoOp = BarrierOp::create(rewriter, op.getLoc(), innerTargets); return qcoOp.getQubitsOut(); }; @@ -177,8 +174,8 @@ static void createBarrierOp(jeff::CustomOp& op, jeff::CustomOpAdaptor& adaptor, /** * @brief Gets the name of the entry point from the module attributes */ -static llvm::StringRef getEntryPointName(Operation* op) { - auto module = llvm::dyn_cast(op); +static StringRef getEntryPointName(Operation* op) { + auto module = dyn_cast(op); if (!module) { llvm::reportFatalInternalError("Expected a module operation"); } @@ -188,20 +185,20 @@ static llvm::StringRef getEntryPointName(Operation* op) { llvm::reportFatalInternalError( "Module is missing 'jeff.entrypoint' attribute"); } - auto entryPoint = llvm::cast(entryPointAttr).getUInt(); + auto entryPoint = cast(entryPointAttr).getUInt(); auto stringsAttr = module->getAttr("jeff.strings"); if (!stringsAttr) { llvm::reportFatalInternalError( "Module is missing 'jeff.strings' attribute"); } - auto strings = llvm::cast(stringsAttr); + auto strings = cast(stringsAttr); if (entryPoint >= strings.size()) { llvm::reportFatalInternalError("Entry point index is out of bounds"); } - return llvm::cast(strings[entryPoint]).getValue(); + return cast(strings[entryPoint]).getValue(); } /** @@ -211,7 +208,7 @@ static llvm::StringRef getEntryPointName(Operation* op) { * @return LogicalResult Success or failure of the cleanup */ static LogicalResult cleanUp(Operation* op) { - auto module = llvm::dyn_cast(op); + auto module = dyn_cast(op); if (!module) { return failure(); } @@ -250,8 +247,8 @@ struct ConvertJeffQuregAllocOpToQCO final matchAndRewrite(jeff::QuregAllocOp op, OpAdaptor adaptor, ConversionPatternRewriter& rewriter) const override { auto sizeValue = getConstantIntValue(adaptor.getNumQubits()); - auto tensorType = llvm::cast( - getTypeConverter()->convertType(op.getType())); + auto tensorType = + cast(getTypeConverter()->convertType(op.getType())); Value size; if (sizeValue.has_value()) { size = arith::ConstantOp::create(rewriter, op.getLoc(), @@ -523,7 +520,7 @@ struct ConvertJeffGPhaseOpToQCO final : OpConversionPattern { if (op.getNumCtrls() == 0 && !op.getIsAdjoint()) { rewriter.replaceOpWithNewOp(op, op.getRotation()); } else { - auto lambda = [&](ValueRange /*targets*/) -> llvm::SmallVector { + auto lambda = [&](ValueRange /*targets*/) -> SmallVector { GPhaseOp::create(rewriter, op.getLoc(), op.getRotation()); return {}; }; @@ -849,7 +846,7 @@ struct ConvertJeffMainToQCO final : OpConversionPattern { auto* block = &op.getBlocks().front(); auto* returnOp = block->getTerminator(); - if (!llvm::isa(returnOp)) { + if (!isa(returnOp)) { return failure(); } diff --git a/mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp b/mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp index dedf1f051d..dcb112804a 100644 --- a/mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp +++ b/mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -29,11 +29,10 @@ #include #include #include -#include #include -#include #include #include +#include #include #include #include @@ -73,15 +72,15 @@ struct LoweringState { bool inInvOp = false; CtrlOp ctrlOp; InvOp invOp; - llvm::SmallVector controlsIn; - llvm::SmallVector controlsOut; - llvm::SmallVector targetsIn; - llvm::SmallVector targetsOut; + SmallVector controlsIn; + SmallVector controlsOut; + SmallVector targetsIn; + SmallVector targetsOut; [[nodiscard]] bool inModifier() const { return inCtrlOp || inInvOp; } // Module information - llvm::SmallVector strings; + SmallVector strings; std::string entryPointName; /// The qubit allocation mode used in the module @@ -258,7 +257,7 @@ static void createCustomOp(QCOOpType& op, ConversionPatternRewriter& rewriter, template static void createPPROp(QCOOpType& op, ConversionPatternRewriter& rewriter, LoweringState& state, ValueRange targets, - const llvm::SmallVector& pauliGates) { + const SmallVector& pauliGates) { auto pauliGatesAttr = DenseI32ArrayAttr::get(rewriter.getContext(), pauliGates); @@ -286,7 +285,7 @@ static LogicalResult cleanUp(Operation* op, LoweringState& state) { return failure(); } - auto module = llvm::dyn_cast(op); + auto module = dyn_cast(op); if (!module) { return failure(); } @@ -312,7 +311,7 @@ static LogicalResult cleanUp(Operation* op, LoweringState& state) { module->setAttr("jeff.entrypoint", builder.getIntegerAttr(uint16Type, entryPoint)); - llvm::SmallVector stringRefs; + SmallVector stringRefs; stringRefs.reserve(state.strings.size()); for (const auto& str : state.strings) { stringRefs.emplace_back(str); @@ -1019,7 +1018,7 @@ struct ConvertQCOMainToJeff final : StatefulOpConversionPattern { } if (!llvm::any_of(passthrough, [](Attribute attr) { - const auto strAttr = llvm::dyn_cast(attr); + const auto strAttr = dyn_cast(attr); return strAttr && strAttr.getValue() == "entry_point"; })) { return failure(); @@ -1031,7 +1030,7 @@ struct ConvertQCOMainToJeff final : StatefulOpConversionPattern { auto* block = &op.getBlocks().front(); auto* returnOp = block->getTerminator(); - if (!llvm::isa(returnOp)) { + if (!isa(returnOp)) { return failure(); } @@ -1070,7 +1069,7 @@ class QCOToJeffTypeConverter final : public TypeConverter { }); addConversion([ctx](RankedTensorType type) -> Type { - if (llvm::isa(type.getElementType())) { + if (isa(type.getElementType())) { return jeff::QuregType::get(ctx, type.getShape()[0]); } return type; diff --git a/mlir/lib/Conversion/QCOToQC/QCOToQC.cpp b/mlir/lib/Conversion/QCOToQC/QCOToQC.cpp index 88e5e6dec7..1fd0dc1742 100644 --- a/mlir/lib/Conversion/QCOToQC/QCOToQC.cpp +++ b/mlir/lib/Conversion/QCOToQC/QCOToQC.cpp @@ -18,7 +18,6 @@ #include "mlir/Dialect/QTensor/IR/QTensorDialect.h" #include "mlir/Dialect/QTensor/IR/QTensorOps.h" -#include #include #include #include @@ -30,7 +29,6 @@ #include #include #include -#include #include #include @@ -136,7 +134,7 @@ class QCOToQCTypeConverter final : public TypeConverter { }); addConversion([ctx](RankedTensorType type) -> Type { - if (llvm::isa(type.getElementType())) { + if (isa(type.getElementType())) { return MemRefType::get(type.getShape(), qc::QubitType::get(ctx)); } return type; @@ -168,7 +166,7 @@ struct ConvertQTensorAllocOp final return failure(); } auto qubitType = qc::QubitType::get(op.getContext()); - auto tensorType = llvm::cast(op.getResult().getType()); + auto tensorType = cast(op.getResult().getType()); auto memrefType = MemRefType::get(tensorType.getShape(), qubitType); if (tensorType.hasStaticShape()) { diff --git a/mlir/lib/Conversion/QCToQCO/QCToQCO.cpp b/mlir/lib/Conversion/QCToQCO/QCToQCO.cpp index 138005075d..fbfea89794 100644 --- a/mlir/lib/Conversion/QCToQCO/QCToQCO.cpp +++ b/mlir/lib/Conversion/QCToQCO/QCToQCO.cpp @@ -19,10 +19,8 @@ #include "mlir/Dialect/QTensor/IR/QTensorOps.h" #include -#include #include #include -#include #include #include #include @@ -106,7 +104,7 @@ struct LoweringState { /// Latest QCO SSA values for QC qubits that are remapped inside the /// modifier region. - llvm::DenseMap currentQubits; + DenseMap currentQubits; }; /// Per-region map from original QC qubit reference to its latest QCO SSA @@ -114,14 +112,14 @@ struct LoweringState { /// /// @details Keys are `Operation::getParentRegion()` for ops being converted /// (typically a `func.func` body or a modifier region). - llvm::DenseMap> qubitMap; + DenseMap> qubitMap; /// Per-region map from original QC register to its latest QTensor SSA value - llvm::DenseMap> tensorMap; + DenseMap> tensorMap; /// Per-region map from original QC qubit reference to its register /// information - llvm::DenseMap> qubitInfoMap; + DenseMap> qubitInfoMap; /// Stack of active modifier regions SmallVector modifierFrames; @@ -192,8 +190,8 @@ currentModifierFrame(LoweringState& state) { * returns the pair containing the map and a mutable reference to the value in * the map. */ -[[nodiscard]] static std::pair*, Value*> -findRegionLocalMap(llvm::DenseMap>& map, +[[nodiscard]] static std::pair*, Value*> +findRegionLocalMap(DenseMap>& map, Operation* anchor, Value reference) { for (auto* current = anchor->getParentRegion(); current != nullptr; current = current->getParentRegion()) { @@ -355,9 +353,9 @@ struct ConvertFuncReturnOp final : StatefulOpConversionPattern { // Build return values from qubitMap and collect live qubit information. // A qubit from the current scope is considered alive if it is returned from // the function. Otherwise, it is considered dead. - llvm::SmallVector returnValues; + SmallVector returnValues; returnValues.reserve(op.getNumOperands()); - llvm::DenseSet liveQubits; + DenseSet liveQubits; for (auto [qcOperand, adaptorOperand] : llvm::zip_equal(op.getOperands(), adaptor.getOperands())) { if (auto it = map.find(qcOperand); it != map.end()) { @@ -425,7 +423,7 @@ struct ConvertMemRefAllocOp final LogicalResult matchAndRewrite(memref::AllocOp op, OpAdaptor adaptor, ConversionPatternRewriter& rewriter) const override { - if (!llvm::isa(op.getType().getElementType())) { + if (!isa(op.getType().getElementType())) { return failure(); } @@ -476,7 +474,7 @@ struct ConvertMemRefLoadOp final : StatefulOpConversionPattern { matchAndRewrite(memref::LoadOp op, OpAdaptor adaptor, ConversionPatternRewriter& rewriter) const override { auto memref = op.getMemref(); - if (!llvm::isa(memref.getType().getElementType())) { + if (!isa(memref.getType().getElementType())) { return failure(); } @@ -538,7 +536,7 @@ struct ConvertMemRefDeallocOp final matchAndRewrite(memref::DeallocOp op, OpAdaptor /*adaptor*/, ConversionPatternRewriter& rewriter) const override { auto memref = op.getMemref(); - if (!llvm::isa(memref.getType().getElementType())) { + if (!isa(memref.getType().getElementType())) { return failure(); } @@ -1020,8 +1018,8 @@ struct QCToQCO final : impl::QCToQCOBase { target.addDynamicallyLegalDialect([](Operation* op) { auto isQubitMemref = [](Type t) { - auto mt = llvm::dyn_cast(t); - return mt && llvm::isa(mt.getElementType()); + auto mt = dyn_cast(t); + return mt && isa(mt.getElementType()); }; return llvm::none_of(op->getOperandTypes(), isQubitMemref) && llvm::none_of(op->getResultTypes(), isQubitMemref); diff --git a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp index 6196336a18..98942d998c 100644 --- a/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp +++ b/mlir/lib/Conversion/QCToQIR/QCToQIR.cpp @@ -16,11 +16,12 @@ #include "mlir/Dialect/QIR/Utils/QIRMetadata.h" #include "mlir/Dialect/QIR/Utils/QIRUtils.h" +#include #include #include #include +#include #include -#include #include #include #include @@ -31,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -86,7 +86,7 @@ struct LoweringState : QIRMetadata { llvm::StringMap resultArrays; /// Map from (register name, index) to loaded result - llvm::DenseMap, Value> loadedResults; + DenseMap, Value> loadedResults; /// Map from index to result pointer for non-register results DenseMap resultPtrs; @@ -339,7 +339,7 @@ struct QCToQIRTypeConverter final : LLVMTypeConverter { [ctx](QubitType /*type*/) { return LLVM::LLVMPointerType::get(ctx); }); addConversion([ctx](MemRefType type) -> Type { - if (llvm::isa(type.getElementType())) { + if (isa(type.getElementType())) { return LLVM::LLVMPointerType::get(ctx); } return type; @@ -1085,21 +1085,11 @@ struct QCToQIR final : impl::QCToQIRBase { builder.setInsertionPoint(&outputBlock.back()); if (!resultPtrs.empty()) { - // Sort result pointers for deterministic output - llvm::SmallVector> sortedPtrs; - for (const auto& [index, resultPtr] : resultPtrs) { - sortedPtrs.emplace_back(index, resultPtr); - } - llvm::sort(sortedPtrs, [](const auto& a, const auto& b) { - return a.first < b.first; - }); - - // Create output recording for each result pointer auto fnSig = LLVM::LLVMFunctionType::get(voidType, {ptrType, ptrType}); auto fnDec = getOrCreateFunctionDeclaration(builder, main, QIR_RECORD_OUTPUT, fnSig); - - for (const auto& [index, ptr] : sortedPtrs) { + // Create output recording for each result pointer + for (const auto& [index, ptr] : resultPtrs) { auto label = createResultLabel(builder, main, "__unnamed__" + std::to_string(index)) .getResult(); @@ -1109,25 +1099,14 @@ struct QCToQIR final : impl::QCToQIRBase { } if (!resultArrays.empty()) { - // Sort registers by name for deterministic output - SmallVector> sortedRegisters; - for (auto& [name, results] : resultArrays) { - sortedRegisters.emplace_back(name, results); - } - llvm::sort(sortedRegisters, [](const auto& a, const auto& b) { - return a.first < b.first; - }); - auto fnSig = LLVM::LLVMFunctionType::get( voidType, {builder.getI64Type(), ptrType, ptrType}); auto fnDec = getOrCreateFunctionDeclaration( builder, main, QIR_ARRAY_RECORD_OUTPUT, fnSig); - // Generate output recording for each register - for (auto& [name, results] : sortedRegisters) { + for (const auto& [name, results] : resultArrays) { auto size = results.getDefiningOp().getArraySize(); auto label = createResultLabel(builder, main, name).getResult(); - LLVM::CallOp::create(builder, main->getLoc(), fnDec, ValueRange{size, results, label}); } diff --git a/mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp b/mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp index d179ab0d29..60218eb7b7 100644 --- a/mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp +++ b/mlir/lib/Dialect/QC/Builder/QCProgramBuilder.cpp @@ -14,9 +14,7 @@ #include "mlir/Dialect/QC/IR/QCOps.h" #include "mlir/Dialect/Utils/Utils.h" -#include #include -#include #include #include #include @@ -29,7 +27,9 @@ #include #include #include +#include +#include #include #include #include @@ -48,7 +48,7 @@ QCProgramBuilder::QCProgramBuilder(MLIRContext* context) void QCProgramBuilder::initialize() { // Set insertion point to the module body - setInsertionPointToStart(module.getBody()); + setInsertionPointToStart(cast(module).getBody()); // Create main function as entry point auto funcType = getFunctionType({}, {getI64Type()}); @@ -68,6 +68,13 @@ Value QCProgramBuilder::intConstant(const int64_t value) { return arith::ConstantOp::create(*this, getI64IntegerAttr(value)).getResult(); } +Value QCProgramBuilder::QubitRegister::operator[](const size_t index) const { + if (index >= qubits.size()) { + llvm::reportFatalUsageError("Qubit index out of bounds"); + } + return qubits[index]; +} + Value QCProgramBuilder::allocQubit() { checkFinalized(); ensureAllocationMode(AllocationMode::Dynamic); @@ -103,7 +110,7 @@ QCProgramBuilder::allocQubitRegister(const int64_t size) { auto memref = memref::AllocOp::create(*this, memrefType); allocatedMemrefs.insert(memref); - llvm::SmallVector qubits; + SmallVector qubits; qubits.reserve(size); for (int64_t i = 0; i < size; ++i) { auto index = arith::ConstantIndexOp::create(*this, i); @@ -115,6 +122,17 @@ QCProgramBuilder::allocQubitRegister(const int64_t size) { return {.value = memref, .qubits = std::move(qubits)}; } +QCProgramBuilder::Bit +QCProgramBuilder::ClassicalRegister::operator[](const int64_t index) const { + if (index < 0 || index >= size) { + const std::string msg = "Bit index " + std::to_string(index) + + " out of bounds for register '" + name + + "' of size " + std::to_string(size); + llvm::reportFatalUsageError(msg.c_str()); + } + return {.registerName = name, .registerSize = size, .registerIndex = index}; +} + QCProgramBuilder::ClassicalRegister QCProgramBuilder::allocClassicalBitRegister(const int64_t size, std::string name) const { @@ -429,16 +447,14 @@ QCProgramBuilder& QCProgramBuilder::barrier(ValueRange qubits) { // Modifiers //===----------------------------------------------------------------------===// -QCProgramBuilder& -QCProgramBuilder::ctrl(ValueRange controls, - const llvm::function_ref& body) { +QCProgramBuilder& QCProgramBuilder::ctrl(ValueRange controls, + const function_ref& body) { checkFinalized(); CtrlOp::create(*this, controls, body); return *this; } -QCProgramBuilder& -QCProgramBuilder::inv(const llvm::function_ref& body) { +QCProgramBuilder& QCProgramBuilder::inv(const function_ref& body) { checkFinalized(); InvOp::create(*this, body); return *this; @@ -451,7 +467,7 @@ QCProgramBuilder::inv(const llvm::function_ref& body) { QCProgramBuilder& QCProgramBuilder::dealloc(Value qubit) { checkFinalized(); - if (llvm::isa_and_nonnull(qubit.getDefiningOp())) { + if (isa_and_nonnull(qubit.getDefiningOp())) { llvm::reportFatalUsageError( "Register-backed qubits cannot be deallocated manually"); } @@ -506,7 +522,7 @@ OwningOpRef QCProgramBuilder::finalize() { // Ensure that main function exists and insertion point is valid auto* insertionBlock = getInsertionBlock(); func::FuncOp mainFunc = nullptr; - for (auto op : module.getOps()) { + for (auto op : cast(module).getOps()) { if (op.getName() == "main") { mainFunc = op; break; @@ -522,7 +538,7 @@ OwningOpRef QCProgramBuilder::finalize() { } for (auto qubit : allocatedQubits) { - if (!llvm::isa(qubit.getDefiningOp())) { + if (!isa(qubit.getDefiningOp())) { DeallocOp::create(*this, qubit); } } @@ -543,12 +559,12 @@ OwningOpRef QCProgramBuilder::finalize() { ctx = nullptr; // Transfer ownership to the caller - return module; + return cast(module); } OwningOpRef QCProgramBuilder::build( MLIRContext* context, - const llvm::function_ref& buildFunc) { + const function_ref& buildFunc) { QCProgramBuilder builder(context); builder.initialize(); buildFunc(builder); diff --git a/mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp b/mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp index 064107de7b..d457fa9a35 100644 --- a/mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Modifiers/CtrlOp.cpp @@ -11,11 +11,8 @@ #include "mlir/Dialect/QC/IR/QCOps.h" #include -#include -#include #include #include -#include #include #include #include @@ -37,7 +34,7 @@ struct MergeNestedCtrl final : OpRewritePattern { LogicalResult matchAndRewrite(CtrlOp op, PatternRewriter& rewriter) const override { auto* bodyUnitary = op.getBodyUnitary().getOperation(); - auto bodyCtrlOp = llvm::dyn_cast(bodyUnitary); + auto bodyCtrlOp = dyn_cast(bodyUnitary); if (!bodyCtrlOp) { return failure(); } @@ -68,14 +65,14 @@ struct ReduceCtrl final : OpRewritePattern { PatternRewriter& rewriter) const override { auto* bodyUnitary = op.getBodyUnitary().getOperation(); // Inline ops from empty control modifiers, IdOp and BarrierOp - if (op.getNumControls() == 0 || llvm::isa(bodyUnitary)) { + if (op.getNumControls() == 0 || isa(bodyUnitary)) { rewriter.moveOpBefore(bodyUnitary, op); rewriter.replaceOp(op, bodyUnitary->getResults()); return success(); } // The remaining code explicitly handles GPhaseOp and nothing else - auto gPhaseOp = llvm::dyn_cast(bodyUnitary); + auto gPhaseOp = dyn_cast(bodyUnitary); if (!gPhaseOp) { return failure(); } @@ -110,7 +107,7 @@ UnitaryOpInterface CtrlOp::getBodyUnitary() { // also contain constants and arithmetic operations, e.g., created as part of // canonicalization. Thus, the only safe way to access the unitary operation // is to get the second operation from the back of the region. - return llvm::cast(*(++getBody()->rbegin())); + return cast(*(++getBody()->rbegin())); } Value CtrlOp::getQubit(const size_t i) { @@ -133,7 +130,7 @@ Value CtrlOp::getControl(const size_t i) { void CtrlOp::build(OpBuilder& odsBuilder, OperationState& odsState, ValueRange controls, - const llvm::function_ref& bodyBuilder) { + const function_ref& bodyBuilder) { const OpBuilder::InsertionGuard guard(odsBuilder); odsState.addOperands(controls); auto* region = odsState.addRegion(); @@ -149,22 +146,22 @@ LogicalResult CtrlOp::verify() { if (block.getOperations().size() < 2) { return emitOpError("body region must have at least two operations"); } - if (!llvm::isa(block.back())) { + if (!isa(block.back())) { return emitOpError( "last operation in body region must be a yield operation"); } auto iter = ++block.rbegin(); - if (!llvm::isa(*iter)) { + if (!isa(*iter)) { return emitOpError( "second to last operation in body region must be a unitary operation"); } for (auto it = ++iter; it != block.rend(); ++it) { - if (llvm::isa(*it)) { + if (isa(*it)) { return emitOpError("body region may only contain a single unitary op"); } } - llvm::SmallPtrSet uniqueQubits; + SmallPtrSet uniqueQubits; for (const auto& control : getControls()) { if (!uniqueQubits.insert(control).second) { return emitOpError("duplicate control qubit found"); diff --git a/mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp b/mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp index 10f3c1d9df..065fe431be 100644 --- a/mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp +++ b/mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp @@ -10,9 +10,7 @@ #include "mlir/Dialect/QC/IR/QCOps.h" -#include #include -#include #include #include #include @@ -20,7 +18,6 @@ #include #include #include -#include #include @@ -39,7 +36,7 @@ struct MoveCtrlOutside final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp invOp, PatternRewriter& rewriter) const override { auto bodyUnitary = invOp.getBodyUnitary(); - auto innerCtrlOp = llvm::dyn_cast(bodyUnitary.getOperation()); + auto innerCtrlOp = dyn_cast(bodyUnitary.getOperation()); if (!innerCtrlOp) { return failure(); } @@ -67,8 +64,7 @@ struct InlineSelfAdjoint final : OpRewritePattern { PatternRewriter& rewriter) const override { auto* innerOp = op.getBodyUnitary().getOperation(); - if (!llvm::isa( - innerOp)) { + if (!isa(innerOp)) { return failure(); } @@ -91,7 +87,7 @@ struct ReplaceWithKnownGates final : OpRewritePattern { PatternRewriter& rewriter) const override { auto* innerOp = op.getBodyUnitary().getOperation(); - return llvm::TypeSwitch(innerOp) + return TypeSwitch(innerOp) .Case([&](auto g) { Value negTheta = arith::NegFOp::create(rewriter, op.getLoc(), g.getTheta()); @@ -240,7 +236,7 @@ struct CancelNestedInv final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp invOp, PatternRewriter& rewriter) const override { auto innerUnitary = invOp.getBodyUnitary(); - auto innerInvOp = llvm::dyn_cast(innerUnitary.getOperation()); + auto innerInvOp = dyn_cast(innerUnitary.getOperation()); if (!innerInvOp) { return failure(); } @@ -261,11 +257,11 @@ UnitaryOpInterface InvOp::getBodyUnitary() { // also contain constants and arithmetic operations, e.g., created as part of // canonicalization. Thus, the only safe way to access the unitary operation // is to get the second operation from the back of the region. - return llvm::cast(*(++getBody()->rbegin())); + return cast(*(++getBody()->rbegin())); } void InvOp::build(OpBuilder& odsBuilder, OperationState& odsState, - const llvm::function_ref& bodyBuilder) { + const function_ref& bodyBuilder) { const OpBuilder::InsertionGuard guard(odsBuilder); auto* region = odsState.addRegion(); auto& block = region->emplaceBlock(); @@ -280,17 +276,17 @@ LogicalResult InvOp::verify() { if (block.getOperations().size() < 2) { return emitOpError("body region must have at least two operations"); } - if (!llvm::isa(block.back())) { + if (!isa(block.back())) { return emitOpError( "last operation in body region must be a yield operation"); } auto iter = ++block.rbegin(); - if (!llvm::isa(*iter)) { + if (!isa(*iter)) { return emitOpError( "second to last operation in body region must be a unitary operation"); } for (auto it = ++iter; it != block.rend(); ++it) { - if (llvm::isa(*it)) { + if (isa(*it)) { return emitOpError("body region may only contain a single unitary op"); } } diff --git a/mlir/lib/Dialect/QC/Transforms/ShrinkQubitRegisters.cpp b/mlir/lib/Dialect/QC/Transforms/ShrinkQubitRegisters.cpp index d81a666c5e..583f5ff135 100644 --- a/mlir/lib/Dialect/QC/Transforms/ShrinkQubitRegisters.cpp +++ b/mlir/lib/Dialect/QC/Transforms/ShrinkQubitRegisters.cpp @@ -11,16 +11,14 @@ #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/Transforms/Passes.h" -#include #include #include -#include #include #include #include #include #include -#include +#include #include #include @@ -59,12 +57,12 @@ struct ShrinkQubitRegister final : OpRewritePattern { return failure(); } - auto memRefType = llvm::dyn_cast(op.getMemref().getType()); + auto memRefType = dyn_cast(op.getMemref().getType()); if (!memRefType || memRefType.getRank() != 1 || !memRefType.hasStaticShape()) { return failure(); } - if (!llvm::isa(memRefType.getElementType())) { + if (!isa(memRefType.getElementType())) { return failure(); } if (!memRefType.getLayout().isIdentity()) { @@ -74,15 +72,15 @@ struct ShrinkQubitRegister final : OpRewritePattern { return failure(); } - llvm::SmallVector loadOps; - llvm::SmallVector liveIndices; - llvm::DenseMap newIndexByOldIndex; + SmallVector loadOps; + SmallVector liveIndices; + DenseMap newIndexByOldIndex; for (auto* user : op.getMemref().getUsers()) { if (user == op.getOperation()) { continue; } - auto loadOp = llvm::dyn_cast(user); + auto loadOp = dyn_cast(user); if (!loadOp) { return failure(); } diff --git a/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp b/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp index a8f3912b8b..d3fb4dc99d 100644 --- a/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp +++ b/mlir/lib/Dialect/QC/Translation/TranslateQuantumComputationToQC.cpp @@ -22,12 +22,10 @@ #include #include #include -#include #include #include #include #include -#include #include #include @@ -53,8 +51,8 @@ struct QregInfo { SmallVector qubits; }; -using BitMemInfo = std::pair; // (register ref, localIdx) +// (register ref, localIdx) +using BitMemInfo = std::pair; using BitIndexVec = SmallVector; } // namespace diff --git a/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp b/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp index becdc42b31..b2be9d8f10 100644 --- a/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp +++ b/mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp @@ -17,11 +17,9 @@ #include "mlir/Dialect/Utils/Utils.h" #include -#include #include #include #include -#include #include #include #include @@ -34,7 +32,9 @@ #include #include #include +#include +#include #include #include #include @@ -53,7 +53,7 @@ QCOProgramBuilder::QCOProgramBuilder(MLIRContext* context) void QCOProgramBuilder::initialize() { // Set insertion point to the module body - setInsertionPointToStart(module.getBody()); + setInsertionPointToStart(mlir::cast(module).getBody()); // Create main function as entry point auto funcType = getFunctionType({}, {getI64Type()}); @@ -73,6 +73,13 @@ Value QCOProgramBuilder::intConstant(const int64_t value) { return arith::ConstantOp::create(*this, getI64IntegerAttr(value)).getResult(); } +Value& QCOProgramBuilder::QubitRegister::operator[](const size_t index) { + if (index >= qubits.size()) { + llvm::reportFatalUsageError("Qubit index out of bounds"); + } + return qubits[index]; +} + Value QCOProgramBuilder::allocQubit() { checkFinalized(); ensureAllocationMode(AllocationMode::Dynamic); @@ -109,7 +116,7 @@ QCOProgramBuilder::allocQubitRegister(const int64_t size) { auto qtensor = qtensorAlloc(size); - llvm::SmallVector qubits; + SmallVector qubits; qubits.reserve(size); for (int64_t i = 0; i < size; ++i) { auto [qtensorOut, qubit] = qtensorExtract(qtensor, i); @@ -120,6 +127,17 @@ QCOProgramBuilder::allocQubitRegister(const int64_t size) { return {.value = qtensor, .qubits = std::move(qubits)}; } +QCOProgramBuilder::Bit +QCOProgramBuilder::ClassicalRegister::operator[](const int64_t index) const { + if (index < 0 || index >= size) { + const std::string msg = "Bit index " + std::to_string(index) + + " out of bounds for register '" + name + + "' of size " + std::to_string(size); + llvm::reportFatalUsageError(msg.c_str()); + } + return {.registerName = name, .registerSize = size, .registerIndex = index}; +} + QCOProgramBuilder::ClassicalRegister QCOProgramBuilder::allocClassicalBitRegister(const int64_t size, std::string name) const { @@ -170,11 +188,11 @@ void QCOProgramBuilder::validateTensorValue(Value tensor) const { "Invalid tensor value used (either consumed or not tracked)"); } - auto tensorType = llvm::dyn_cast(tensor.getType()); + auto tensorType = dyn_cast(tensor.getType()); if (!tensorType || tensorType.getRank() != 1) { llvm::reportFatalUsageError("Tensor must be of 1-D RankedTensorType!"); } - if (!llvm::isa(tensorType.getElementType())) { + if (!isa(tensorType.getElementType())) { llvm::reportFatalUsageError("Elements must be of QubitType!"); } } @@ -220,7 +238,7 @@ Value QCOProgramBuilder::qtensorFromElements(ValueRange elements) { } for (auto element : elements) { - if (!llvm::isa(element.getType())) { + if (!isa(element.getType())) { llvm::reportFatalUsageError("Elements must be QubitType!"); } validateQubitValue(element); @@ -339,12 +357,10 @@ Value QCOProgramBuilder::reset(Value qubit) { checkFinalized(); \ auto param = variantToValue(*this, getLoc(), PARAM); \ const auto controlsOut = \ - ctrl(control, {}, \ - [&](ValueRange /*targets*/) -> llvm::SmallVector { \ - OP_NAME(param); \ - return {}; \ - }) \ - .first; \ + ctrl(control, {}, [&](ValueRange /*targets*/) -> SmallVector { \ + OP_NAME(param); \ + return {}; \ + }).first; \ return controlsOut[0]; \ } \ ValueRange QCOProgramBuilder::mc##OP_NAME( \ @@ -352,12 +368,10 @@ Value QCOProgramBuilder::reset(Value qubit) { checkFinalized(); \ auto param = variantToValue(*this, getLoc(), PARAM); \ const auto controlsOut = \ - ctrl(controls, {}, \ - [&](ValueRange /*targets*/) -> llvm::SmallVector { \ - OP_NAME(param); \ - return {}; \ - }) \ - .first; \ + ctrl(controls, {}, [&](ValueRange /*targets*/) -> SmallVector { \ + OP_NAME(param); \ + return {}; \ + }).first; \ return controlsOut; \ } @@ -378,8 +392,8 @@ DEFINE_ZERO_TARGET_ONE_PARAMETER(GPhaseOp, gphase, theta) std::pair QCOProgramBuilder::c##OP_NAME(Value control, \ Value target) { \ checkFinalized(); \ - const auto [controlsOut, targetsOut] = ctrl( \ - control, target, [&](ValueRange targets) -> llvm::SmallVector { \ + const auto [controlsOut, targetsOut] = \ + ctrl(control, target, [&](ValueRange targets) -> SmallVector { \ return {OP_NAME(targets[0])}; \ }); \ return {controlsOut[0], targetsOut[0]}; \ @@ -388,10 +402,9 @@ DEFINE_ZERO_TARGET_ONE_PARAMETER(GPhaseOp, gphase, theta) ValueRange controls, Value target) { \ checkFinalized(); \ const auto [controlsOut, targetsOut] = \ - ctrl(controls, target, \ - [&](ValueRange targets) -> llvm::SmallVector { \ - return {OP_NAME(targets[0])}; \ - }); \ + ctrl(controls, target, [&](ValueRange targets) -> SmallVector { \ + return {OP_NAME(targets[0])}; \ + }); \ return {controlsOut, targetsOut[0]}; \ } @@ -425,8 +438,8 @@ DEFINE_ONE_TARGET_ZERO_PARAMETER(SXdgOp, sxdg) Value target) { \ checkFinalized(); \ auto param = variantToValue(*this, getLoc(), PARAM); \ - const auto [controlsOut, targetsOut] = ctrl( \ - control, target, [&](ValueRange targets) -> llvm::SmallVector { \ + const auto [controlsOut, targetsOut] = \ + ctrl(control, target, [&](ValueRange targets) -> SmallVector { \ return {OP_NAME(param, targets[0])}; \ }); \ return {controlsOut[0], targetsOut[0]}; \ @@ -437,10 +450,9 @@ DEFINE_ONE_TARGET_ZERO_PARAMETER(SXdgOp, sxdg) checkFinalized(); \ auto param = variantToValue(*this, getLoc(), PARAM); \ const auto [controlsOut, targetsOut] = \ - ctrl(controls, target, \ - [&](ValueRange targets) -> llvm::SmallVector { \ - return {OP_NAME(param, targets[0])}; \ - }); \ + ctrl(controls, target, [&](ValueRange targets) -> SmallVector { \ + return {OP_NAME(param, targets[0])}; \ + }); \ return {controlsOut, targetsOut[0]}; \ } @@ -470,8 +482,8 @@ DEFINE_ONE_TARGET_ONE_PARAMETER(POp, p, phi) checkFinalized(); \ auto param1 = variantToValue(*this, getLoc(), PARAM1); \ auto param2 = variantToValue(*this, getLoc(), PARAM2); \ - const auto [controlsOut, targetsOut] = ctrl( \ - control, target, [&](ValueRange targets) -> llvm::SmallVector { \ + const auto [controlsOut, targetsOut] = \ + ctrl(control, target, [&](ValueRange targets) -> SmallVector { \ return {OP_NAME(param1, param2, targets[0])}; \ }); \ return {controlsOut[0], targetsOut[0]}; \ @@ -484,10 +496,9 @@ DEFINE_ONE_TARGET_ONE_PARAMETER(POp, p, phi) auto param1 = variantToValue(*this, getLoc(), PARAM1); \ auto param2 = variantToValue(*this, getLoc(), PARAM2); \ const auto [controlsOut, targetsOut] = \ - ctrl(controls, target, \ - [&](ValueRange targets) -> llvm::SmallVector { \ - return {OP_NAME(param1, param2, targets[0])}; \ - }); \ + ctrl(controls, target, [&](ValueRange targets) -> SmallVector { \ + return {OP_NAME(param1, param2, targets[0])}; \ + }); \ return {controlsOut, targetsOut[0]}; \ } @@ -519,8 +530,8 @@ DEFINE_ONE_TARGET_TWO_PARAMETER(U2Op, u2, phi, lambda) auto param1 = variantToValue(*this, getLoc(), PARAM1); \ auto param2 = variantToValue(*this, getLoc(), PARAM2); \ auto param3 = variantToValue(*this, getLoc(), PARAM3); \ - const auto [controlsOut, targetsOut] = ctrl( \ - control, target, [&](ValueRange targets) -> llvm::SmallVector { \ + const auto [controlsOut, targetsOut] = \ + ctrl(control, target, [&](ValueRange targets) -> SmallVector { \ return {OP_NAME(param1, param2, param3, targets[0])}; \ }); \ return {controlsOut[0], targetsOut[0]}; \ @@ -535,10 +546,9 @@ DEFINE_ONE_TARGET_TWO_PARAMETER(U2Op, u2, phi, lambda) auto param2 = variantToValue(*this, getLoc(), PARAM2); \ auto param3 = variantToValue(*this, getLoc(), PARAM3); \ const auto [controlsOut, targetsOut] = \ - ctrl(controls, target, \ - [&](ValueRange targets) -> llvm::SmallVector { \ - return {OP_NAME(param1, param2, param3, targets[0])}; \ - }); \ + ctrl(controls, target, [&](ValueRange targets) -> SmallVector { \ + return {OP_NAME(param1, param2, param3, targets[0])}; \ + }); \ return {controlsOut, targetsOut[0]}; \ } @@ -564,7 +574,7 @@ DEFINE_ONE_TARGET_THREE_PARAMETER(UOp, u, theta, phi, lambda) checkFinalized(); \ const auto [controlsOut, targetsOut] = \ ctrl(control, {qubit0, qubit1}, \ - [&](ValueRange targets) -> llvm::SmallVector { \ + [&](ValueRange targets) -> SmallVector { \ auto [q0, q1] = OP_NAME(targets[0], targets[1]); \ return {q0, q1}; \ }); \ @@ -576,7 +586,7 @@ DEFINE_ONE_TARGET_THREE_PARAMETER(UOp, u, theta, phi, lambda) checkFinalized(); \ const auto [controlsOut, targetsOut] = \ ctrl(controls, {qubit0, qubit1}, \ - [&](ValueRange targets) -> llvm::SmallVector { \ + [&](ValueRange targets) -> SmallVector { \ auto [q0, q1] = OP_NAME(targets[0], targets[1]); \ return {q0, q1}; \ }); \ @@ -610,7 +620,7 @@ DEFINE_TWO_TARGET_ZERO_PARAMETER(ECROp, ecr) auto param = variantToValue(*this, getLoc(), PARAM); \ const auto [controlsOut, targetsOut] = \ ctrl(control, {qubit0, qubit1}, \ - [&](ValueRange targets) -> llvm::SmallVector { \ + [&](ValueRange targets) -> SmallVector { \ auto [q0, q1] = OP_NAME(param, targets[0], targets[1]); \ return {q0, q1}; \ }); \ @@ -624,7 +634,7 @@ DEFINE_TWO_TARGET_ZERO_PARAMETER(ECROp, ecr) auto param = variantToValue(*this, getLoc(), PARAM); \ const auto [controlsOut, targetsOut] = \ ctrl(controls, {qubit0, qubit1}, \ - [&](ValueRange targets) -> llvm::SmallVector { \ + [&](ValueRange targets) -> SmallVector { \ auto [q0, q1] = OP_NAME(param, targets[0], targets[1]); \ return {q0, q1}; \ }); \ @@ -662,7 +672,7 @@ DEFINE_TWO_TARGET_ONE_PARAMETER(RZZOp, rzz, theta) auto param2 = variantToValue(*this, getLoc(), PARAM2); \ const auto [controlsOut, targetsOut] = \ ctrl(control, {qubit0, qubit1}, \ - [&](ValueRange targets) -> llvm::SmallVector { \ + [&](ValueRange targets) -> SmallVector { \ auto [q0, q1] = \ OP_NAME(param1, param2, targets[0], targets[1]); \ return {q0, q1}; \ @@ -679,7 +689,7 @@ DEFINE_TWO_TARGET_ONE_PARAMETER(RZZOp, rzz, theta) auto param2 = variantToValue(*this, getLoc(), PARAM2); \ const auto [controlsOut, targetsOut] = \ ctrl(controls, {qubit0, qubit1}, \ - [&](ValueRange targets) -> llvm::SmallVector { \ + [&](ValueRange targets) -> SmallVector { \ auto [q0, q1] = \ OP_NAME(param1, param2, targets[0], targets[1]); \ return {q0, q1}; \ @@ -709,9 +719,9 @@ ValueRange QCOProgramBuilder::barrier(ValueRange qubits) { // Modifiers //===----------------------------------------------------------------------===// -std::pair QCOProgramBuilder::ctrl( - ValueRange controls, ValueRange targets, - llvm::function_ref(ValueRange)> body) { +std::pair +QCOProgramBuilder::ctrl(ValueRange controls, ValueRange targets, + function_ref(ValueRange)> body) { checkFinalized(); auto ctrlOp = CtrlOp::create(*this, controls, targets); @@ -746,9 +756,9 @@ std::pair QCOProgramBuilder::ctrl( return {controlsOut, targetsOut}; } -ValueRange QCOProgramBuilder::inv( - ValueRange qubits, - llvm::function_ref(ValueRange)> body) { +ValueRange +QCOProgramBuilder::inv(ValueRange qubits, + function_ref(ValueRange)> body) { checkFinalized(); auto invOp = InvOp::create(*this, qubits); @@ -803,8 +813,8 @@ QCOProgramBuilder& QCOProgramBuilder::sink(Value qubit) { ValueRange QCOProgramBuilder::qcoIf( const std::variant& condition, ValueRange qubits, - llvm::function_ref(ValueRange)> thenBody, - llvm::function_ref(ValueRange)> elseBody) { + function_ref(ValueRange)> thenBody, + function_ref(ValueRange)> elseBody) { checkFinalized(); auto conditionValue = variantToValue(*this, getLoc(), condition); @@ -828,7 +838,7 @@ ValueRange QCOProgramBuilder::qcoIf( const auto thenResult = thenBody(thenBlock.getArguments()); YieldOp::create(*this, thenResult); setInsertionPointToStart(&elseBlock); - llvm::SmallVector elseResult; + SmallVector elseResult; if (elseBody) { elseResult = elseBody(elseBlock.getArguments()); YieldOp::create(*this, elseResult); @@ -902,7 +912,7 @@ OwningOpRef QCOProgramBuilder::finalize() { // Ensure that main function exists and insertion point is valid auto* insertionBlock = getInsertionBlock(); func::FuncOp mainFunc = nullptr; - for (auto op : module.getOps()) { + for (auto op : cast(module).getOps()) { if (op.getName() == "main") { mainFunc = op; break; @@ -917,13 +927,12 @@ OwningOpRef QCOProgramBuilder::finalize() { "Insertion point is not in entry block of main function"); } - llvm::DenseSet validTensorIds; + DenseSet validTensorIds; for (const auto& [tensor, info] : validTensors) { validTensorIds.insert(info.regId); } - llvm::DenseMap>> - qubitsByRegister; + DenseMap>> qubitsByRegister; for (auto [qubit, info] : validQubits) { if (info.regId == -1 || !validTensorIds.contains(info.regId)) { // Automatically deallocate all still-allocated qubits @@ -958,12 +967,12 @@ OwningOpRef QCOProgramBuilder::finalize() { // Invalidate context to prevent use-after-finalize ctx = nullptr; - return module; + return cast(module); } OwningOpRef QCOProgramBuilder::build( MLIRContext* context, - const llvm::function_ref& buildFunc) { + const function_ref& buildFunc) { QCOProgramBuilder builder(context); builder.initialize(); buildFunc(builder); diff --git a/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp b/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp index 96c811eed8..25fc88d084 100644 --- a/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -24,7 +23,6 @@ #include #include #include -#include #include #include @@ -51,8 +49,7 @@ struct MergeNestedCtrl final : OpRewritePattern { return failure(); } - auto bodyCtrlOp = - llvm::dyn_cast(op.getBodyUnitary().getOperation()); + auto bodyCtrlOp = dyn_cast(op.getBodyUnitary().getOperation()); if (!bodyCtrlOp) { return failure(); } @@ -66,7 +63,7 @@ struct MergeNestedCtrl final : OpRewritePattern { rewriter.replaceOpWithNewOp( op, newControls, newTargets, - [&](ValueRange newTargetArgs) -> llvm::SmallVector { + [&](ValueRange newTargetArgs) -> SmallVector { IRMapping mapping; auto* innerBody = bodyCtrlOp.getBody(); for (size_t i = 0; i < bodyCtrlOp.getNumTargets(); ++i) { @@ -92,7 +89,7 @@ struct ReduceCtrl final : OpRewritePattern { PatternRewriter& rewriter) const override { auto* bodyUnitary = op.getBodyUnitary().getOperation(); // Inline ops from empty control modifiers, IdOp and BarrierOp - if (op.getNumControls() == 0 || llvm::isa(bodyUnitary)) { + if (op.getNumControls() == 0 || isa(bodyUnitary)) { rewriter.moveOpBefore(bodyUnitary, op); bodyUnitary->setOperands(0, op.getNumTargets(), op.getTargetsIn()); rewriter.replaceAllUsesWith(op.getControlsOut(), op.getControlsIn()); @@ -103,7 +100,7 @@ struct ReduceCtrl final : OpRewritePattern { } // The remaining code explicitly handles GPhaseOp and nothing else - auto gPhaseOp = llvm::dyn_cast(bodyUnitary); + auto gPhaseOp = dyn_cast(bodyUnitary); if (!gPhaseOp) { return failure(); } @@ -136,7 +133,7 @@ struct ReduceCtrl final : OpRewritePattern { POp::create(rewriter, gPhaseOp.getLoc(), arg, gPhaseOp.getTheta()); // Add the results of the POp to the yield operation - auto yieldOp = llvm::cast(op.getBody()->back()); + auto yieldOp = cast(op.getBody()->back()); yieldOp->setOperands(pOp->getResults()); // erase the GPhaseOp @@ -154,7 +151,7 @@ UnitaryOpInterface CtrlOp::getBodyUnitary() { // also contain constants and arithmetic operations, e.g., created as part of // canonicalization. Thus, the only safe way to access the unitary operation // is to get the second operation from the back of the region. - return llvm::cast(*(++getBody()->rbegin())); + return cast(*(++getBody()->rbegin())); } Value CtrlOp::getInputQubit(const size_t i) { @@ -235,10 +232,9 @@ Value CtrlOp::getOutputForInput(Value input) { llvm::reportFatalUsageError("Given qubit is not an input of the operation"); } -void CtrlOp::build( - OpBuilder& odsBuilder, OperationState& odsState, ValueRange controls, - ValueRange targets, - llvm::function_ref(ValueRange)> bodyBuilder) { +void CtrlOp::build(OpBuilder& odsBuilder, OperationState& odsState, + ValueRange controls, ValueRange targets, + function_ref(ValueRange)> bodyBuilder) { build(odsBuilder, odsState, controls, targets); auto& block = odsState.regions.front()->emplaceBlock(); @@ -270,7 +266,7 @@ LogicalResult CtrlOp::verify() { << i << " does not match target type"; } } - if (!llvm::isa(block.back())) { + if (!isa(block.back())) { return emitOpError( "last operation in body region must be a yield operation"); } @@ -280,12 +276,12 @@ LogicalResult CtrlOp::verify() { << numTargets << " values, but found " << numYieldOperands; } auto iter = ++block.rbegin(); - if (!llvm::isa(*iter)) { + if (!isa(*iter)) { return emitOpError( "second to last operation in body region must be a unitary operation"); } for (auto it = ++iter; it != block.rend(); ++it) { - if (llvm::isa(*it)) { + if (isa(*it)) { return emitOpError("body region may only contain a single unitary op"); } } diff --git a/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp b/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp index 18cc331c5b..d82a64f819 100644 --- a/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp @@ -13,9 +13,7 @@ #include #include -#include #include -#include #include #include #include @@ -25,7 +23,6 @@ #include #include #include -#include #include #include @@ -46,7 +43,7 @@ struct MoveCtrlOutside final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp invOp, PatternRewriter& rewriter) const override { auto bodyUnitary = invOp.getBodyUnitary(); - auto innerCtrlOp = llvm::dyn_cast(bodyUnitary.getOperation()); + auto innerCtrlOp = dyn_cast(bodyUnitary.getOperation()); if (!innerCtrlOp) { return failure(); } @@ -59,10 +56,10 @@ struct MoveCtrlOutside final : OpRewritePattern { rewriter.replaceOpWithNewOp( invOp, controls, targets, - [&](ValueRange newTargetArgs) -> llvm::SmallVector { + [&](ValueRange newTargetArgs) -> SmallVector { return InvOp::create( rewriter, invOp.getLoc(), newTargetArgs, - [&](ValueRange invArgs) -> llvm::SmallVector { + [&](ValueRange invArgs) -> SmallVector { IRMapping mapping; auto* innerBody = innerCtrlOp.getBody(); for (size_t i = 0; i < innerCtrlOp.getNumTargets(); @@ -93,8 +90,7 @@ struct InlineSelfAdjoint final : OpRewritePattern { PatternRewriter& rewriter) const override { auto* innerOp = op.getBodyUnitary().getOperation(); - if (!llvm::isa( - innerOp)) { + if (!isa(innerOp)) { return failure(); } @@ -118,7 +114,7 @@ struct ReplaceWithKnownGates final : OpRewritePattern { PatternRewriter& rewriter) const override { auto* innerOp = op.getBodyUnitary().getOperation(); - return llvm::TypeSwitch(innerOp) + return TypeSwitch(innerOp) .Case([&](auto g) { Value negTheta = arith::NegFOp::create(rewriter, op.getLoc(), g.getTheta()); @@ -263,7 +259,7 @@ struct CancelNestedInv final : OpRewritePattern { LogicalResult matchAndRewrite(InvOp op, PatternRewriter& rewriter) const override { auto* innerUnitary = op.getBodyUnitary().getOperation(); - auto innerInvOp = llvm::dyn_cast(innerUnitary); + auto innerInvOp = dyn_cast(innerUnitary); if (!innerInvOp) { return failure(); } @@ -285,7 +281,7 @@ UnitaryOpInterface InvOp::getBodyUnitary() { // also contain constants and arithmetic operations, e.g., created as part of // canonicalization. Thus, the only safe way to access the unitary operation // is to get the second operation from the back of the region. - return llvm::cast(*(++getBody()->rbegin())); + return cast(*(++getBody()->rbegin())); } Value InvOp::getInputQubit(const size_t i) { @@ -320,9 +316,9 @@ Value InvOp::getOutputForInput(Value input) { llvm::reportFatalUsageError("Given qubit is not an input of the operation"); } -void InvOp::build( - OpBuilder& odsBuilder, OperationState& odsState, ValueRange qubits, - llvm::function_ref(ValueRange)> bodyBuilder) { +void InvOp::build(OpBuilder& odsBuilder, OperationState& odsState, + ValueRange qubits, + function_ref(ValueRange)> bodyBuilder) { build(odsBuilder, odsState, qubits); auto& block = odsState.regions.front()->emplaceBlock(); @@ -354,7 +350,7 @@ LogicalResult InvOp::verify() { << i << " does not match target type"; } } - if (!llvm::isa(block.back())) { + if (!isa(block.back())) { return emitOpError( "last operation in body region must be a yield operation"); } @@ -364,12 +360,12 @@ LogicalResult InvOp::verify() { << numTargets << " values, but found " << numYieldOperands; } auto iter = ++block.rbegin(); - if (!llvm::isa(*iter)) { + if (!isa(*iter)) { return emitOpError( "second to last operation in body region must be a unitary operation"); } for (auto it = ++iter; it != block.rend(); ++it) { - if (llvm::isa(*it)) { + if (isa(*it)) { return emitOpError("body region may only contain a single unitary op"); } } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/ResetOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/ResetOp.cpp index 3db78fbd8b..8832162354 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/ResetOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/ResetOp.cpp @@ -12,13 +12,12 @@ #include "mlir/Dialect/QTensor/IR/QTensorOps.h" #include "mlir/Dialect/QTensor/IR/QTensorUtils.h" -#include #include #include #include #include #include -#include +#include using namespace mlir; using namespace mlir::qco; @@ -40,11 +39,11 @@ static bool originatesFromQTensorAlloc(qtensor::ExtractOp extractOp) { } while (auto* definingOp = current.getDefiningOp()) { - if (llvm::isa(definingOp)) { + if (isa(definingOp)) { return true; } - if (auto nestedExtractOp = llvm::dyn_cast(definingOp)) { + if (auto nestedExtractOp = dyn_cast(definingOp)) { auto nestedExtractIndex = nestedExtractOp.getIndex(); if (!getConstantIntValue(nestedExtractIndex)) { return false; @@ -56,7 +55,7 @@ static bool originatesFromQTensorAlloc(qtensor::ExtractOp extractOp) { continue; } - if (auto insertOp = llvm::dyn_cast(definingOp)) { + if (auto insertOp = dyn_cast(definingOp)) { auto insertIndex = insertOp.getIndex(); if (!getConstantIntValue(insertIndex)) { return false; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/BarrierOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/BarrierOp.cpp index d168624987..0bd50dd3a3 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/BarrierOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/BarrierOp.cpp @@ -10,15 +10,15 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" +#include #include -#include +#include #include #include #include #include #include #include -#include #include @@ -44,7 +44,7 @@ struct MergeSubsequentBarrier final : OpRewritePattern { SmallVector indicesToFill; for (size_t i = 0; i < qubitsIn.size(); ++i) { - if (llvm::isa( + if (isa( *op.getOutputForInput(qubitsIn[i]).getUsers().begin())) { anythingToMerge = true; newQubitsOutMap[i] = qubitsIn[i]; diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp index ca26cf0c78..02e3c5d421 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RXXOp.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp index 6b9d540062..2996fdd289 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RYYOp.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp index be40c3de9a..9f3ecedfe6 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZXOp.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp index 5fb05c3379..8a6793475b 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/RZZOp.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp index 79dc169ae1..6bd93add60 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXMinusYYOp.cpp @@ -12,15 +12,12 @@ #include "mlir/Dialect/Utils/Utils.h" #include -#include #include #include -#include #include #include #include #include -#include #include #include @@ -44,8 +41,7 @@ struct MergeSubsequentXXMinusYY final : OpRewritePattern { LogicalResult matchAndRewrite(XXMinusYYOp op, PatternRewriter& rewriter) const override { // Check if the successor is the same operation - auto nextOp = - llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } diff --git a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp index 03d8853371..63648cdcf3 100644 --- a/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/Operations/StandardGates/XXPlusYYOp.cpp @@ -12,14 +12,12 @@ #include "mlir/Dialect/Utils/Utils.h" #include -#include #include #include #include #include #include #include -#include #include #include @@ -43,8 +41,7 @@ struct MergeSubsequentXXPlusYY final : OpRewritePattern { LogicalResult matchAndRewrite(XXPlusYYOp op, PatternRewriter& rewriter) const override { // Check if the successor is the same operation - auto nextOp = - llvm::dyn_cast(*op.getOutputQubit(0).user_begin()); + auto nextOp = dyn_cast(*op.getOutputQubit(0).user_begin()); if (!nextOp) { return failure(); } diff --git a/mlir/lib/Dialect/QCO/IR/QCOOps.cpp b/mlir/lib/Dialect/QCO/IR/QCOOps.cpp index 36aa66ad54..21beaedf96 100644 --- a/mlir/lib/Dialect/QCO/IR/QCOOps.cpp +++ b/mlir/lib/Dialect/QCO/IR/QCOOps.cpp @@ -14,13 +14,13 @@ #include #include +#include #include #include #include #include #include #include -#include // The following headers are needed for some template instantiations. // IWYU pragma: begin_keep @@ -160,7 +160,7 @@ parseIfOpAliasing(OpAsmParser& parser, Region& thenRegion, Region& elseRegion, } // Remove duplicate operands - llvm::DenseSet seen; + DenseSet seen; llvm::erase_if(operands, [&](const auto& op) { return !seen.insert(op.name).second; }); diff --git a/mlir/lib/Dialect/QCO/IR/QubitManagement/SinkOp.cpp b/mlir/lib/Dialect/QCO/IR/QubitManagement/SinkOp.cpp index de6ee6edc7..ec8b33b833 100644 --- a/mlir/lib/Dialect/QCO/IR/QubitManagement/SinkOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/QubitManagement/SinkOp.cpp @@ -10,10 +10,9 @@ #include "mlir/Dialect/QCO/IR/QCOOps.h" -#include #include #include -#include +#include using namespace mlir; using namespace mlir::qco; @@ -30,7 +29,7 @@ struct RemoveAllocSinkPair final : OpRewritePattern { LogicalResult matchAndRewrite(SinkOp op, PatternRewriter& rewriter) const override { auto* defOp = op.getQubit().getDefiningOp(); - if (!llvm::isa_and_nonnull(defOp)) { + if (!isa_and_nonnull(defOp)) { return failure(); } diff --git a/mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp b/mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp index 6aec9b9ed7..220c36e7db 100644 --- a/mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp +++ b/mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -28,18 +27,16 @@ #include #include #include -#include #include using namespace mlir; using namespace mlir::qco; -void IfOp::build( - OpBuilder& odsBuilder, OperationState& odsState, Value condition, - ValueRange qubits, - llvm::function_ref(ValueRange)> thenBuilder, - llvm::function_ref(ValueRange)> elseBuilder) { +void IfOp::build(OpBuilder& odsBuilder, OperationState& odsState, + Value condition, ValueRange qubits, + function_ref(ValueRange)> thenBuilder, + function_ref(ValueRange)> elseBuilder) { // Build the empty operation build(odsBuilder, odsState, qubits.getTypes(), condition, qubits); @@ -49,22 +46,19 @@ void IfOp::build( const OpBuilder::InsertionGuard guard(odsBuilder); // Add the block arguments and insert the yield operation - thenBlock.addArguments( - qubits.getTypes(), - SmallVector(qubits.size(), odsState.location)); + thenBlock.addArguments(qubits.getTypes(), + SmallVector(qubits.size(), odsState.location)); odsBuilder.setInsertionPointToStart(&thenBlock); - qco::YieldOp::create(odsBuilder, odsState.location, - thenBuilder(thenBlock.getArguments())); - elseBlock.addArguments( - qubits.getTypes(), - SmallVector(qubits.size(), odsState.location)); + YieldOp::create(odsBuilder, odsState.location, + thenBuilder(thenBlock.getArguments())); + elseBlock.addArguments(qubits.getTypes(), + SmallVector(qubits.size(), odsState.location)); odsBuilder.setInsertionPointToStart(&elseBlock); if (elseBuilder) { - qco::YieldOp::create(odsBuilder, odsState.location, - elseBuilder(elseBlock.getArguments())); + YieldOp::create(odsBuilder, odsState.location, + elseBuilder(elseBlock.getArguments())); } else { - qco::YieldOp::create(odsBuilder, odsState.location, - elseBlock.getArguments()); + YieldOp::create(odsBuilder, odsState.location, elseBlock.getArguments()); } } @@ -113,7 +107,7 @@ void IfOp::getEntrySuccessorRegions(ArrayRef operands, void IfOp::getRegionInvocationBounds( ArrayRef operands, SmallVectorImpl& invocationBounds) { - if (auto cond = llvm::dyn_cast_or_null(operands[0])) { + if (auto cond = dyn_cast_or_null(operands[0])) { // If the condition is known, then one region is known to be executed once // and the other zero times. invocationBounds.emplace_back(0, cond.getValue() ? 1 : 0); @@ -206,7 +200,7 @@ struct ConditionPropagation : public OpRewritePattern { } bool changed = false; - mlir::Type i1Ty = rewriter.getI1Type(); + Type i1Ty = rewriter.getI1Type(); // These variables serve to prevent creating duplicate constants // and hold constant true or false values. @@ -257,7 +251,7 @@ LogicalResult IfOp::verify() { const auto numOutputQubits = outputQubits.size(); for (auto type : inputQubits.getTypes()) { - if (!llvm::isa(type)) { + if (!isa(type)) { return emitOpError("Inputs must be qubit type!"); } } diff --git a/mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt b/mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt index 2268584167..fc6ee74b9d 100644 --- a/mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt +++ b/mlir/lib/Dialect/QCO/Transforms/CMakeLists.txt @@ -15,6 +15,8 @@ add_mlir_library( PRIVATE MLIRQCODialect MLIRQCOUtils + MLIRArithDialect + MLIRMathDialect DEPENDS MLIRQCOTransformsIncGen) diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/Architecture.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/Architecture.cpp index 593c2e6501..0f2cb5876f 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/Architecture.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/Architecture.cpp @@ -10,6 +10,8 @@ #include "mlir/Dialect/QCO/Transforms/Mapping/Architecture.h" +#include +#include #include #include @@ -39,7 +41,8 @@ std::size_t Architecture::distanceBetween(std::size_t u, std::size_t v) const { return dist_[u][v]; } -SmallVector Architecture::neighboursOf(std::size_t u) const { +SmallVector +Architecture::neighboursOf(const std::size_t u) const { return neighbours_[u]; } diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp index 0682b1dd87..a3482f50bb 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp @@ -14,8 +14,11 @@ #include "mlir/Dialect/QCO/Transforms/Mapping/Architecture.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Dialect/QCO/Utils/Drivers.h" +#include "mlir/Dialect/QCO/Utils/Qubits.h" #include "mlir/Dialect/QCO/Utils/WireIterator.h" +#include +#include #include #include #include @@ -45,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -810,7 +812,7 @@ struct MappingPass : impl::MappingPassBase { ArrayRef::iterator anchorIt = anchors.begin(); ArrayRef>::iterator swapIt = swaps.begin(); - walkUnit(funcBody, [&](Operation* op, Qubits& qubits) { + walkProgram(funcBody, [&](Operation* op, Qubits& qubits) { // Early exit if we've processed all layers. if (anchorIt == anchors.end()) { return WalkResult::interrupt(); @@ -830,14 +832,6 @@ struct MappingPass : impl::MappingPassBase { rewriter.replaceAllUsesExcept(in0, out1, insertedOp); rewriter.replaceAllUsesExcept(in1, out0, insertedOp); - - // Remove old qubit values. - qubits.remove(in0); - qubits.remove(in1); - - // Add permutated qubit value - hw index pair. - qubits.add(out0, hw1); - qubits.add(out1, hw0); } // Collect statistics. diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp new file mode 100644 index 0000000000..920fd5ba7c --- /dev/null +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/SwapAbsorb.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" +#include "mlir/Dialect/QCO/Utils/Drivers.h" +#include "mlir/Dialect/QCO/Utils/WireIterator.h" +#include "mlir/Dialect/QTensor/IR/QTensorOps.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mlir::qco { +#define GEN_PASS_DEF_SWAPABSORB +#include "mlir/Dialect/QCO/Transforms/Passes.h.inc" + +namespace { +struct SwapAbsorb : impl::SwapAbsorbBase { +public: + using SwapAbsorbBase::SwapAbsorbBase; + + void runOnOperation() override { + ModuleOp anchor = getOperation(); + IRRewriter rewriter(&getContext()); + insertStatics(anchor, rewriter); + + for (auto func : anchor.getOps()) { + SmallVector wires; + for (auto op : func.getOps()) { + wires.emplace_back(op.getQubit()); + } + + SmallVector readyToAbsorb; + readyToAbsorb.reserve((wires.size() + 1) / 2); + + // std::ignore = + // walkCircuitGraph(wires, WalkDirection::Forward, + // [&](const ReadyRange& ready, ReleasedOps& released) { + // for (const auto& [op, indices] : ready) { + // if (isa(op)) { + // readyToAbsorb.emplace_back(op); + // } + // released.emplace_back(op); + // } + // return WalkResult::interrupt(); + // }); + + for (auto swapOp : readyToAbsorb) { + auto in0 = swapOp.getQubit0In(); + auto in1 = swapOp.getQubit1In(); + + auto out0 = swapOp.getQubit0Out(); + auto out1 = swapOp.getQubit1Out(); + + // TODO: What if single qubit gates are in front of the SWAP input? + // Tipp: Use the WireIterator. + StaticOp op0 = cast(in0.getDefiningOp()); + StaticOp op1 = cast(in1.getDefiningOp()); + + rewriter.replaceAllUsesWith(out0, op1.getQubit()); + rewriter.replaceAllUsesWith(out1, op0.getQubit()); + rewriter.eraseOp(swapOp); + } + } + } + +private: + static void insertStatics(ModuleOp anchor, IRRewriter& rewriter) { + for (auto func : anchor.getOps()) { + SmallVector worklist; + for (Operation& op : func.getOps()) { + worklist.emplace_back(&op); + } + + std::size_t n = llvm::range_size(func.getOps()) - 1; + for (Operation* op : llvm::reverse(worklist)) { + rewriter.setInsertionPoint(op); + + if (auto tensorDealloc = dyn_cast(op)) { + rewriter.eraseOp(tensorDealloc); + continue; + } + + if (auto tensorInsert = dyn_cast(op)) { + auto q = tensorInsert.getScalar(); + rewriter.create(rewriter.getUnknownLoc(), q); + rewriter.eraseOp(tensorInsert); + continue; + } + + if (auto tensorExtract = dyn_cast(op)) { + auto q = tensorExtract.getResult(); + auto staticOp = + rewriter.create(rewriter.getUnknownLoc(), n); + rewriter.replaceAllUsesWith(q, staticOp.getQubit()); + rewriter.eraseOp(tensorExtract); + n--; + continue; + } + + if (auto tensorAlloc = dyn_cast(op)) { + rewriter.eraseOp(tensorAlloc); + continue; + } + } + } + } +}; +} // namespace +} // namespace mlir::qco diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp new file mode 100644 index 0000000000..f35ce043ad --- /dev/null +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/MergeSingleQubitRotationGates.cpp @@ -0,0 +1,684 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace mlir::qco { + +#define GEN_PASS_DEF_MERGESINGLEQUBITROTATIONGATES +#include "mlir/Dialect/QCO/Transforms/Passes.h.inc" + +namespace { + +/** + * @brief Pattern that merges consecutive rotation gates using quaternion + * multiplication. + */ +struct MergeSingleQubitRotationGatesPattern final + : OpInterfaceRewritePattern { + explicit MergeSingleQubitRotationGatesPattern(MLIRContext* context) + : OpInterfaceRewritePattern(context) {} + + /// Quaternion representation (w + xi + yj + zk) using MLIR Values. + struct Quaternion { + Value w; + Value x; + Value y; + Value z; + }; + + /// Axis of a single-axis rotation gate. + enum class RotationAxis : std::uint8_t { X, Y, Z }; + + /// Cached frequently-used constant Values. + struct Constants { + Value negOne; + Value zero; + Value one; + Value two; + Value eps; + Value pi; + }; + + /// Euler-angle triple for a U gate (theta, phi, lambda). + struct UOpAngles { + Value theta; + Value phi; + Value lambda; + }; + + /// Returns whether an operation is considered mergeable + static bool isMergeable(Operation* op) { + return isa(op); + } + + /// Checks if two gates a and b are mergeable via quaternion-based merging. + [[nodiscard]] static bool areQuaternionMergeable(Operation& a, Operation& b) { + return isMergeable(&a) && isMergeable(&b); + } + + /** + * @brief Returns the rotation axis for an RXOp, RYOp, or RZOp. + * + * @param op The operation to query + * @return The rotation axis, or std::nullopt if the operation is not + * RXOp, RYOp, or RZOp. + */ + static std::optional getRotationAxis(Operation* op) { + return TypeSwitch>(op) + .Case([](auto) { return RotationAxis::X; }) + .Case([](auto) { return RotationAxis::Y; }) + .Case([](auto) { return RotationAxis::Z; }) + .Default([](auto) { return std::nullopt; }); + } + + /** + * @brief Creates shared f64 arithmetic constants used throughout the pass. + * + * These constants are created once and reused across quaternion construction, + * Hamilton product, and Euler angle extraction to avoid redundant ops in the + * generated IR. + * + * @param loc Source location for the created operations + * @param rewriter Pattern rewriter for creating new operations + * @return A Constants struct with all pre-built constant ops + */ + static Constants createConstants(Location loc, PatternRewriter& rewriter) { + return { + .negOne = arith::ConstantFloatOp::create( + rewriter, loc, rewriter.getF64Type(), APFloat(-1.0)), + .zero = arith::ConstantFloatOp::create( + rewriter, loc, rewriter.getF64Type(), APFloat(0.0)), + .one = arith::ConstantFloatOp::create( + rewriter, loc, rewriter.getF64Type(), APFloat(1.0)), + .two = arith::ConstantFloatOp::create( + rewriter, loc, rewriter.getF64Type(), APFloat(2.0)), + // Tolerance for gimbal-lock detection in quaternion-to-Euler + // conversion. Value from reference implementation: + // https://github.com/evbernardes/quaternion_to_euler/blob/main/euler_from_quat.py + .eps = arith::ConstantFloatOp::create( + rewriter, loc, rewriter.getF64Type(), APFloat(1e-12)), + .pi = arith::ConstantFloatOp::create( + rewriter, loc, rewriter.getF64Type(), APFloat(std::numbers::pi)), + }; + } + + /** + * @brief Normalizes an angle to the range [-PI, PI]. + * + * Uses floor-based modular arithmetic: + * normalize(a) = a - floor((a + π) / 2π) * 2π + * + * @param angle The angle value to normalize + * @param loc Source location for the created operations + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return The normalized angle value + */ + static Value normalizeAngle(Value angle, Location loc, + const Constants& constants, + PatternRewriter& rewriter) { + auto twoPi = + arith::MulFOp::create(rewriter, loc, constants.two, constants.pi); + auto shifted = arith::AddFOp::create(rewriter, loc, angle, constants.pi); + auto divided = arith::DivFOp::create(rewriter, loc, shifted, twoPi); + auto floored = math::FloorOp::create(rewriter, loc, divided); + auto multiple = arith::MulFOp::create(rewriter, loc, floored, twoPi); + return arith::SubFOp::create(rewriter, loc, angle, multiple); + } + + /** + * @brief Converts a single-axis rotation to quaternion representation. + * + * Uses half-angle formulas: + * RX(a) = Q(cos(a/2), sin(a/2), 0, 0) + * RY(a) = Q(cos(a/2), 0, sin(a/2), 0) + * RZ(a) = Q(cos(a/2), 0, 0, sin(a/2)) + * + * @see + * https://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles + * @param angle The rotation angle + * @param axis The rotation axis (X, Y, or Z) + * @param loc Location in the IR + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return Quaternion representing the rotation + */ + static Quaternion createAxisQuaternion(Value angle, RotationAxis axis, + Location loc, + const Constants& constants, + PatternRewriter& rewriter) { + auto half = arith::DivFOp::create(rewriter, loc, angle, constants.two); + // cos(angle/2) + auto cos = math::CosOp::create(rewriter, loc, half); + // sin(angle/2) + auto sin = math::SinOp::create(rewriter, loc, half); + + switch (axis) { + case RotationAxis::X: + return {.w = cos, .x = sin, .y = constants.zero, .z = constants.zero}; + case RotationAxis::Y: + return {.w = cos, .x = constants.zero, .y = sin, .z = constants.zero}; + case RotationAxis::Z: + return {.w = cos, .x = constants.zero, .y = constants.zero, .z = sin}; + } + } + + /** + * @brief Converts a ZYZ Euler angle decomposition to quaternion. + * + * U(theta, phi, lambda) uses ZYZ decomposition: RZ(lambda) -> RY(theta) -> + * RZ(phi). + * + * When composing rotations, quaternion multiplication follows matrix + * multiplication order (right-to-left), which is the reverse of the + * application sequence: + * Sequential application: RZ(lambda), then RY(theta), then RZ(phi) + * Quaternion product: qPhi * qTheta * qLambda + * + * @note U is defined as P(phi)*RY(theta)*P(lambda), which equals + * e^{i*(phi+lambda)/2} * RZ(phi)*RY(theta)*RZ(lambda). + * Since quaternions represent SU(2), this pass works with the SU(2) part + * RZ(phi)*RY(theta)*RZ(lambda) and tracks the factored-out global phase + * (phi+lambda)/2 separately via globalPhaseOf. + * + * @param theta The Y-rotation angle + * @param phi The first Z-rotation angle + * @param lambda The second Z-rotation angle + * @param loc Location in the IR + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return Quaternion representing the ZYZ rotation + */ + static Quaternion quaternionFromZYZ(Value theta, Value phi, Value lambda, + Location loc, const Constants& constants, + PatternRewriter& rewriter) { + auto qTheta = + createAxisQuaternion(theta, RotationAxis::Y, loc, constants, rewriter); + auto qPhi = + createAxisQuaternion(phi, RotationAxis::Z, loc, constants, rewriter); + auto qLambda = + createAxisQuaternion(lambda, RotationAxis::Z, loc, constants, rewriter); + + // qPhi * qTheta * qLambda (multiplication in reverse order!) + auto temp = hamiltonProduct(qPhi, qTheta, loc, rewriter); + return hamiltonProduct(temp, qLambda, loc, rewriter); + } + + /** + * @brief Converts a UOp to quaternion representation. + * + * U(theta, phi, lambda) is decomposed via ZYZ Euler angles. + * + * @note Global phase is discarded; see quaternionFromZYZ for details. + * + * @param op The UOp to convert + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return Quaternion representing the UOp + */ + static Quaternion quaternionFromUOp(UOp op, const Constants& constants, + PatternRewriter& rewriter) { + return quaternionFromZYZ(op.getParameter(0), op.getParameter(1), + op.getParameter(2), op->getLoc(), constants, + rewriter); + } + + /** + * @brief Converts a U2Op to quaternion representation. + * + * U2(phi, lambda) = U(pi / 2, phi, lambda), using ZYZ decomposition with + * theta = pi / 2. + * + * @note Global phase is discarded; see quaternionFromZYZ for details. + * + * @param op The U2Op to convert + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return Quaternion representing the U2Op + */ + static Quaternion quaternionFromU2Op(U2Op op, const Constants& constants, + PatternRewriter& rewriter) { + auto loc = op->getLoc(); + auto piHalf = + arith::DivFOp::create(rewriter, loc, constants.pi, constants.two); + return quaternionFromZYZ(piHalf, op.getParameter(0), op.getParameter(1), + loc, constants, rewriter); + } + + /** + * @brief Converts an ROp to quaternion representation. + * + * R(theta, phi) represents a rotation by theta around axis + * (cos(phi), sin(phi), 0) in the XY plane: + * Q(cos(theta / 2), sin(theta / 2) * cos(phi), sin(theta / 2) * sin(phi), 0) + * + * @param op The ROp to convert + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return Quaternion representing the ROp + */ + static Quaternion quaternionFromROp(ROp op, const Constants& constants, + PatternRewriter& rewriter) { + auto loc = op->getLoc(); + auto theta = op.getParameter(0); + auto phi = op.getParameter(1); + + auto halfTheta = arith::DivFOp::create(rewriter, loc, theta, constants.two); + auto cosHalf = math::CosOp::create(rewriter, loc, halfTheta); + auto sinHalf = math::SinOp::create(rewriter, loc, halfTheta); + auto cosPhi = math::CosOp::create(rewriter, loc, phi); + auto sinPhi = math::SinOp::create(rewriter, loc, phi); + + auto x = arith::MulFOp::create(rewriter, loc, sinHalf, cosPhi); + auto y = arith::MulFOp::create(rewriter, loc, sinHalf, sinPhi); + + return {.w = cosHalf, .x = x, .y = y, .z = constants.zero}; + } + + /** + * @brief Converts a rotation gate to quaternion representation. + * + * @note Global phase is discarded; see quaternionFromZYZ for details. + * + * @param op The rotation gate to convert (RXOp, RYOp, RZOp, POp, ROp, U2Op, + * UOp) + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return Quaternion representing the rotation gate + */ + static Quaternion quaternionFromRotation(UnitaryOpInterface op, + const Constants& constants, + PatternRewriter& rewriter) { + // Single-axis rotations (RX, RY, RZ, P) share the same conversion pattern + if (auto axis = getRotationAxis(op.getOperation())) { + return createAxisQuaternion(op.getParameter(0), *axis, op->getLoc(), + constants, rewriter); + } + + // Multi-parameter gates each need their own conversion + return TypeSwitch(op.getOperation()) + .Case( + [&](ROp o) { return quaternionFromROp(o, constants, rewriter); }) + .Case( + [&](U2Op o) { return quaternionFromU2Op(o, constants, rewriter); }) + .Case( + [&](UOp o) { return quaternionFromUOp(o, constants, rewriter); }) + .Default([](auto) -> Quaternion { + llvm_unreachable("Unsupported operation type"); + }); + } + + /** + * @brief Returns the global phase contribution of a rotation gate. + * + * Rotation gates can be factored as U = e^{i * phase} * SU(2), where SU(2) + * is the quaternion-representable part and phase is the global phase. This + * function returns the global phase for each gate type: + * + * - RX, RY, RZ, R -> none (already SU(2), no global phase) + * - P(theta) -> theta / 2 (P = e^{i * theta / 2} * RZ(theta)) + * - U(theta, phi, lambda) -> (phi + lambda) / 2 + * - U2(phi, lambda) -> (phi + lambda) / 2 + * + * @param op The rotation gate to query + * @param constants Pre-created arithmetic constants + * @param loc Source location for created operations + * @param rewriter Pattern rewriter for creating new operations + * @return The global phase as a Value, or std::nullopt for SU(2) gates + */ + static std::optional globalPhaseOf(UnitaryOpInterface op, + const Constants& constants, + Location loc, + PatternRewriter& rewriter) { + return TypeSwitch>(op.getOperation()) + .Case( + [&](auto) -> std::optional { return std::nullopt; }) + .Case([&](auto) -> std::optional { + return arith::DivFOp::create(rewriter, loc, op.getParameter(0), + constants.two); + }) + .Case([&](auto) -> std::optional { + // phi is at different indexes for UOp and U2Op + auto phiIdx = isa(op.getOperation()) ? 1U : 0U; + auto sum = + arith::AddFOp::create(rewriter, loc, op.getParameter(phiIdx), + op.getParameter(phiIdx + 1)); + return arith::DivFOp::create(rewriter, loc, sum, constants.two); + }) + .Default([](auto) -> std::optional { + llvm_unreachable("Unsupported operation type"); + }); + } + + /** + * @brief Checks if this op is the start of a mergeable chain. + * + * A chain start is a mergeable op whose qubit input does NOT come from + * a chain-compatible predecessor. This ensures the greedy rewriter only + * triggers the rewrite at chain heads, building the maximal chain in one + * shot regardless of worklist order. + * + * @param op The operation to check + * @return True if this op is the start of a chain + */ + static bool isChainStart(UnitaryOpInterface op) { + if (!isMergeable(op.getOperation())) { + return false; + } + auto input = op.getInputQubit(0); + auto* defOp = input.getDefiningOp(); + return defOp == nullptr || + !areQuaternionMergeable(*defOp, *op.getOperation()); + } + + /** + * @brief Collects a chain of consecutive mergeable gates. + * + * Walks forward via single-use SSA edges. Breaks when the next operation is + * not considered as mergeable. + * + * @param start The chain head (must satisfy isChainStart) + * @return The chain of operations in circuit order (first applied to last) + */ + static SmallVector + collectChain(UnitaryOpInterface start) { + SmallVector chain = {start}; + auto current = start; + while (true) { + auto* userOp = *current->getUsers().begin(); + if (!areQuaternionMergeable(*current.getOperation(), *userOp)) { + break; + } + current = chain.emplace_back(cast(userOp)); + } + return chain; + } + + /** + * @brief Computes the Hamilton product of two quaternions (q1 * q2). + * + * For q1 = w1 + x1*i + y1*j + z1*k and q2 = w2 + x2*i + y2*j + z2*k: + * + * q1 * q2 = (w1w2 - x1x2 - y1y2 - z1z2) + * + (w1x2 + x1w2 + y1z2 - z1y2) * i + * + (w1y2 - x1z2 + y1w2 + z1x2) * j + * + (w1z2 + x1y2 - y1x2 + z1w2) * k + * + * @see https://en.wikipedia.org/wiki/Quaternion#Hamilton_product + * @param q1 The first quaternion + * @param q2 The second quaternion + * @param loc Location in the IR + * @param rewriter Pattern rewriter for creating new operations + * @return Product quaternion + */ + static Quaternion hamiltonProduct(Quaternion q1, Quaternion q2, Location loc, + PatternRewriter& rewriter) { + // wRes = w1w2 - x1x2 - y1y2 - z1z2 + auto w1w2 = arith::MulFOp::create(rewriter, loc, q1.w, q2.w); + auto x1x2 = arith::MulFOp::create(rewriter, loc, q1.x, q2.x); + auto y1y2 = arith::MulFOp::create(rewriter, loc, q1.y, q2.y); + auto z1z2 = arith::MulFOp::create(rewriter, loc, q1.z, q2.z); + auto wTemp1 = arith::SubFOp::create(rewriter, loc, w1w2, x1x2); + auto wTemp2 = arith::SubFOp::create(rewriter, loc, wTemp1, y1y2); + auto wRes = arith::SubFOp::create(rewriter, loc, wTemp2, z1z2); + + // xRes = w1x2 + x1w2 + y1z2 - z1y2 + auto w1x2 = arith::MulFOp::create(rewriter, loc, q1.w, q2.x); + auto x1w2 = arith::MulFOp::create(rewriter, loc, q1.x, q2.w); + auto y1z2 = arith::MulFOp::create(rewriter, loc, q1.y, q2.z); + auto z1y2 = arith::MulFOp::create(rewriter, loc, q1.z, q2.y); + auto xTemp1 = arith::AddFOp::create(rewriter, loc, w1x2, x1w2); + auto xTemp2 = arith::AddFOp::create(rewriter, loc, xTemp1, y1z2); + auto xRes = arith::SubFOp::create(rewriter, loc, xTemp2, z1y2); + + // yRes = w1y2 - x1z2 + y1w2 + z1x2 + auto w1y2 = arith::MulFOp::create(rewriter, loc, q1.w, q2.y); + auto x1z2 = arith::MulFOp::create(rewriter, loc, q1.x, q2.z); + auto y1w2 = arith::MulFOp::create(rewriter, loc, q1.y, q2.w); + auto z1x2 = arith::MulFOp::create(rewriter, loc, q1.z, q2.x); + auto yTemp1 = arith::SubFOp::create(rewriter, loc, w1y2, x1z2); + auto yTemp2 = arith::AddFOp::create(rewriter, loc, yTemp1, y1w2); + auto yRes = arith::AddFOp::create(rewriter, loc, yTemp2, z1x2); + + // zRes = w1z2 + x1y2 - y1x2 + z1w2 + auto w1z2 = arith::MulFOp::create(rewriter, loc, q1.w, q2.z); + auto x1y2 = arith::MulFOp::create(rewriter, loc, q1.x, q2.y); + auto y1x2 = arith::MulFOp::create(rewriter, loc, q1.y, q2.x); + auto z1w2 = arith::MulFOp::create(rewriter, loc, q1.z, q2.w); + auto zTemp1 = arith::AddFOp::create(rewriter, loc, w1z2, x1y2); + auto zTemp2 = arith::SubFOp::create(rewriter, loc, zTemp1, y1x2); + auto zRes = arith::AddFOp::create(rewriter, loc, zTemp2, z1w2); + + return {.w = wRes, .x = xRes, .y = yRes, .z = zRes}; + } + + /** + * @brief Extracts ZYZ Euler angles from a unit quaternion. + * + * For unit quaternion q = w + x * i + y * j + z * k, extracts UOp parameters: + * + * - alpha = atan2(z, w) + atan2(-x, y) + * - beta = acos(2 * (w^2 + z^2) - 1) + * - gamma = atan2(z, w) - atan2(-x, y) + * + * Based on Bernardes & Viollet (2022), simplified for unit quaternions and + * proper ZYZ Euler angles (Chapter 3.3): + * https://doi.org/10.1371/journal.pone.0276302 + * + * Reference implementation: + * https://github.com/evbernardes/quaternion_to_euler + * SymPy also implements this paper: + * https://docs.sympy.org/latest/modules/algebras.html#sympy.algebras.Quaternion.to_euler + * + * @note Floating-point errors may accumulate when merging many gates. + * @param q The quaternion to convert + * @param loc Source location for the created operations + * @param constants Pre-created arithmetic constants + * @param rewriter Pattern rewriter for creating new operations + * @return UOpAngles {theta, phi, lambda} suitable for UOp::create + */ + static UOpAngles anglesFromQuaternion(Quaternion q, Location loc, + const Constants& constants, + PatternRewriter& rewriter) { + // Calculate angle beta (for y-rotation) + // beta = acos(2 * (w^2 + z^2) - 1) + // NOTE: the term (2 * (w^2 + z^2) - 1) is clamped to [-1, 1], + // otherwise acos could produce NaN. + auto ww = arith::MulFOp::create(rewriter, loc, q.w, q.w); + auto zz = arith::MulFOp::create(rewriter, loc, q.z, q.z); + auto bTemp1 = arith::AddFOp::create(rewriter, loc, ww, zz); + auto bTemp2 = arith::MulFOp::create(rewriter, loc, constants.two, bTemp1); + auto bTemp3 = arith::SubFOp::create(rewriter, loc, bTemp2, constants.one); + auto clampedLow = + arith::MaximumFOp::create(rewriter, loc, bTemp3, constants.negOne); + auto clamped = + arith::MinimumFOp::create(rewriter, loc, clampedLow, constants.one); + auto beta = math::AcosOp::create(rewriter, loc, clamped); + + // intermediates to check for gimbal lock (|beta| and |beta - PI|) + auto absBeta = math::AbsFOp::create(rewriter, loc, beta); + auto betaMinusPi = arith::SubFOp::create(rewriter, loc, beta, constants.pi); + auto absBetaMinusPi = math::AbsFOp::create(rewriter, loc, betaMinusPi); + + // safe1 = beta not within boundary eps around 0: + // |beta| >= eps + auto safe1 = arith::CmpFOp::create(rewriter, loc, arith::CmpFPredicate::OGE, + absBeta, constants.eps); + // safe2 = beta not within boundary eps around PI: |beta - PI| >= eps + auto safe2 = arith::CmpFOp::create(rewriter, loc, arith::CmpFPredicate::OGE, + absBetaMinusPi, constants.eps); + // is safe (not in gimbal lock) when both hold (safe1 AND safe2) + auto safe = arith::AndIOp::create(rewriter, loc, safe1, safe2); + + // intermediate angles for z-rotations alpha and gamma + // theta+ = atan2(z, w) + // theta- = atan2(-x, y) + auto xMinus = arith::NegFOp::create(rewriter, loc, q.x); + auto thetaPlus = math::Atan2Op::create(rewriter, loc, q.z, q.w); + auto thetaMinus = math::Atan2Op::create(rewriter, loc, xMinus, q.y); + + // intermediate angles for gimbal lock cases + // twoTheta+ = 2 * theta+ + // twoTheta- = 2 * theta- + auto twoThetaPlus = + arith::MulFOp::create(rewriter, loc, constants.two, thetaPlus); + auto twoThetaMinus = + arith::MulFOp::create(rewriter, loc, constants.two, thetaMinus); + + // Safe Case (no gimbal lock): + // alphaSafe = theta+ + theta- + // gammaSafe = theta+ - theta- + auto alphaSafe = + arith::AddFOp::create(rewriter, loc, thetaPlus, thetaMinus); + auto gammaSafe = + arith::SubFOp::create(rewriter, loc, thetaPlus, thetaMinus); + + // Unsafe Case (gimbal lock): + // when beta = 0 then alpha = 2 * (atan2(z, w)) + // when beta = PI then alpha = 2 * (atan2(-x, y)) + // gamma is set to zero in both cases + auto alphaUnsafe = arith::SelectOp::create(rewriter, loc, safe1, + twoThetaMinus, twoThetaPlus); + + // choose correct alpha and gamma whether safe or not + auto alpha = + arith::SelectOp::create(rewriter, loc, safe, alphaSafe, alphaUnsafe); + auto gamma = + arith::SelectOp::create(rewriter, loc, safe, gammaSafe, constants.zero); + + // normalize alpha and gamma to [-PI, PI] since they are sums/differences + // of atan2 results and can exceed that range + auto alphaNorm = normalizeAngle(alpha, loc, constants, rewriter); + auto gammaNorm = normalizeAngle(gamma, loc, constants, rewriter); + + return {.theta = beta.getResult(), .phi = alphaNorm, .lambda = gammaNorm}; + } + + /** + * @brief Matches and merges a chain of consecutive rotation gates. + * + * Detects the full chain of mergeable operations, folds their quaternions + * via Hamilton product, and emits a single UOp. + * + * @param op The operation to match (only chain heads trigger the rewrite) + * @param rewriter Pattern rewriter for applying transformations + * @return success() if operations were merged, failure() otherwise + */ + LogicalResult matchAndRewrite(UnitaryOpInterface op, + PatternRewriter& rewriter) const override { + if (!isChainStart(op)) { + return failure(); + } + + auto chain = collectChain(op); + if (chain.size() < 2) { + return failure(); + } + + // Emit all helper ops at the chain tail so the merged UOp is placed + // adjacent to the last gate it replaces. + OpBuilder::InsertionGuard guard(rewriter); + rewriter.setInsertionPointAfter(chain.back().getOperation()); + + auto loc = op->getLoc(); + auto constants = createConstants(loc, rewriter); + + // Initialize accumulators from the first operation + auto qAccum = quaternionFromRotation(chain.front(), constants, rewriter); + auto phaseAccum = globalPhaseOf(chain.front(), constants, loc, rewriter); + + // Fold remaining operations via Hamilton product + for (auto chainOp : llvm::drop_begin(chain)) { + auto qi = quaternionFromRotation(chainOp, constants, rewriter); + qAccum = hamiltonProduct(qi, qAccum, loc, rewriter); + + if (auto phase = globalPhaseOf(chainOp, constants, loc, rewriter)) { + phaseAccum = phaseAccum ? Value(arith::AddFOp::create( + rewriter, loc, *phaseAccum, *phase)) + : phase; + } + + // Bypass each tail operation + rewriter.replaceOp(chainOp, chainOp.getInputQubit(0)); + } + + // Extract Euler angles from merged quaternion + auto [theta, phi, lambda] = + anglesFromQuaternion(qAccum, loc, constants, rewriter); + + // Emit global phase correction: + // The synthesized UOp carries an intrinsic phase + // outPhase = (phi + lambda) / 2 that must always be compensated. + // correction = totalInputPhase - outPhase + auto phiPlusLambda = arith::AddFOp::create(rewriter, loc, phi, lambda); + auto outPhase = + arith::DivFOp::create(rewriter, loc, phiPlusLambda, constants.two); + auto inputPhase = phaseAccum.value_or(constants.zero); + auto correction = + arith::SubFOp::create(rewriter, loc, inputPhase, outPhase); + GPhaseOp::create(rewriter, loc, correction.getResult()); + + // Replace the head operation with the merged UOp + rewriter.replaceOpWithNewOp( + chain.front(), chain.front().getInputQubit(0), theta, phi, lambda); + + return success(); + } +}; + +/** + * @brief Pass that merges consecutive rotation gates using quaternion + * multiplication. + */ +struct MergeSingleQubitRotationGates final + : impl::MergeSingleQubitRotationGatesBase { + using impl::MergeSingleQubitRotationGatesBase< + MergeSingleQubitRotationGates>::MergeSingleQubitRotationGatesBase; + +protected: + void runOnOperation() override { + auto op = getOperation(); + auto* ctx = &getContext(); + + RewritePatternSet patterns(ctx); + patterns.add(patterns.getContext()); + + if (failed(applyPatternsGreedily(op, std::move(patterns)))) { + signalPassFailure(); + } + } +}; + +} // namespace + +} // namespace mlir::qco diff --git a/mlir/lib/Dialect/QCO/Utils/Driver.cpp b/mlir/lib/Dialect/QCO/Utils/Qubits.cpp similarity index 95% rename from mlir/lib/Dialect/QCO/Utils/Driver.cpp rename to mlir/lib/Dialect/QCO/Utils/Qubits.cpp index 7f96a17003..d448a0609d 100644 --- a/mlir/lib/Dialect/QCO/Utils/Driver.cpp +++ b/mlir/lib/Dialect/QCO/Utils/Qubits.cpp @@ -8,8 +8,9 @@ * Licensed under the MIT License */ +#include "mlir/Dialect/QCO/Utils/Qubits.h" + #include "mlir/Dialect/QCO/IR/QCODialect.h" -#include "mlir/Dialect/QCO/Utils/Drivers.h" #include @@ -58,13 +59,13 @@ void Qubits::remove(TypedValue q) { hardwareToValue_.erase(index); } -TypedValue Qubits::getProgramQubit(std::size_t index) { +TypedValue Qubits::getProgramQubit(std::size_t index) const { assert(programToValue_.contains(index)); return programToValue_.lookup(index); } -TypedValue Qubits::getHardwareQubit(std::size_t index) { +TypedValue Qubits::getHardwareQubit(std::size_t index) const { assert(hardwareToValue_.contains(index)); return hardwareToValue_.lookup(index); } -} // namespace mlir::qco +} // namespace mlir::qco \ No newline at end of file diff --git a/mlir/lib/Dialect/QCO/Utils/WireIterator.cpp b/mlir/lib/Dialect/QCO/Utils/WireIterator.cpp index c9f60b1eb3..0aa61634d3 100644 --- a/mlir/lib/Dialect/QCO/Utils/WireIterator.cpp +++ b/mlir/lib/Dialect/QCO/Utils/WireIterator.cpp @@ -25,7 +25,7 @@ namespace mlir::qco { mlir::Value WireIterator::qubit() const { // A sink/deallocation doesn't have an OpResult. - if (op_ != nullptr && mlir::isa(op_)) { + if (op_ != nullptr && isa(op_)) { return nullptr; } return qubit_; @@ -42,12 +42,12 @@ void WireIterator::forward() { op_ = *(qubit_.getUsers().begin()); // A sink op defines the end of the qubit wire (dynamic and static). - if (mlir::isa(op_)) { + if (isa(op_)) { isSentinel_ = true; return; } - if (!(mlir::isa(op_))) { + if (!(isa(op_))) { // Find the output from the input qubit SSA value. mlir::TypeSwitch(op_) .Case([&](UnitaryOpInterface op) { @@ -71,14 +71,14 @@ void WireIterator::backward() { // For sinks/deallocations, qubit_ is an OpOperand. Hence, only get the // def-op. - if (mlir::isa(op_)) { + if (isa(op_)) { op_ = qubit_.getDefiningOp(); return; } // Allocations or static definitions define the start of the qubit wire. // Consequently, stop and early exit. - if (mlir::isa(op_)) { + if (isa(op_)) { return; } diff --git a/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp b/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp index 35800e7607..0b3434a705 100644 --- a/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp +++ b/mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp @@ -12,11 +12,8 @@ #include "mlir/Dialect/QIR/Utils/QIRUtils.h" -#include -#include #include #include -#include #include #include #include @@ -51,7 +48,7 @@ QIRProgramBuilder::QIRProgramBuilder(MLIRContext* context) void QIRProgramBuilder::initialize() { // Set insertion point to the module body - setInsertionPointToStart(module.getBody()); + setInsertionPointToStart(cast(module).getBody()); // Create main function: () -> i64 auto funcType = LLVM::LLVMFunctionType::get(getI64Type(), {}); @@ -199,6 +196,17 @@ SmallVector QIRProgramBuilder::allocQubitRegister(const int64_t size) { return qubits; } +QIRProgramBuilder::Bit +QIRProgramBuilder::ClassicalRegister::operator[](const int64_t index) const { + if (index < 0 || index >= size) { + const std::string msg = "Bit index " + std::to_string(index) + + " out of bounds for register '" + name + + "' of size " + std::to_string(size); + llvm::reportFatalUsageError(msg.c_str()); + } + return {.registerName = name, .registerSize = size, .registerIndex = index}; +} + QIRProgramBuilder::ClassicalRegister QIRProgramBuilder::allocClassicalBitRegister(const int64_t size, const std::string& name) { @@ -666,20 +674,11 @@ void QIRProgramBuilder::generateOutputRecording() { setInsertionPoint(outputBlock->getTerminator()); if (!resultPtrs.empty()) { - // Sort result pointers for deterministic output - llvm::SmallVector> sortedPtrs; - for (const auto& [index, resultPtr] : resultPtrs) { - sortedPtrs.emplace_back(index, resultPtr); - } - llvm::sort(sortedPtrs, - [](const auto& a, const auto& b) { return a.first < b.first; }); - - // Create output recording for each result pointer auto fnSig = LLVM::LLVMFunctionType::get(voidType, {ptrType, ptrType}); auto fnDec = getOrCreateFunctionDeclaration(*this, module, QIR_RECORD_OUTPUT, fnSig); - - for (const auto& [index, ptr] : sortedPtrs) { + // Create output recording for each result pointer + for (const auto& [index, ptr] : resultPtrs) { auto label = createResultLabel(*this, module, "__unnamed__" + std::to_string(index)) .getResult(); @@ -688,24 +687,14 @@ void QIRProgramBuilder::generateOutputRecording() { } if (!resultArrays.empty()) { - // Sort registers by name for deterministic output - SmallVector> sortedArrays; - for (auto& [name, results] : resultArrays) { - sortedArrays.emplace_back(name, results); - } - llvm::sort(sortedArrays, - [](const auto& a, const auto& b) { return a.first < b.first; }); - auto fnSig = LLVM::LLVMFunctionType::get(voidType, {getI64Type(), ptrType, ptrType}); auto fnDec = getOrCreateFunctionDeclaration(*this, module, QIR_ARRAY_RECORD_OUTPUT, fnSig); - // Create output recording for each register - for (auto& [name, results] : sortedArrays) { + for (const auto& [name, results] : resultArrays) { auto size = results.getDefiningOp().getArraySize(); auto label = createResultLabel(*this, module, name).getResult(); - LLVM::CallOp::create(*this, fnDec, ValueRange{size, results, label}); } } @@ -753,17 +742,17 @@ OwningOpRef QIRProgramBuilder::finalize() { LLVM::CallOp::create(*this, dec, ValueRange{size, array}); } - auto mainFuncOp = llvm::cast(mainFunc); + auto mainFuncOp = cast(mainFunc); setQIRAttributes(mainFuncOp, metadata_); isFinalized = true; - return module; + return cast(module); } OwningOpRef QIRProgramBuilder::build( MLIRContext* context, - const llvm::function_ref& buildFunc) { + const function_ref& buildFunc) { QIRProgramBuilder builder(context); builder.initialize(); buildFunc(builder); diff --git a/mlir/lib/Dialect/QIR/Transforms/QIRCleanup.cpp b/mlir/lib/Dialect/QIR/Transforms/QIRCleanup.cpp index f1188ce19a..64e3d1dd13 100644 --- a/mlir/lib/Dialect/QIR/Transforms/QIRCleanup.cpp +++ b/mlir/lib/Dialect/QIR/Transforms/QIRCleanup.cpp @@ -12,8 +12,8 @@ #include "mlir/Dialect/QIR/Utils/QIRUtils.h" #include +#include #include -#include #include #include #include @@ -22,7 +22,6 @@ #include #include #include -#include #include #include @@ -33,20 +32,20 @@ namespace mlir::qir { #include "mlir/Dialect/QIR/Transforms/Passes.h.inc" [[nodiscard]] static StringAttr getMetadataKey(const Attribute attr) { - auto pair = llvm::dyn_cast(attr); + auto pair = dyn_cast(attr); if (!pair || pair.size() != 2) { return {}; } - auto key = llvm::dyn_cast(pair[0]); - if (!key || !llvm::isa(pair[1])) { + auto key = dyn_cast(pair[0]); + if (!key || !isa(pair[1])) { return {}; } return key; } -[[nodiscard]] static llvm::StringRef getCalleeName(LLVM::CallOp callOp) { +[[nodiscard]] static StringRef getCalleeName(LLVM::CallOp callOp) { auto calleeAttr = callOp.getCalleeAttr(); - auto flatRef = llvm::dyn_cast_or_null(calleeAttr); + auto flatRef = dyn_cast_or_null(calleeAttr); if (!flatRef) { return {}; } @@ -105,9 +104,9 @@ static void normalizeQIRMetadata(ModuleOp module) { continue; } if (key.getValue() == "required_num_qubits") { - requiredNumQubitsAttr = llvm::cast(attr); + requiredNumQubitsAttr = cast(attr); } else if (key.getValue() == "required_num_results") { - requiredNumResultsAttr = llvm::cast(attr); + requiredNumResultsAttr = cast(attr); } } @@ -166,7 +165,7 @@ struct RemoveDeadQubitArrayPair final : OpRewritePattern { LLVM::CallOp allocCall = nullptr; for (Operation* user : allocaOp.getResult().getUsers()) { - auto callOp = llvm::dyn_cast(user); + auto callOp = dyn_cast(user); if (!callOp) { return failure(); } diff --git a/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp b/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp index b698c06dc7..04be35f3e6 100644 --- a/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp +++ b/mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp @@ -13,7 +13,6 @@ #include "mlir/Dialect/QIR/Utils/QIRMetadata.h" #include -#include #include #include #include @@ -63,7 +62,7 @@ void setQIRAttributes(LLVM::LLVMFuncOp& main, const QIRMetadata& metadata) { } OpBuilder builder(main.getBody()); - llvm::SmallVector attributes; + SmallVector attributes; // Core QIR attributes attributes.emplace_back(builder.getStringAttr("entry_point")); diff --git a/mlir/lib/Dialect/QTensor/IR/Operations/AllocOp.cpp b/mlir/lib/Dialect/QTensor/IR/Operations/AllocOp.cpp index 05978b6f36..61e60eccfb 100644 --- a/mlir/lib/Dialect/QTensor/IR/Operations/AllocOp.cpp +++ b/mlir/lib/Dialect/QTensor/IR/Operations/AllocOp.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include diff --git a/mlir/lib/Dialect/QTensor/IR/Operations/ExtractOp.cpp b/mlir/lib/Dialect/QTensor/IR/Operations/ExtractOp.cpp index a899d3ae41..04d6b834c7 100644 --- a/mlir/lib/Dialect/QTensor/IR/Operations/ExtractOp.cpp +++ b/mlir/lib/Dialect/QTensor/IR/Operations/ExtractOp.cpp @@ -14,7 +14,6 @@ #include #include #include -#include using namespace mlir; using namespace mlir::qtensor; diff --git a/mlir/lib/Dialect/QTensor/IR/Operations/InsertOp.cpp b/mlir/lib/Dialect/QTensor/IR/Operations/InsertOp.cpp index adeac1cb8d..9ec4bd5d07 100644 --- a/mlir/lib/Dialect/QTensor/IR/Operations/InsertOp.cpp +++ b/mlir/lib/Dialect/QTensor/IR/Operations/InsertOp.cpp @@ -11,7 +11,6 @@ #include "mlir/Dialect/QTensor/IR/QTensorOps.h" #include "mlir/Dialect/QTensor/IR/QTensorUtils.h" -#include #include #include #include @@ -19,7 +18,6 @@ #include #include #include -#include using namespace mlir; using namespace mlir::qtensor; @@ -69,7 +67,7 @@ static ExtractOp findMatchingExtractInTensorChain(InsertOp insertOp) { } while (auto* definingOp = current.getDefiningOp()) { - if (auto nestedInsertOp = llvm::dyn_cast(definingOp)) { + if (auto nestedInsertOp = dyn_cast(definingOp)) { auto nestedInsertIndex = nestedInsertOp.getIndex(); if (!getConstantIntValue(nestedInsertIndex)) { return nullptr; @@ -81,7 +79,7 @@ static ExtractOp findMatchingExtractInTensorChain(InsertOp insertOp) { current = nestedInsertOp.getDest(); continue; } - if (auto extractOp = llvm::dyn_cast(definingOp)) { + if (auto extractOp = dyn_cast(definingOp)) { auto extractIndex = extractOp.getIndex(); if (!getConstantIntValue(extractIndex)) { return nullptr; diff --git a/mlir/lib/Dialect/QTensor/Transforms/ShrinkRegisters.cpp b/mlir/lib/Dialect/QTensor/Transforms/ShrinkRegisters.cpp index feb64d6e15..33146caed4 100644 --- a/mlir/lib/Dialect/QTensor/Transforms/ShrinkRegisters.cpp +++ b/mlir/lib/Dialect/QTensor/Transforms/ShrinkRegisters.cpp @@ -11,16 +11,14 @@ #include "mlir/Dialect/QTensor/IR/QTensorOps.h" #include "mlir/Dialect/QTensor/Transforms/Passes.h" -#include #include #include -#include #include #include #include #include #include -#include +#include #include #include @@ -45,7 +43,7 @@ namespace mlir::qtensor { * @brief Mark a single live index. */ [[nodiscard]] static LogicalResult markLiveIndex(const int64_t index, - llvm::BitVector& liveIndices) { + BitVector& liveIndices) { if (index < 0 || std::cmp_greater_equal(index, liveIndices.size())) { return failure(); } @@ -58,21 +56,21 @@ namespace mlir::qtensor { */ [[nodiscard]] static LogicalResult remapTensorOperand(Operation* op, Value from, Value to) { - if (auto extractOp = llvm::dyn_cast(op)) { + if (auto extractOp = dyn_cast(op)) { if (extractOp.getTensor() != from) { return failure(); } extractOp->setOperand(0, to); return success(); } - if (auto insertOp = llvm::dyn_cast(op)) { + if (auto insertOp = dyn_cast(op)) { if (insertOp.getDest() != from) { return failure(); } insertOp->setOperand(1, to); return success(); } - if (auto deallocOp = llvm::dyn_cast(op)) { + if (auto deallocOp = dyn_cast(op)) { if (deallocOp.getTensor() != from) { return failure(); } @@ -85,9 +83,8 @@ namespace mlir::qtensor { /** * @brief Walk alloc->dealloc and collect all touched indices. */ -[[nodiscard]] static LogicalResult collectLiveIndices(AllocOp allocOp, - llvm::BitVector& live, - DeallocOp& deallocOp) { +[[nodiscard]] static LogicalResult +collectLiveIndices(AllocOp allocOp, BitVector& live, DeallocOp& deallocOp) { auto tensor = allocOp.getResult(); while (true) { auto* user = getLinearTensorUser(tensor); @@ -95,7 +92,7 @@ namespace mlir::qtensor { return failure(); } - if (auto currentDealloc = llvm::dyn_cast(user)) { + if (auto currentDealloc = dyn_cast(user)) { if (currentDealloc.getTensor() != tensor) { return failure(); } @@ -103,7 +100,7 @@ namespace mlir::qtensor { return success(); } - if (auto extractOp = llvm::dyn_cast(user)) { + if (auto extractOp = dyn_cast(user)) { if (extractOp.getTensor() != tensor) { return failure(); } @@ -115,7 +112,7 @@ namespace mlir::qtensor { continue; } - if (auto insertOp = llvm::dyn_cast(user)) { + if (auto insertOp = dyn_cast(user)) { if (insertOp.getDest() != tensor) { return failure(); } @@ -147,7 +144,7 @@ struct ShrinkStaticQTensor final : OpRewritePattern { return failure(); } - llvm::BitVector live(static_cast(*oldSize), false); + BitVector live(static_cast(*oldSize), false); DeallocOp oldDeallocOp{}; if (failed(collectLiveIndices(allocOp, live, oldDeallocOp))) { return failure(); @@ -157,8 +154,7 @@ struct ShrinkStaticQTensor final : OpRewritePattern { return failure(); } - llvm::SmallVector newIndexByOldIndex(static_cast(*oldSize), - -1); + SmallVector newIndexByOldIndex(static_cast(*oldSize), -1); int64_t newSize = 0; for (int64_t index = 0; index < *oldSize; ++index) { if (live.test(static_cast(index))) { @@ -184,7 +180,7 @@ struct ShrinkStaticQTensor final : OpRewritePattern { return failure(); } - if (auto deallocOp = llvm::dyn_cast(currentOp)) { + if (auto deallocOp = dyn_cast(currentOp)) { if (deallocOp != oldDeallocOp || deallocOp.getTensor() != oldTensor) { return failure(); } @@ -194,7 +190,7 @@ struct ShrinkStaticQTensor final : OpRewritePattern { break; } - if (auto extractOp = llvm::dyn_cast(currentOp)) { + if (auto extractOp = dyn_cast(currentOp)) { if (extractOp.getTensor() != oldTensor) { return failure(); } @@ -230,7 +226,7 @@ struct ShrinkStaticQTensor final : OpRewritePattern { continue; } - if (auto insertOp = llvm::dyn_cast(currentOp)) { + if (auto insertOp = dyn_cast(currentOp)) { if (insertOp.getDest() != oldTensor) { return failure(); } diff --git a/mlir/lib/Support/IRVerification.cpp b/mlir/lib/Support/IRVerification.cpp index db2799a533..eaac426f0a 100644 --- a/mlir/lib/Support/IRVerification.cpp +++ b/mlir/lib/Support/IRVerification.cpp @@ -12,7 +12,6 @@ #include "mlir/Dialect/QTensor/IR/QTensorUtils.h" -#include #include #include #include @@ -21,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -36,6 +34,7 @@ #include #include #include +#include #include #include @@ -119,9 +118,9 @@ struct StructuralOperationKey { }; /// Map to track value equivalence between two modules. -using ValueEquivalenceMap = llvm::DenseMap; +using ValueEquivalenceMap = DenseMap; -using OperationSet = llvm::DenseSet; +using OperationSet = DenseSet; struct InsertWrite { Value scalar; @@ -131,7 +130,7 @@ struct InsertWrite { struct InsertChainSummary { Value baseTensor; Value finalTensor; - llvm::SmallVector writes; + SmallVector writes; }; } // namespace @@ -154,13 +153,13 @@ static bool areIndexValuesEquivalent(Value lhs, Value rhs, } static bool isQTensorInsertOp(Operation* op) { - return llvm::isa(op); + return isa(op); } static bool isCommutableQTensorInsertDependency(Operation* dependent, Operation* dependency) { - auto dependentInsert = llvm::dyn_cast(dependent); - auto dependencyInsert = llvm::dyn_cast(dependency); + auto dependentInsert = dyn_cast(dependent); + auto dependencyInsert = dyn_cast(dependency); if (!dependentInsert || !dependencyInsert) { return false; } @@ -187,17 +186,16 @@ static Value getInsertChainBaseTensor(Value tensor, const OperationSet& group) { return current; } -static bool -summarizeInsertGroup(llvm::ArrayRef ops, - llvm::SmallVectorImpl& chains) { +static bool summarizeInsertGroup(ArrayRef ops, + SmallVectorImpl& chains) { OperationSet groupOps; for (Operation* op : ops) { groupOps.insert(op); } - llvm::DenseSet consumedInsertResults; + DenseSet consumedInsertResults; for (Operation* op : ops) { - auto insertOp = llvm::cast(op); + auto insertOp = cast(op); if (auto definingInsert = insertOp.getDest().getDefiningOp()) { if (groupOps.contains(definingInsert.getOperation())) { @@ -206,9 +204,9 @@ summarizeInsertGroup(llvm::ArrayRef ops, } } - llvm::DenseMap chainByBaseTensor; + DenseMap chainByBaseTensor; for (Operation* op : ops) { - auto insertOp = llvm::cast(op); + auto insertOp = cast(op); const Value baseTensor = getInsertChainBaseTensor(insertOp.getDest(), groupOps); @@ -242,7 +240,7 @@ summarizeInsertGroup(llvm::ArrayRef ops, } // Reordering writes to the same index is not semantics-preserving. - llvm::SmallVector seenIndices; + SmallVector seenIndices; for (const auto& write : chain.writes) { if (llvm::any_of(seenIndices, [&](Value seenIndex) { return qtensor::areEquivalentIndices(seenIndex, write.index); @@ -257,9 +255,9 @@ summarizeInsertGroup(llvm::ArrayRef ops, } static bool areInsertWritesEquivalentRec(const size_t lhsIdx, - llvm::ArrayRef lhsWrites, - llvm::ArrayRef rhsWrites, - llvm::SmallVectorImpl& rhsUsed, + ArrayRef lhsWrites, + ArrayRef rhsWrites, + SmallVectorImpl& rhsUsed, ValueEquivalenceMap& valueMap) { if (lhsIdx == lhsWrites.size()) { return true; @@ -290,13 +288,13 @@ static bool areInsertWritesEquivalentRec(const size_t lhsIdx, return false; } -static bool areInsertWritesEquivalent(llvm::ArrayRef lhsWrites, - llvm::ArrayRef rhsWrites, +static bool areInsertWritesEquivalent(ArrayRef lhsWrites, + ArrayRef rhsWrites, ValueEquivalenceMap& valueMap) { if (lhsWrites.size() != rhsWrites.size()) { return false; } - llvm::SmallVector rhsUsed(rhsWrites.size(), 0); + SmallVector rhsUsed(rhsWrites.size(), 0); return areInsertWritesEquivalentRec(0, lhsWrites, rhsWrites, rhsUsed, valueMap); } @@ -322,10 +320,11 @@ static bool areInsertChainsEquivalent(const InsertChainSummary& lhsChain, return true; } -static bool areInsertGroupsEquivalentRec( - const size_t lhsChainIdx, llvm::ArrayRef lhsChains, - llvm::ArrayRef rhsChains, - llvm::SmallVectorImpl& rhsChainUsed, ValueEquivalenceMap& valueMap) { +static bool areInsertGroupsEquivalentRec(const size_t lhsChainIdx, + ArrayRef lhsChains, + ArrayRef rhsChains, + SmallVectorImpl& rhsChainUsed, + ValueEquivalenceMap& valueMap) { if (lhsChainIdx == lhsChains.size()) { return true; } @@ -353,15 +352,15 @@ static bool areInsertGroupsEquivalentRec( return false; } -static bool areInsertGroupsEquivalent(llvm::ArrayRef lhsOps, - llvm::ArrayRef rhsOps, +static bool areInsertGroupsEquivalent(ArrayRef lhsOps, + ArrayRef rhsOps, ValueEquivalenceMap& valueMap) { if (lhsOps.size() != rhsOps.size()) { return false; } - llvm::SmallVector lhsChains; - llvm::SmallVector rhsChains; + SmallVector lhsChains; + SmallVector rhsChains; if (!summarizeInsertGroup(lhsOps, lhsChains) || !summarizeInsertGroup(rhsOps, rhsChains)) { return false; @@ -370,7 +369,7 @@ static bool areInsertGroupsEquivalent(llvm::ArrayRef lhsOps, return false; } - llvm::SmallVector rhsChainUsed(rhsChains.size(), 0); + SmallVector rhsChainUsed(rhsChains.size(), 0); return areInsertGroupsEquivalentRec(0, lhsChains, rhsChains, rhsChainUsed, valueMap); } @@ -408,8 +407,8 @@ template <> struct llvm::DenseMapInfo { } }; -static bool areFloatValuesNear(const llvm::APFloat& lhs, - const llvm::APFloat& rhs, const unsigned width) { +static bool areFloatValuesNear(const APFloat& lhs, const APFloat& rhs, + const unsigned width) { if (lhs.isNaN() || rhs.isNaN()) { return lhs.isNaN() && rhs.isNaN(); } @@ -443,8 +442,8 @@ static bool areConstantAttributesEquivalent(const Attribute& lhs, return true; } - if (auto lhsFloat = llvm::dyn_cast(lhs)) { - auto rhsFloat = llvm::dyn_cast(rhs); + if (auto lhsFloat = dyn_cast(lhs)) { + auto rhsFloat = dyn_cast(rhs); if (!rhsFloat) { return false; } @@ -465,8 +464,8 @@ static bool areOperationsEquivalent(Operation* lhs, Operation* rhs, } // Check arith::ConstantOp - if (auto lhsConst = llvm::dyn_cast(lhs)) { - auto rhsConst = llvm::dyn_cast(rhs); + if (auto lhsConst = dyn_cast(lhs)) { + auto rhsConst = dyn_cast(rhs); if (!rhsConst) { return false; } @@ -478,8 +477,8 @@ static bool areOperationsEquivalent(Operation* lhs, Operation* rhs, } // Check LLVM::ConstantOp - if (auto lhsConst = llvm::dyn_cast(lhs)) { - auto rhsConst = llvm::dyn_cast(rhs); + if (auto lhsConst = dyn_cast(lhs)) { + auto rhsConst = dyn_cast(rhs); if (!rhsConst) { return false; } @@ -490,8 +489,8 @@ static bool areOperationsEquivalent(Operation* lhs, Operation* rhs, } // Check LLVM::CallOp - if (auto lhsCall = llvm::dyn_cast(lhs)) { - auto rhsCall = llvm::dyn_cast(rhs); + if (auto lhsCall = dyn_cast(lhs)) { + auto rhsCall = dyn_cast(rhs); if (!rhsCall) { return false; } @@ -567,19 +566,19 @@ static bool hasOrderingConstraints(Operation* op) { // Symbol-defining operations (like function declarations) can be reordered if (op->hasTrait() || - llvm::isa(op)) { + isa(op)) { return false; } // Check for memory effects that enforce ordering - if (auto memInterface = llvm::dyn_cast(op)) { - llvm::SmallVector effects; + if (auto memInterface = dyn_cast(op)) { + SmallVector effects; memInterface.getEffects(effects); bool hasNonAllocFreeEffects = false; for (const auto& effect : effects) { // Allow operations with no effects or pure allocation/free effects - if (!llvm::isa( + if (!isa( effect.getEffect())) { hasNonAllocFreeEffects = true; break; @@ -596,17 +595,14 @@ static bool hasOrderingConstraints(Operation* op) { /// Build a dependence graph for operations. /// Returns a map from each operation to the set of operations it depends on. -llvm::DenseMap< - Operation*, - llvm::DenseSet< - Operation*>> static buildDependenceGraph(llvm::ArrayRef - ops) { - llvm::DenseMap> dependsOn; - llvm::DenseMap valueProducers; +DenseMap> static buildDependenceGraph( + ArrayRef ops) { + DenseMap> dependsOn; + DenseMap valueProducers; // Build value-to-producer map and dependence relationships for (Operation* op : ops) { - dependsOn[op] = llvm::DenseSet(); + dependsOn[op] = DenseSet(); // This operation depends on the producers of its operands for (const auto operand : op->getOperands()) { @@ -626,16 +622,15 @@ llvm::DenseMap< /// Partition operations into groups that can be compared as multisets. /// Operations in the same group are independent and can be reordered. -llvm::SmallVector> static partitionIndependentGroups(llvm::ArrayRef - ops) { - llvm::SmallVector> groups; +SmallVector> static partitionIndependentGroups( + ArrayRef ops) { + SmallVector> groups; if (ops.empty()) { return groups; } auto dependsOn = buildDependenceGraph(ops); - llvm::SmallVector currentGroup; + SmallVector currentGroup; for (auto* op : ops) { bool dependsOnCurrent = false; @@ -682,15 +677,15 @@ llvm::SmallVector lhsOps, - llvm::ArrayRef rhsOps) { +static bool areIndependentGroupsEquivalent(ArrayRef lhsOps, + ArrayRef rhsOps) { if (lhsOps.size() != rhsOps.size()) { return false; } // Build frequency maps for both groups - llvm::DenseMap lhsFrequencyMap; - llvm::DenseMap rhsFrequencyMap; + DenseMap lhsFrequencyMap; + DenseMap rhsFrequencyMap; for (auto* op : lhsOps) { lhsFrequencyMap[StructuralOperationKey(op)]++; @@ -734,8 +729,8 @@ static bool areBlocksEquivalent(Block& lhs, Block& rhs, } // Collect all operations - llvm::SmallVector lhsOps; - llvm::SmallVector rhsOps; + SmallVector lhsOps; + SmallVector rhsOps; for (Operation& op : lhs) { lhsOps.push_back(&op); @@ -784,7 +779,7 @@ static bool areBlocksEquivalent(Block& lhs, Block& rhs, // by trying all permutations (for small groups) or use a greedy approach // Use a simple greedy matching - llvm::DenseSet matchedRhs; + DenseSet matchedRhs; for (Operation* lhsOp : lhsGroup) { bool matched = false; for (Operation* rhsOp : rhsGroup) { diff --git a/mlir/lib/Support/Passes.cpp b/mlir/lib/Support/Passes.cpp index 0369056533..70a5a499d1 100644 --- a/mlir/lib/Support/Passes.cpp +++ b/mlir/lib/Support/Passes.cpp @@ -14,12 +14,11 @@ #include "mlir/Dialect/QIR/Transforms/Passes.h" #include "mlir/Dialect/QTensor/Transforms/Passes.h" -#include #include #include #include #include -#include +#include #include using namespace mlir; @@ -31,8 +30,8 @@ static void addSimplificationPasses(PassManager& pm) { static LogicalResult runWithPassManager(ModuleOp module, - const llvm::function_ref populatePasses, - const llvm::StringRef errorMessage) { + const function_ref populatePasses, + const StringRef errorMessage) { PassManager pm(module.getContext()); populatePasses(pm); if (pm.run(module).failed()) { diff --git a/mlir/lib/Support/PrettyPrinting.cpp b/mlir/lib/Support/PrettyPrinting.cpp index c8323ba848..5ce09f58a7 100644 --- a/mlir/lib/Support/PrettyPrinting.cpp +++ b/mlir/lib/Support/PrettyPrinting.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -29,7 +30,7 @@ constexpr int CONTENT_WIDTH = TOTAL_WIDTH - (2 * BORDER_WIDTH); // Pre-built strings, initialised once on first call. Each UTF-8 "═" is 3 // bytes. BORDER_SEP is the "═" run between box corners; SPACES is used for // padding. -static llvm::StringRef getBorderSep() { +static StringRef getBorderSep() { static const std::string BORDER_SEP = [] { std::string s; s.reserve(static_cast(TOTAL_WIDTH - 2) * 3U); @@ -41,12 +42,12 @@ static llvm::StringRef getBorderSep() { return BORDER_SEP; } -static llvm::StringRef getSpaces() { +static StringRef getSpaces() { static const std::string SPACES(static_cast(CONTENT_WIDTH), ' '); return SPACES; } -int calculateDisplayWidth(llvm::StringRef str) { +int calculateDisplayWidth(StringRef str) { auto displayWidth = 0; for (size_t i = 0; i < str.size();) { if (const unsigned char c = str[i]; (c & 0x80) == 0) { @@ -73,9 +74,8 @@ int calculateDisplayWidth(llvm::StringRef str) { return displayWidth; } -void wrapLine(llvm::StringRef line, const int maxWidth, - llvm::SmallVectorImpl>& result, - const int indent) { +void wrapLine(StringRef line, const int maxWidth, + SmallVectorImpl>& result, const int indent) { if (line.empty()) { result.emplace_back(""); return; @@ -94,7 +94,7 @@ void wrapLine(llvm::StringRef line, const int maxWidth, } // Extract the content without leading whitespace - const llvm::StringRef content = line.substr(line.find_first_not_of(" \t")); + const StringRef content = line.substr(line.find_first_not_of(" \t")); if (content.empty()) { result.emplace_back(line); return; @@ -114,15 +114,15 @@ void wrapLine(llvm::StringRef line, const int maxWidth, return; } - llvm::SmallString<128> currentLine; - llvm::SmallString<64> currentWord; + SmallString<128> currentLine; + SmallString<64> currentWord; auto currentWidth = 0; auto isFirstLine = true; // Helper: build and emit a completed line with proper indent prefix. // `addArrow` appends " →" to signal the line continues. auto flushLine = [&](const bool addArrow, const bool lastLine) { - llvm::SmallString<128> lineWithIndent; + SmallString<128> lineWithIndent; lineWithIndent.append(leadingSpaces, ' '); lineWithIndent += currentLine; if (addArrow && (!isFirstLine || !lastLine)) { @@ -131,7 +131,7 @@ void wrapLine(llvm::StringRef line, const int maxWidth, result.emplace_back(std::move(lineWithIndent)); }; - auto addWord = [&](llvm::StringRef word) -> bool { + auto addWord = [&](StringRef word) -> bool { const int wordWidth = calculateDisplayWidth(word); const int spaceWidth = currentLine.empty() ? 0 : 1; const int effectiveWidth = isFirstLine ? firstLineWidth : contLineWidth; @@ -161,7 +161,7 @@ void wrapLine(llvm::StringRef line, const int maxWidth, } // Start new continuation line currentLine = currentWord; - currentWidth = calculateDisplayWidth(llvm::StringRef(currentWord)); + currentWidth = calculateDisplayWidth(StringRef(currentWord)); isFirstLine = false; } currentWord.clear(); @@ -179,7 +179,7 @@ void wrapLine(llvm::StringRef line, const int maxWidth, flushLine(/*addArrow=*/true, /*lastLine=*/false); } // Add word on new continuation line (no arrow — this is the last line) - llvm::SmallString<128> contLine("↳ "); + SmallString<128> contLine("↳ "); contLine.append(leadingSpaces, ' '); contLine += currentWord; result.emplace_back(std::move(contLine)); @@ -203,30 +203,23 @@ void wrapLine(llvm::StringRef line, const int maxWidth, // Prepend "↳ " to all continuation lines (index >= 1) that don't have it yet for (size_t i = 1; i < result.size(); ++i) { - if (!llvm::StringRef(result[i]).contains("↳")) { - llvm::SmallString<128> newLine("↳ "); - const llvm::StringRef lineRef = result[i]; + if (!StringRef(result[i]).contains("↳")) { + SmallString<128> newLine("↳ "); + const StringRef lineRef = result[i]; newLine += lineRef.substr(leadingSpaces); result[i] = std::move(newLine); } } } -void printBoxTop(llvm::raw_ostream& os) { - os << "╔" << getBorderSep() << "╗\n"; -} +void printBoxTop(raw_ostream& os) { os << "╔" << getBorderSep() << "╗\n"; } -void printBoxMiddle(llvm::raw_ostream& os) { - os << "╠" << getBorderSep() << "╣\n"; -} +void printBoxMiddle(raw_ostream& os) { os << "╠" << getBorderSep() << "╣\n"; } -void printBoxBottom(llvm::raw_ostream& os) { - os << "╚" << getBorderSep() << "╝\n"; -} +void printBoxBottom(raw_ostream& os) { os << "╚" << getBorderSep() << "╝\n"; } // Internal helper: emit one already-wrapped line inside the box with padding. -static void emitBoxedLine(llvm::StringRef line, const int indent, - llvm::raw_ostream& os) { +static void emitBoxedLine(StringRef line, const int indent, raw_ostream& os) { const int displayWidth = calculateDisplayWidth(line); const int padding = CONTENT_WIDTH - indent - displayWidth; @@ -240,8 +233,7 @@ static void emitBoxedLine(llvm::StringRef line, const int indent, os << " ║\n"; } -void printBoxLine(llvm::StringRef text, const int indent, - llvm::raw_ostream& os) { +void printBoxLine(StringRef text, const int indent, raw_ostream& os) { // Trim trailing whitespace before processing const auto trimmedText = text.rtrim(); @@ -253,7 +245,7 @@ void printBoxLine(llvm::StringRef text, const int indent, } // Wrap the line - llvm::SmallVector, 4> wrappedLines; + SmallVector, 4> wrappedLines; wrapLine(trimmedText, CONTENT_WIDTH, wrappedLines, indent); for (const auto& line : wrappedLines) { @@ -261,10 +253,9 @@ void printBoxLine(llvm::StringRef text, const int indent, } } -void printBoxText(llvm::StringRef text, const int indent, - llvm::raw_ostream& os) { +void printBoxText(StringRef text, const int indent, raw_ostream& os) { // Trim trailing newlines from the entire text, then iterate line-by-line - llvm::StringRef remaining = text.rtrim(); + StringRef remaining = text.rtrim(); while (!remaining.empty()) { auto [lineStr, rest] = remaining.split('\n'); @@ -273,14 +264,13 @@ void printBoxText(llvm::StringRef text, const int indent, } } -void printProgram(ModuleOp module, const llvm::StringRef header, - llvm::raw_ostream& os) { +void printProgram(ModuleOp module, const StringRef header, raw_ostream& os) { printBoxTop(os); printBoxLine(header, 0, os); printBoxMiddle(os); // Capture the IR to a string so we can wrap it in box lines. - llvm::SmallString<4096> irString; + SmallString<4096> irString; llvm::raw_svector_ostream irStream(irString); module.print(irStream); diff --git a/mlir/tools/mqt-cc/mqt-cc.cpp b/mlir/tools/mqt-cc/mqt-cc.cpp index 76848b6594..a016d8062f 100644 --- a/mlir/tools/mqt-cc/mqt-cc.cpp +++ b/mlir/tools/mqt-cc/mqt-cc.cpp @@ -13,6 +13,7 @@ #include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QC/Translation/TranslateQuantumComputationToQC.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QTensor/IR/QTensorDialect.h" #include "qasm3/Exception.hpp" #include "qasm3/Importer.hpp" @@ -25,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +73,11 @@ static cl::opt cl::desc("Print IR after each compiler stage"), cl::init(false)); +static cl::opt disableMergeSingleQubitRotationGates( + "disable-merge-single-qubit-rotation-gates", + cl::desc("Disable quaternion-based single-qubit rotation gate merging"), + cl::init(false)); + /** * @brief Load and parse a .qasm file */ @@ -143,6 +150,8 @@ int main(int argc, char** argv) { registry.insert(); registry.insert(); registry.insert(); + registry.insert(); + registry.insert(); MLIRContext context(registry); context.loadAllAvailableDialects(); @@ -165,6 +174,8 @@ int main(int argc, char** argv) { config.enableTiming = enableTiming; config.enableStatistics = enableStatistics; config.printIRAfterAllStages = printIRAfterAllStages; + config.disableMergeSingleQubitRotationGates = + disableMergeSingleQubitRotationGates; // Run the compilation pipeline CompilationRecord record; diff --git a/mlir/unittests/Compiler/test_compiler_pipeline.cpp b/mlir/unittests/Compiler/test_compiler_pipeline.cpp index 9cde9043a7..0549b1f4ab 100644 --- a/mlir/unittests/Compiler/test_compiler_pipeline.cpp +++ b/mlir/unittests/Compiler/test_compiler_pipeline.cpp @@ -117,9 +117,12 @@ class CompilerPipelineTest } static void runPipeline(const mlir::ModuleOp module, const bool convertToQIR, + const bool disableMergeSingleQubitRotationGates, mlir::CompilationRecord& record) { mlir::QuantumCompilerConfig config; config.convertToQIR = convertToQIR; + config.disableMergeSingleQubitRotationGates = + disableMergeSingleQubitRotationGates; config.recordIntermediates = true; config.printIRAfterAllStages = true; @@ -163,7 +166,7 @@ TEST_P(CompilerPipelineTest, EndToEndPipeline) { EXPECT_TRUE(mlir::verify(*module).succeeded()); mlir::CompilationRecord record; - runPipeline(module.get(), testCase.convertToQIR, record); + runPipeline(module.get(), testCase.convertToQIR, false, record); ASSERT_TRUE(testCase.qcReferenceBuilder); auto qcReference = buildQCReference(testCase.qcReferenceBuilder); @@ -189,6 +192,33 @@ TEST_P(CompilerPipelineTest, EndToEndPipeline) { } } +/** + * @brief Test: Rotation merging pass is invoked during the optimization stage + * + * @details + * The merged U gate parameters are computed via floating-point arithmetic + * that is not bit-identical across platforms, so we cannot use + * verifyAllStages with hardcoded expected values. Instead, we run the + * pipeline once with the pass enabled and compare afterQCOCanon against + * afterOptimization to verify the pass transformed the IR. + * Correctness of the pass is tested in a dedicated test. + */ +TEST_F(CompilerPipelineTest, RotationGateMergingPass) { + auto module = mlir::qc::QCProgramBuilder::build( + context.get(), [&](mlir::qc::QCProgramBuilder& b) { + auto q = b.allocQubit(); + b.rz(1.0, q); + b.rx(1.0, q); + }); + ASSERT_TRUE(module); + + mlir::CompilationRecord record; + runPipeline(module.get(), false, false, record); + + // The outputs must differ, proving the pass ran and transformed the IR + EXPECT_NE(record.afterQCOCanon, record.afterOptimization); +} + INSTANTIATE_TEST_SUITE_P( QuantumComputationPipelineProgramsTest, CompilerPipelineTest, testing::Values( diff --git a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp index 6ad3d1438e..a7c42660f5 100644 --- a/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp +++ b/mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp @@ -19,12 +19,12 @@ #include "qco_programs.h" #include -#include #include #include #include #include #include +#include #include #include @@ -125,9 +125,9 @@ TEST_F(QCOTest, DirectIfBuilder) { auto measureOp = MeasureOp::create(builder, q1); auto ifOp = IfOp::create(builder, measureOp.getResult(), measureOp.getQubitOut(), - [&](ValueRange qubits) -> llvm::SmallVector { + [&](ValueRange qubits) -> SmallVector { auto innerQubit = XOp::create(builder, qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); auto r2 = qtensor::InsertOp::create(builder, ifOp.getResult(0), extractOp.getOutTensor(), c0); diff --git a/mlir/unittests/Dialect/QCO/Transforms/CMakeLists.txt b/mlir/unittests/Dialect/QCO/Transforms/CMakeLists.txt index 30ddc4dc38..9f9b03449d 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/CMakeLists.txt +++ b/mlir/unittests/Dialect/QCO/Transforms/CMakeLists.txt @@ -7,3 +7,4 @@ # Licensed under the MIT License add_subdirectory(Mapping) +add_subdirectory(Optimizations) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Mapping/CMakeLists.txt b/mlir/unittests/Dialect/QCO/Transforms/Mapping/CMakeLists.txt index 1ced00dc34..80b9c7720d 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Mapping/CMakeLists.txt +++ b/mlir/unittests/Dialect/QCO/Transforms/Mapping/CMakeLists.txt @@ -7,12 +7,12 @@ # Licensed under the MIT License set(target_name mqt-core-mlir-unittest-mapping) -add_executable(${target_name} test_mapping.cpp) +add_executable(${target_name} test_mapping.cpp test_swap_absorb.cpp) target_link_libraries( ${target_name} - PRIVATE MLIRParser - GTest::gtest_main + PRIVATE GTest::gtest_main + MLIRParser MLIRQCProgramBuilder MLIRQCOProgramBuilder MLIRQCOUtils diff --git a/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp b/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp index 70a943c536..a8106c3604 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp @@ -19,6 +19,7 @@ #include "mlir/Dialect/QCO/Transforms/Passes.h" #include +#include #include #include #include diff --git a/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_swap_absorb.cpp b/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_swap_absorb.cpp new file mode 100644 index 0000000000..27cd788f9f --- /dev/null +++ b/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_swap_absorb.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + + #include "mlir/Conversion/QCOToQC/QCOToQC.h" +#include "mlir/Conversion/QCToQCO/QCToQCO.h" +#include "mlir/Dialect/QC/Builder/QCProgramBuilder.h" +#include "mlir/Dialect/QC/IR/QCDialect.h" +#include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +using namespace mlir; +using namespace mlir::qco; + +namespace { + +class SwapAbsorbPassTest : public testing::Test{ + + +protected: + void SetUp() override { + // Register all necessary dialects + DialectRegistry registry; + registry.insert(); + context = std::make_unique(); + context->appendDialectRegistry(registry); + context->loadAllAvailableDialects(); + } + + static void applySwapAbsorb(OwningOpRef& moduleOp) { + PassManager pm(moduleOp->getContext()); + pm.addPass(createQCToQCO()); + pm.addPass(qco::createSwapAbsorb()); + pm.addPass(createQCOToQC()); + auto res = pm.run(*moduleOp); + + ASSERT_TRUE(succeeded(res)); + } + + std::unique_ptr context; +}; +}; // namespace + + + +TEST_F(SwapAbsorbPassTest, PassDoesNotChangeSwaplessProgram) { + + qc::QCProgramBuilder builder(context.get()); + builder.initialize(); + + const auto q0 = builder.allocQubit(); + const auto q1 = builder.allocQubit(); + const auto q2 = builder.allocQubit(); + + builder.h(q0); + builder.cx(q0, q1); + + builder.dealloc(q0); + builder.dealloc(q1); + + auto moduleThroughPass = builder.finalize(); + auto originalModule = moduleThroughPass->clone(); + + applySwapAbsorb(moduleThroughPass); + ASSERT_TRUE(mlir::OperationEquivalence::isEquivalentTo( + moduleThroughPass.get(), + originalModule, + mlir::OperationEquivalence::Flags::IgnoreLocations)); +} \ No newline at end of file diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt new file mode 100644 index 0000000000..73606c2efb --- /dev/null +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt @@ -0,0 +1,26 @@ +# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM +# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +set(target_name mqt-core-mlir-unittest-optimizations) +add_executable(${target_name} test_qco_merge_single_qubit_rotation.cpp) + +target_link_libraries( + ${target_name} + PRIVATE GTest::gtest_main + MLIRQCOProgramBuilder + MLIRQCOTransforms + MLIRQCOUtils + MLIRParser + MLIRIR + MLIRPass + MLIRSupport + LLVMSupport) + +mqt_mlir_configure_unittest_target(${target_name}) + +gtest_discover_tests(${target_name} PROPERTIES LABELS mqt-mlir-unittests DISCOVERY_TIMEOUT 60) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/compute_expected_merge_single_qubit_rotation.py b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/compute_expected_merge_single_qubit_rotation.py new file mode 100644 index 0000000000..16f8818cda --- /dev/null +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/compute_expected_merge_single_qubit_rotation.py @@ -0,0 +1,208 @@ +# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM +# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +"""Compute expected U gate parameters and global phase for the merge-single-qubit-rotation pass. + +Reference script for test_qco_merge_single_qubit_rotation.cpp. +Uses SymPy quaternion algebra to produce ground-truth values. +""" + +import math + +from sympy import N, Quaternion, cos, pi, sin + + +def r_gate(theta: float, phi: float) -> Quaternion: + """Return the SU(2) quaternion for an R(theta, phi) gate.""" + return Quaternion(cos(theta / 2), sin(theta / 2) * cos(phi), sin(theta / 2) * sin(phi), 0) + + +def u2_gate(phi: float, lam: float) -> Quaternion: + """Return the SU(2) quaternion for a U2(phi, lambda) gate.""" + return Quaternion.from_euler([phi, pi / 2, lam], "ZYZ") + + +def normalize_angle(a: float) -> float: + """Normalize angle to [-pi, pi], matching the pass's normalizeAngle. + + Returns: + Angle in the range [-pi, pi]. + """ + two_pi = 2 * math.pi + return a - math.floor((a + math.pi) / two_pi) * two_pi + + +def angles_from_quaternion(w: float, x: float, y: float, z: float) -> tuple[float, float, float]: + """ZYZ Euler angles from quaternion, matching anglesFromQuaternion in the pass. + + Returns: + Tuple (theta, phi, lambda) in ZYZ convention. + """ + eps = 1e-12 + + # Clamp before acos to guard against floating-point drift outside [-1, 1] + arg = 2 * (w * w + z * z) - 1 + arg = max(-1.0, min(1.0, arg)) + beta = math.acos(arg) + + abs_beta = abs(beta) + abs_beta_minus_pi = abs(beta - math.pi) + + safe1 = abs_beta >= eps # not near 0 + safe2 = abs_beta_minus_pi >= eps # not near pi + safe = safe1 and safe2 + + theta_plus = math.atan2(z, w) + theta_minus = math.atan2(-x, y) + + if safe: + alpha = theta_plus + theta_minus + gamma = theta_plus - theta_minus + elif not safe1: + # beta near 0 + alpha = 2 * theta_plus + gamma = 0.0 + else: + # beta near pi + alpha = 2 * theta_minus + gamma = 0.0 + + alpha = normalize_angle(alpha) + gamma = normalize_angle(gamma) + + # U gate convention: theta=beta, phi=alpha, lambda=gamma + return beta, alpha, gamma + + +def global_phase(gate_type: str, *angles: float) -> float: + """Return the global phase contribution of a gate. + + U = e^{i*phase} * SU(2), this returns 'phase'. + + Returns: + Global phase in radians. + + Raises: + ValueError: If gate_type is not a recognized gate. + """ + if gate_type in {"RX", "RY", "RZ", "R"}: + return 0.0 + if gate_type == "P": + return angles[0] / 2 + if gate_type == "U": + # U(theta, phi, lambda): phase = (phi + lambda) / 2 + _theta, phi, lam = angles + return (phi + lam) / 2 + if gate_type == "U2": + # U2(phi, lambda): phase = (phi + lambda) / 2 + phi, lam = angles + return (phi + lam) / 2 + msg = f"Unknown gate type: {gate_type!r}" + raise ValueError(msg) + + +def output_phase(phi: float, lam: float) -> float: + """Return the intrinsic phase of the synthesized U(theta, phi, lambda). + + Returns: + Phase in radians. + """ + return (phi + lam) / 2 + + +def gphase_correction(input_phase: float, phi: float, lam: float) -> float: + """Return the GPhaseOp correction = total_input_phase - output_UOp_phase. + + Returns: + Phase correction in radians. + """ + return input_phase - output_phase(phi, lam) + + +# ---- Helper to compute merge + gphase for a chain ---- +def compute_merge(chain: list[tuple]) -> tuple[float, float, float, float]: + """Merge a chain of gates into a single U gate with global phase. + + chain: list of (gate_type, quaternion, *angles). + + Uses our own Euler extraction that matches the C++ pass exactly: + no quaternion sign normalization, same atan2/acos/clamp logic, + same gimbal-lock handling, same angle normalization. + + Returns: + Tuple (theta, phi, lambda, gphase) all as floats. + """ + _, q0, *a0 = chain[0] + q = q0 + total_input_phase = global_phase(chain[0][0], *a0) + + for entry in chain[1:]: + gt, qi, *ai = entry + q = qi.mul(q) # Hamilton product in circuit order + total_input_phase += global_phase(gt, *ai) + + # Extract Euler angles matching the pass (no sign normalization) + w, x, y, z = float(N(q.a)), float(N(q.b)), float(N(q.c)), float(N(q.d)) + theta, phi, lam = angles_from_quaternion(w, x, y, z) + + corr = gphase_correction(total_input_phase, phi, lam) + + return theta, phi, lam, float(N(corr)) + + +# ---- Build gates ---- +rx = Quaternion.from_euler([1, 0, 0], "xyz") +ry = Quaternion.from_euler([0, 1, 0], "xyz") +rz = Quaternion.from_euler([0, 0, 1], "xyz") +mx = Quaternion.from_euler([-1, 0, 0], "xyz") +my = Quaternion.from_euler([0, -1, 0], "xyz") +mz = Quaternion.from_euler([0, 0, -1], "xyz") +px = Quaternion.from_euler([pi, 0, 0], "xyz") +py = Quaternion.from_euler([0, pi, 0], "xyz") +pz = Quaternion.from_euler([0, 0, pi], "xyz") +smallx = Quaternion.from_euler([0.001, 0, 0], "xyz") +smally = Quaternion.from_euler([0, 0.001, 0], "xyz") + +# P gate has same SU(2) quaternion as RZ +p1 = Quaternion.from_euler([0, 0, 1], "xyz") # P(1) same rotation as RZ(1) + +u1 = Quaternion.from_euler([2, 1, 3], "ZYZ") # U(1,2,3) +u2 = Quaternion.from_euler([5, 4, 6], "ZYZ") # U(4,5,6) + +u2_12 = u2_gate(1, 2) +u2_34 = u2_gate(3, 4) + +r12 = r_gate(1, 2) +r34 = r_gate(3, 4) +r11 = r_gate(1, 1) + +cases = [ + ("RX+RX", [("RX", rx), ("RX", rx)]), + ("RX+RY", [("RX", rx), ("RY", ry)]), + ("RX+RZ", [("RX", rx), ("RZ", rz)]), + ("RY+RX", [("RY", ry), ("RX", rx)]), + ("RY+RY", [("RY", ry), ("RY", ry)]), + ("RY+RZ", [("RY", ry), ("RZ", rz)]), + ("RZ+RX", [("RZ", rz), ("RX", rx)]), + ("RZ+RY", [("RZ", rz), ("RY", ry)]), + ("RZ+RZ", [("RZ", rz), ("RZ", rz)]), + ("U+U", [("U", u1, 1.0, 2.0, 3.0), ("U", u2, 4.0, 5.0, 6.0)]), + ("P+RX", [("P", p1, 1.0), ("RX", rx)]), + ("R+R", [("R", r12, 1.0, 2.0), ("R", r34, 3.0, 4.0)]), + ("U2+U2", [("U2", u2_12, 1.0, 2.0), ("U2", u2_34, 3.0, 4.0)]), + ("RZ+RY+RX pi", [("RZ", pz), ("RY", py), ("RX", px)]), + ("RY+RZ+RZ-+RY-", [("RY", ry), ("RZ", rz), ("RZ", mz), ("RY", my)]), + ("small RX+RY", [("RX", smallx), ("RY", smally)]), + ("RX(pi)+RY(pi)", [("RX", px), ("RY", py)]), + ("R+R same", [("R", r11, 1.0, 1.0), ("R", r11, 1.0, 1.0)]), +] + +if __name__ == "__main__": + for name, chain in cases: + theta, phi, lam, gphase = compute_merge(chain) + print(f"{name}: U({theta}, {phi}, {lam}) gphase={gphase}") diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp new file mode 100644 index 0000000000..5ab06957f0 --- /dev/null +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_qco_merge_single_qubit_rotation.cpp @@ -0,0 +1,810 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" +#include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace { + +using namespace mlir; +using namespace mlir::qco; + +/// A constant for the value of \f$\pi\f$. +constexpr double PI = std::numbers::pi; + +class MergeSingleQubitRotationGatesTest : public ::testing::Test { +protected: + MLIRContext context; + QCOProgramBuilder builder; + OwningOpRef module; + + enum class GateType : std::uint8_t { RX, RY, RZ, P, R, U2, U }; + /** + * @brief Struct to easily construct a rotation gate inline. + * opName uses the getOperationName() mnemonic. + */ + struct RotationGate { + GateType type; + llvm::SmallVector angles; + }; + + MergeSingleQubitRotationGatesTest() : builder(&context) {} + + void SetUp() override { + context.loadDialect(); + context.loadDialect(); + context.loadDialect(); + context.loadDialect(); + + builder.initialize(); + } + + /** + * @brief Counts the amount of operations the current module/circuit contains. + */ + template int countOps() { + int count = 0; + module->walk([&](OpTy) { ++count; }); + return count; + } + + /** + * @brief Extract constant floating point value from a mlir::Value + */ + static std::optional toDouble(mlir::Value v) { + if (auto constOp = v.getDefiningOp()) { + if (auto floatAttr = + mlir::dyn_cast(constOp.getValue())) { + return floatAttr.getValueAsDouble(); + } + } + return std::nullopt; + } + + /** + * @brief Find the first occurrence of a u-gate in the current module and get + * the numeric value of its parameters. This assumes that parameters are + * constant and can be extracted. + */ + std::optional> getUGateParams() { + UOp uOp = nullptr; + module->walk([&](UOp op) { + uOp = op; + // stop after finding first UOp + return mlir::WalkResult::interrupt(); + }); + + if (!uOp) { + return std::nullopt; + } + + auto theta = toDouble(uOp.getTheta()); + auto phi = toDouble(uOp.getPhi()); + auto lambda = toDouble(uOp.getLambda()); + + if (!theta || !phi || !lambda) { + return std::nullopt; + } + + return std::make_tuple(*theta, *phi, *lambda); + } + + /** + * @brief Gets the first u-gate of a module and tests whether its angle + * parameters are equal to the expected ones. + */ + void expectUGateParams(double expectedTheta, double expectedPhi, + double expectedLambda, double tolerance = 1e-8) { + auto params = getUGateParams(); + ASSERT_TRUE(params.has_value()); + + auto [theta, phi, lambda] = *params; + EXPECT_NEAR(theta, expectedTheta, tolerance); + EXPECT_NEAR(phi, expectedPhi, tolerance); + EXPECT_NEAR(lambda, expectedLambda, tolerance); + } + + /** + * @brief Find the first occurrence of a gphase op in the current module and + * get the numeric value of its parameter. + */ + std::optional getGPhaseParam() { + GPhaseOp gOp = nullptr; + module->walk([&](GPhaseOp op) { + gOp = op; + return mlir::WalkResult::interrupt(); + }); + + if (!gOp) { + return std::nullopt; + } + + return toDouble(gOp.getParameter(0)); + } + + /** + * @brief Gets the first gphase op of a module and tests whether its angle + * parameter is equal to the expected one. + */ + void expectGPhaseParam(double expected, double tolerance = 1e-8) { + auto param = getGPhaseParam(); + ASSERT_TRUE(param.has_value()); + EXPECT_NEAR(*param, expected, tolerance); + } + + Value buildRotations(llvm::ArrayRef rotations, Value& q) { + auto qubit = q; + + for (const auto& gate : rotations) { + switch (gate.type) { + case GateType::RX: + assert(gate.angles.size() == 1 && "RXOp requires 1 angle parameter"); + qubit = builder.rx(gate.angles[0], qubit); + break; + case GateType::RY: + assert(gate.angles.size() == 1 && "RYOp requires 1 angle parameter"); + qubit = builder.ry(gate.angles[0], qubit); + break; + case GateType::RZ: + assert(gate.angles.size() == 1 && "RZOp requires 1 angle parameter"); + qubit = builder.rz(gate.angles[0], qubit); + break; + case GateType::P: + assert(gate.angles.size() == 1 && "POp requires 1 angle parameter"); + qubit = builder.p(gate.angles[0], qubit); + break; + case GateType::R: + assert(gate.angles.size() == 2 && "ROp requires 2 angle parameters"); + qubit = builder.r(gate.angles[0], gate.angles[1], qubit); + break; + case GateType::U2: + assert(gate.angles.size() == 2 && "U2Op requires 2 angle parameters"); + qubit = builder.u2(gate.angles[0], gate.angles[1], qubit); + break; + case GateType::U: + assert(gate.angles.size() == 3 && "UOp requires 3 angle parameters"); + qubit = + builder.u(gate.angles[0], gate.angles[1], gate.angles[2], qubit); + break; + } + } + + return qubit; + } + + /** + * @brief Takes a list of rotation gates (rx, ry, rz and u) and uses the + * builder api to build a small quantum circuit, where a qubit is fed through + * all rotations in the list. + */ + LogicalResult testGateMerge(llvm::ArrayRef rotations) { + auto q = builder.allocQubitRegister(1); + + buildRotations(rotations, q[0]); + + module = builder.finalize(); + return runMergePass(module.get()); + } + + /** + * @brief Adds the mergeRotationGates Pass to the current context and runs it. + */ + static LogicalResult runMergePass(ModuleOp module) { + PassManager pm(module.getContext()); + pm.addPass(qco::createMergeSingleQubitRotationGates()); + return pm.run(module); + } +}; + +} // namespace + +// Note: All expected values are computed using the reference script +// compute_expected_merge_single_qubit_rotation.py in this directory, which uses +// SymPy's quaternion algebra: +// https://docs.sympy.org/latest/modules/algebras.html#module-sympy.algebras.Quaternion + +// ################################################## +// # Two Gate Merging Tests +// ################################################## + +/** + * @brief Test: RX->RX should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRXRXGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RX, .angles = {1.}}, + {.type = GateType::RX, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: RX->RY should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRXRYGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RX, .angles = {1.}}, + {.type = GateType::RY, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1.27455578230629, -1.07542903757622, 0.495367289218673); + expectGPhaseParam(0.290030874178775); +} + +/** + * @brief Test: RX->RZ should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRXRZGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RX, .angles = {1.}}, + {.type = GateType::RZ, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1., -0.570796326794897, 1.57079632679490); + expectGPhaseParam(-0.5); +} + +/** + * @brief Test: RY->RX should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRYRXGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RY, .angles = {1.}}, + {.type = GateType::RX, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1.27455578230629, -0.495367289218673, 1.07542903757622); + expectGPhaseParam(-0.290030874178775); +} + +/** + * @brief Test: RY->RY should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRYRYGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RY, .angles = {1.}}, + {.type = GateType::RY, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: RY->RZ should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRYRZGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RY, .angles = {1.}}, + {.type = GateType::RZ, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1., 1., 0.); + expectGPhaseParam(-0.5); +} + +/** + * @brief Test: RZ->RX should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRZRXGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RZ, .angles = {1.}}, + {.type = GateType::RX, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1., -1.57079632679490, 2.57079632679490); + expectGPhaseParam(-0.5); +} + +/** + * @brief Test: RZ->RY should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRZRYGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RZ, .angles = {1.}}, + {.type = GateType::RY, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1., 0., 1.); + expectGPhaseParam(-0.5); +} + +/** + * @brief Test: RZ->RZ should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRZRZGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RZ, .angles = {1.}}, + {.type = GateType::RZ, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: U->U should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeUUGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U, .angles = {1., 2., 3.}}, + {.type = GateType::U, .angles = {4., 5., 6.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); + expectUGateParams(2.03289042623884, 0.663830775701153, 0.849231441867857); + expectGPhaseParam(7.243468891215494); +} + +/** + * @brief Test: U->RX should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeURXGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U, .angles = {1., 2., 3.}}, + {.type = GateType::RX, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: U->RY should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeURYGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U, .angles = {1., 2., 3.}}, + {.type = GateType::RY, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: U->RZ should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeURZGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U, .angles = {1., 2., 3.}}, + {.type = GateType::RZ, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: RX->U should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRXUGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RX, .angles = {1.}}, + {.type = GateType::U, .angles = {1., 2., 3.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: RY->U should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRYUGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RY, .angles = {1.}}, + {.type = GateType::U, .angles = {1., 2., 3.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: RZ->U should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRZUGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RZ, .angles = {1.}}, + {.type = GateType::U, .angles = {1., 2., 3.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); +} +/** + * @brief Test: P->RX should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergePRXGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::P, .angles = {1.}}, + {.type = GateType::RX, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1., -1.57079632679490, 2.57079632679490); + expectGPhaseParam(1.11022302462516e-16); +} + +/** + * @brief Test: P->RY should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergePRYGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::P, .angles = {1.}}, + {.type = GateType::RY, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: P->U should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergePUGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::P, .angles = {1.}}, + {.type = GateType::U, .angles = {1., 2., 3.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: R->RX should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRRXGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::R, .angles = {1., 1.}}, + {.type = GateType::RX, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: P->P should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergePPGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::P, .angles = {1.}}, + {.type = GateType::P, .angles = {1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: R->R should merge into a single U gate (same multi-parameter + * type always uses quaternion merge) + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeRRGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::R, .angles = {1., 2.}}, + {.type = GateType::R, .angles = {3., 4.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + expectUGateParams(2.07770669385131, 1.36334275733332, 2.85969871348886); + expectGPhaseParam(-2.1115207354110845); +} + +/** + * @brief Test: U2->U should merge into a single U gate + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeU2UGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U2, .angles = {1., 2.}}, + {.type = GateType::U, .angles = {1., 2., 3.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: U2->U2 should merge into a single U gate (same multi-parameter + * type always uses quaternion merge) + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeU2U2Gates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U2, .angles = {1., 2.}}, + {.type = GateType::U2, .angles = {3., 4.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(1.85840734641021, 1.42920367320511, 0.429203673205103); + expectGPhaseParam(4.070796326794897); +} + +// ################################################## +// # Not Merging Tests +// ################################################## + +/** + * @brief Test: single RX should not convert to U + */ +TEST_F(MergeSingleQubitRotationGatesTest, noMergeSingleRXGate) { + ASSERT_TRUE( + testGateMerge({{.type = GateType::RX, .angles = {1.}}}).succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); +} + +/** + * @brief Test: single RY should not convert to U + */ +TEST_F(MergeSingleQubitRotationGatesTest, noMergeSingleRYGate) { + ASSERT_TRUE( + testGateMerge({{.type = GateType::RY, .angles = {1.}}}).succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); +} + +/** + * @brief Test: single RZ should not convert to U + */ +TEST_F(MergeSingleQubitRotationGatesTest, noMergeSingleRZGate) { + ASSERT_TRUE( + testGateMerge({{.type = GateType::RZ, .angles = {1.}}}).succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); +} + +/** + * @brief Test: Gates on different qubits should not merge + */ +TEST_F(MergeSingleQubitRotationGatesTest, dontMergeGatesFromDifferentQubits) { + auto q = builder.allocQubitRegister(2); + + builder.rx(1.0, q[0]); + builder.ry(1.0, q[1]); + module = builder.finalize(); + + ASSERT_TRUE(runMergePass(module.get()).succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); +} + +/** + * @brief Test: Non-consecutive gates should not merge + */ +TEST_F(MergeSingleQubitRotationGatesTest, dontMergeNonConsecutiveGates) { + auto q = builder.allocQubitRegister(1); + + auto q1 = builder.rx(1.0, q[0]); + auto q2 = builder.h(q1); + builder.ry(1.0, q2); + + module = builder.finalize(); + + ASSERT_TRUE(runMergePass(module.get()).succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); +} + +// ################################################## +// # Greedy Merging Tests +// ################################################## + +/** + * @brief Test: Many gates should greedily merge into one U + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeManyGates) { + ASSERT_TRUE(testGateMerge({{.type = GateType::U, .angles = {1., 2., 3.}}, + {.type = GateType::RX, .angles = {1.}}, + {.type = GateType::RY, .angles = {2.}}, + {.type = GateType::RZ, .angles = {3.}}, + {.type = GateType::U, .angles = {4., 5., 6.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); +} + +/** + * @brief Test: Many gates with one unmergeable in between should merge into two + * U with the unmergeable in between. + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeManyWithUnmergeable) { + auto reg = builder.allocQubitRegister(1); + auto q = reg[0]; + q = buildRotations({{.type = GateType::U, .angles = {1., 2., 3.}}, + {.type = GateType::RX, .angles = {1.}}, + {.type = GateType::RY, .angles = {2.}}, + {.type = GateType::RZ, .angles = {3.}}}, + q); + q = builder.h(q); + q = buildRotations({{.type = GateType::RZ, .angles = {4.}}, + {.type = GateType::RY, .angles = {5.}}, + {.type = GateType::RX, .angles = {6.}}, + {.type = GateType::U, .angles = {4., 5., 6.}}}, + q); + + module = builder.finalize(); + + ASSERT_TRUE(runMergePass(module.get()).succeeded()); + EXPECT_EQ(countOps(), 2); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 2); +} + +// ################################################## +// # Special Cases Tests +// ################################################## + +/** + * @brief Test: Consecutive gates with another gate in between should merge + */ +TEST_F(MergeSingleQubitRotationGatesTest, mergeConsecutiveWithGateInBetween) { + auto q = builder.allocQubitRegister(2); + + auto q1 = builder.rx(1.0, q[0]); + builder.h(q[1]); + builder.ry(1.0, q1); + + module = builder.finalize(); + + ASSERT_TRUE(runMergePass(module.get()).succeeded()); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); +} + +// ################################################## +// # Numerical Correctness +// ################################################## + +/** + * @brief Test: RZ(PI)->RY(PI)->RX(PI) should merge into U(0, 0, 0) + */ +TEST_F(MergeSingleQubitRotationGatesTest, numericalRotationIdentity) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RZ, .angles = {PI}}, + {.type = GateType::RY, .angles = {PI}}, + {.type = GateType::RX, .angles = {PI}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(0., 0., 0.); + expectGPhaseParam(0.); +} + +/** + * @brief Test: RY(1)->RZ(1)->RZ(-1)->RY(-1) should merge into U(0, 0, 0) + */ +TEST_F(MergeSingleQubitRotationGatesTest, numericalRotationIdentity2) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RY, .angles = {1}}, + {.type = GateType::RZ, .angles = {1}}, + {.type = GateType::RZ, .angles = {-1}}, + {.type = GateType::RY, .angles = {-1}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(0., 0., 0.); + expectGPhaseParam(0.); +} + +/** + * @brief Test: RX(0.001)->RY(0.001) should merge into U(0.00141421344452194, + * -0.785398413397490, 0.785397913397407) + */ +TEST_F(MergeSingleQubitRotationGatesTest, numericalSmallAngles) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RX, .angles = {0.001}}, + {.type = GateType::RY, .angles = {0.001}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(0.00141421344452194, -0.785398413397490, 0.785397913397407); + expectGPhaseParam(2.50000041668308e-7); +} + +/** + * @brief Test: RX(PI)->RY(PI) should merge into U(0, -PI, 0.) + */ +TEST_F(MergeSingleQubitRotationGatesTest, numericalGimbalLock) { + ASSERT_TRUE(testGateMerge({{.type = GateType::RX, .angles = {PI}}, + {.type = GateType::RY, .angles = {PI}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(0., -PI, 0.); + expectGPhaseParam(1.57079632679490); +} + +/** + * @brief Test: R(1,1)->R(1,1) (same axis) should merge into U(2.00000000000000, + * -0.570796326794897, 0.570796326794897) + */ +TEST_F(MergeSingleQubitRotationGatesTest, numericalAccuracyRRSameAxis) { + ASSERT_TRUE(testGateMerge({{.type = GateType::R, .angles = {1., 1.}}, + {.type = GateType::R, .angles = {1., 1.}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 0); + EXPECT_EQ(countOps(), 1); + expectUGateParams(2., -0.570796326794897, 0.570796326794897); + expectGPhaseParam(0.0); +} + +/** + * @brief Test: U(0, -2.0360075460227076, 0)->U(0, 4.157656961105587, 0) should + * not produce NaN. These specific numbers would produce NaN if acos parameter + * would not be clamped to [-1, 1] + */ +TEST_F(MergeSingleQubitRotationGatesTest, numericalAcosClampingPreventsNaN) { + ASSERT_TRUE(testGateMerge( + {{.type = GateType::U, .angles = {0, -2.0360075460227076, 0}}, + {.type = GateType::U, .angles = {0, 4.157656961105587, 0}}}) + .succeeded()); + EXPECT_EQ(countOps(), 1); + EXPECT_EQ(countOps(), 1); + + auto params = getUGateParams(); + ASSERT_TRUE(params.has_value()); + + auto [theta, phi, lambda] = *params; + EXPECT_FALSE(std::isnan(theta)); + EXPECT_FALSE(std::isnan(phi)); + EXPECT_FALSE(std::isnan(lambda)); + + auto gphase = getGPhaseParam(); + ASSERT_TRUE(gphase.has_value()); + EXPECT_FALSE(std::isnan(*gphase)); +} diff --git a/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp b/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp index b7e664e309..616e08d66d 100644 --- a/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp +++ b/mlir/unittests/Dialect/QCO/Utils/test_drivers.cpp @@ -10,17 +10,24 @@ #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Utils/Drivers.h" +#include "mlir/Dialect/QCO/Utils/Qubits.h" +#include "mlir/Dialect/QCO/Utils/WireIterator.h" #include +#include +#include #include #include #include #include #include +#include #include #include +#include using namespace mlir; @@ -40,7 +47,7 @@ class DriversTest : public testing::Test { }; } // namespace -TEST_F(DriversTest, FullWalk) { +TEST_F(DriversTest, ProgramWalk) { qco::QCOProgramBuilder builder(context.get()); builder.initialize(); const auto q00 = builder.allocQubit(); @@ -62,27 +69,148 @@ TEST_F(DriversTest, FullWalk) { builder.sink(q22); builder.sink(q32); - auto module = builder.finalize(); - auto func = *(module->getOps().begin()); + auto mod = builder.finalize(); + auto func = *(mod->getOps().begin()); Value ex0 = nullptr; Value ex1 = nullptr; Value ex2 = nullptr; Value ex3 = nullptr; - qco::walkUnit(func.getBody(), [&](Operation* op, qco::Qubits& qubits) { - if (op == q03.getDefiningOp()) { - ex0 = qubits.getProgramQubit(0); - ex1 = qubits.getProgramQubit(1); - ex2 = qubits.getProgramQubit(2); - ex3 = qubits.getProgramQubit(3); - return WalkResult::interrupt(); - } - return WalkResult::advance(); - }); + // Walk until the first measurement operation is encountered and stop. + // Since WalkOrder::PreOrder is used here, the state of the qubits is not yet + // updated with the SSA values of the measurement op. + // Consequently, the program qubits point at the outputs of the controlled-Xs. + std::ignore = qco::walkProgram(func.getBody(), + [&](Operation* op, const qco::Qubits& qubits) { + if (op == q03.getDefiningOp()) { + ex0 = qubits.getProgramQubit(0); + ex1 = qubits.getProgramQubit(1); + ex2 = qubits.getProgramQubit(2); + ex3 = qubits.getProgramQubit(3); + return WalkResult::interrupt(); + } + return WalkResult::advance(); + }); ASSERT_EQ(ex0, q02); ASSERT_EQ(ex1, q11); ASSERT_EQ(ex2, q21); ASSERT_EQ(ex3, q31); } + +TEST_F(DriversTest, ProgramGraphWalk) { + qco::QCOProgramBuilder builder(context.get()); + builder.initialize(); + + const auto q00 = builder.allocQubit(); + const auto q10 = builder.allocQubit(); + const auto q20 = builder.allocQubit(); + const auto q30 = builder.allocQubit(); + + const auto q01 = builder.h(q00); + + const auto [q02, q11] = builder.cx(q01, q10); + const auto [q21, q31] = builder.cx(q20, q30); + + const auto q03 = builder.z(q02); + const auto q22 = builder.h(q21); + + const auto [q12, q23] = builder.cx(q11, q22); + + const auto [q04, q13] = builder.cx(q03, q12); + const auto q14 = builder.h(q13); + + builder.measure(q04); + builder.measure(q14); + builder.measure(q23); + builder.measure(q31); + + auto mod = builder.finalize(); + auto func = *(mod->getOps().begin()); + + // Collect wires. + SmallVector wires; + for (qco::AllocOp op : func.getOps()) { + wires.emplace_back(op.getResult()); + } + + // Unit-test supporting datastructure. + SmallVector> readyPerLayer; + + // Forward pass. + auto res = qco::walkProgramGraph( + wires, [&](const qco::ReadyRange& ready, qco::ReleasedOps& released) { + DenseSet layer; + for (const auto& [op, progs] : ready) { + layer.insert(op); + released.emplace_back(op); + } + readyPerLayer.emplace_back(layer); + return WalkResult::advance(); + }); + + ASSERT_TRUE(res.succeeded()); + ASSERT_GE(readyPerLayer.size(), 3); + ASSERT_TRUE(readyPerLayer[0].contains(q02.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[0].contains(q21.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[1].contains(q12.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[2].contains(q04.getDefiningOp())); + + // Backward pass. + readyPerLayer.clear(); + res = qco::walkProgramGraph( + wires, [&](const qco::ReadyRange& ready, qco::ReleasedOps& released) { + DenseSet layer; + for (const auto& [op, progs] : ready) { + layer.insert(op); + released.emplace_back(op); + } + readyPerLayer.emplace_back(layer); + return WalkResult::advance(); + }); + + ASSERT_TRUE(res.succeeded()); + ASSERT_GE(readyPerLayer.size(), 3); + ASSERT_TRUE(readyPerLayer[0].contains(q04.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[1].contains(q12.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[2].contains(q02.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[2].contains(q21.getDefiningOp())); + + // Forward, but instead of releasing all, we use ::skip(). + readyPerLayer.clear(); + res = qco::walkProgramGraph( + wires, [&](const qco::ReadyRange& ready, qco::ReleasedOps&) { + DenseSet layer; + for (const auto& [op, progs] : ready) { + layer.insert(op); + } + readyPerLayer.emplace_back(layer); + + return WalkResult::skip(); + }); + + ASSERT_TRUE(res.succeeded()); + ASSERT_GE(readyPerLayer.size(), 3); + ASSERT_TRUE(readyPerLayer[0].contains(q02.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[0].contains(q21.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[1].contains(q12.getDefiningOp())); + ASSERT_TRUE(readyPerLayer[2].contains(q04.getDefiningOp())); + + // Backward, but stop after first layer. + readyPerLayer.clear(); + res = qco::walkProgramGraph( + wires, [&](const qco::ReadyRange& ready, qco::ReleasedOps& released) { + DenseSet layer; + for (const auto& [op, progs] : ready) { + layer.insert(op); + released.emplace_back(op); + } + readyPerLayer.emplace_back(layer); + return WalkResult::interrupt(); + }); + + ASSERT_TRUE(res.failed()); + ASSERT_EQ(readyPerLayer.size(), 1); + ASSERT_TRUE(readyPerLayer[0].contains(q04.getDefiningOp())); +} diff --git a/mlir/unittests/Dialect/Utils/test_utils.cpp b/mlir/unittests/Dialect/Utils/test_utils.cpp index 36bd137673..5ed1a21638 100644 --- a/mlir/unittests/Dialect/Utils/test_utils.cpp +++ b/mlir/unittests/Dialect/Utils/test_utils.cpp @@ -11,9 +11,7 @@ #include "mlir/Dialect/Utils/Utils.h" #include -#include #include -#include #include #include #include @@ -22,6 +20,7 @@ #include #include #include +#include #include #include @@ -96,7 +95,7 @@ TEST_F(UtilsTest, valueToDoubleCastFromMaxUnsignedInteger) { auto op = arith::ConstantOp::create( *builder, builder->getIntegerAttr(builder->getIntegerType(bitCount, false), - llvm::APInt::getMaxValue(bitCount))); + APInt::getMaxValue(bitCount))); ASSERT_TRUE(op); const auto stdValue = utils::valueToDouble(op.getResult()); @@ -126,11 +125,11 @@ TEST_F(UtilsTest, valueToDoubleFoldedConstant) { auto op = createAddition(1.5, 2.0); ASSERT_TRUE(op); - llvm::SmallVector tmp; - llvm::SmallVector newConstants; + SmallVector tmp; + SmallVector newConstants; ASSERT_TRUE(builder->tryFold(op, tmp, &newConstants).succeeded()); ASSERT_EQ(newConstants.size(), 1); - auto cst = llvm::dyn_cast(newConstants[0]); + auto cst = dyn_cast(newConstants[0]); ASSERT_TRUE(cst); const auto stdValue = utils::valueToDouble(cst.getResult()); ASSERT_TRUE(stdValue.has_value()); diff --git a/mlir/unittests/programs/qco_programs.cpp b/mlir/unittests/programs/qco_programs.cpp index 88dca42b54..ff05a9f075 100644 --- a/mlir/unittests/programs/qco_programs.cpp +++ b/mlir/unittests/programs/qco_programs.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -66,7 +67,7 @@ void staticQubitsWithCtrl(QCOProgramBuilder& b) { void staticQubitsWithInv(QCOProgramBuilder& b) { auto q0 = b.staticQubit(0); - q0 = b.inv({q0}, [&](auto targets) -> llvm::SmallVector { + q0 = b.inv({q0}, [&](auto targets) -> SmallVector { return {b.t(targets[0])}; })[0]; } @@ -175,19 +176,18 @@ void multipleControlledGlobalPhase(QCOProgramBuilder& b) { } void inverseGlobalPhase(QCOProgramBuilder& b) { - b.inv({}, [&](mlir::ValueRange /*qubits*/) { + b.inv({}, [&](ValueRange /*qubits*/) { b.gphase(-0.123); - return llvm::SmallVector{}; + return SmallVector{}; }); } void inverseMultipleControlledGlobalPhase(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { - llvm::SmallVector controls{qubits[0], qubits[1], qubits[2]}; + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { + SmallVector controls{qubits[0], qubits[1], qubits[2]}; auto controlsOut = b.mcgphase(-0.123, controls); - return llvm::SmallVector(controlsOut.begin(), - controlsOut.end()); + return SmallVector(controlsOut.begin(), controlsOut.end()); }); } @@ -208,13 +208,13 @@ void multipleControlledIdentity(QCOProgramBuilder& b) { void nestedControlledIdentity(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.id(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.id(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -225,18 +225,17 @@ void trivialControlledIdentity(QCOProgramBuilder& b) { void inverseIdentity(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.id(qubits[0])}; - }); + b.inv({q[0]}, + [&](ValueRange qubits) { return SmallVector{b.id(qubits[0])}; }); } void inverseMultipleControlledIdentity(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcid({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -257,13 +256,13 @@ void multipleControlledX(QCOProgramBuilder& b) { void nestedControlledX(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.x(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.x(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -274,18 +273,16 @@ void trivialControlledX(QCOProgramBuilder& b) { void inverseX(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.x(qubits[0])}; - }); + b.inv({q[0]}, [&](ValueRange qubits) { return SmallVector{b.x(qubits[0])}; }); } void inverseMultipleControlledX(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcx({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -312,13 +309,13 @@ void multipleControlledY(QCOProgramBuilder& b) { void nestedControlledY(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.y(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.y(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -329,18 +326,16 @@ void trivialControlledY(QCOProgramBuilder& b) { void inverseY(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.y(qubits[0])}; - }); + b.inv({q[0]}, [&](ValueRange qubits) { return SmallVector{b.y(qubits[0])}; }); } void inverseMultipleControlledY(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcy({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -367,13 +362,13 @@ void multipleControlledZ(QCOProgramBuilder& b) { void nestedControlledZ(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.z(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.z(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -384,18 +379,16 @@ void trivialControlledZ(QCOProgramBuilder& b) { void inverseZ(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.z(qubits[0])}; - }); + b.inv({q[0]}, [&](ValueRange qubits) { return SmallVector{b.z(qubits[0])}; }); } void inverseMultipleControlledZ(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcz({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -422,13 +415,13 @@ void multipleControlledH(QCOProgramBuilder& b) { void nestedControlledH(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.h(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.h(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -439,18 +432,16 @@ void trivialControlledH(QCOProgramBuilder& b) { void inverseH(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.h(qubits[0])}; - }); + b.inv({q[0]}, [&](ValueRange qubits) { return SmallVector{b.h(qubits[0])}; }); } void inverseMultipleControlledH(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mch({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -482,13 +473,13 @@ void multipleControlledS(QCOProgramBuilder& b) { void nestedControlledS(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.s(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.s(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -499,18 +490,16 @@ void trivialControlledS(QCOProgramBuilder& b) { void inverseS(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.s(qubits[0])}; - }); + b.inv({q[0]}, [&](ValueRange qubits) { return SmallVector{b.s(qubits[0])}; }); } void inverseMultipleControlledS(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcs({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -543,13 +532,13 @@ void multipleControlledSdg(QCOProgramBuilder& b) { void nestedControlledSdg(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.sdg(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.sdg(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -560,18 +549,17 @@ void trivialControlledSdg(QCOProgramBuilder& b) { void inverseSdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.sdg(qubits[0])}; - }); + b.inv({q[0]}, + [&](ValueRange qubits) { return SmallVector{b.sdg(qubits[0])}; }); } void inverseMultipleControlledSdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcsdg({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -604,13 +592,13 @@ void multipleControlledT(QCOProgramBuilder& b) { void nestedControlledT(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.t(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.t(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -621,18 +609,16 @@ void trivialControlledT(QCOProgramBuilder& b) { void inverseT(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.t(qubits[0])}; - }); + b.inv({q[0]}, [&](ValueRange qubits) { return SmallVector{b.t(qubits[0])}; }); } void inverseMultipleControlledT(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mct({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -665,13 +651,13 @@ void multipleControlledTdg(QCOProgramBuilder& b) { void nestedControlledTdg(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.tdg(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.tdg(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -682,18 +668,17 @@ void trivialControlledTdg(QCOProgramBuilder& b) { void inverseTdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.tdg(qubits[0])}; - }); + b.inv({q[0]}, + [&](ValueRange qubits) { return SmallVector{b.tdg(qubits[0])}; }); } void inverseMultipleControlledTdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mctdg({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -726,13 +711,13 @@ void multipleControlledSx(QCOProgramBuilder& b) { void nestedControlledSx(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.sx(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.sx(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -743,18 +728,17 @@ void trivialControlledSx(QCOProgramBuilder& b) { void inverseSx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.sx(qubits[0])}; - }); + b.inv({q[0]}, + [&](ValueRange qubits) { return SmallVector{b.sx(qubits[0])}; }); } void inverseMultipleControlledSx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcsx({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -787,13 +771,13 @@ void multipleControlledSxdg(QCOProgramBuilder& b) { void nestedControlledSxdg(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.sxdg(innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.sxdg(innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -804,18 +788,17 @@ void trivialControlledSxdg(QCOProgramBuilder& b) { void inverseSxdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.sxdg(qubits[0])}; - }); + b.inv({q[0]}, + [&](ValueRange qubits) { return SmallVector{b.sxdg(qubits[0])}; }); } void inverseMultipleControlledSxdg(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcsxdg({qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -848,13 +831,13 @@ void multipleControlledRx(QCOProgramBuilder& b) { void nestedControlledRx(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.rx(0.123, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.rx(0.123, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -865,18 +848,18 @@ void trivialControlledRx(QCOProgramBuilder& b) { void inverseRx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.rx(-0.123, qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.rx(-0.123, qubits[0])}; }); } void inverseMultipleControlledRx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcrx(-0.123, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -908,13 +891,13 @@ void multipleControlledRy(QCOProgramBuilder& b) { void nestedControlledRy(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.ry(0.456, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.ry(0.456, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -925,18 +908,18 @@ void trivialControlledRy(QCOProgramBuilder& b) { void inverseRy(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.ry(-0.456, qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.ry(-0.456, qubits[0])}; }); } void inverseMultipleControlledRy(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcry(-0.456, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } void twoRyOppositePhase(QCOProgramBuilder& b) { @@ -967,13 +950,13 @@ void multipleControlledRz(QCOProgramBuilder& b) { void nestedControlledRz(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.rz(0.789, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.rz(0.789, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -984,18 +967,18 @@ void trivialControlledRz(QCOProgramBuilder& b) { void inverseRz(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.rz(-0.789, qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.rz(-0.789, qubits[0])}; }); } void inverseMultipleControlledRz(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcrz(-0.789, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -1022,13 +1005,13 @@ void multipleControlledP(QCOProgramBuilder& b) { void nestedControlledP(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{b.p(0.123, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.p(0.123, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1039,18 +1022,17 @@ void trivialControlledP(QCOProgramBuilder& b) { void inverseP(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.p(-0.123, qubits[0])}; - }); + b.inv({q[0]}, + [&](ValueRange qubits) { return SmallVector{b.p(-0.123, qubits[0])}; }); } void inverseMultipleControlledP(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcp(-0.123, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -1077,14 +1059,13 @@ void multipleControlledR(QCOProgramBuilder& b) { void nestedControlledR(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{ - b.r(0.123, 0.456, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.r(0.123, 0.456, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1095,18 +1076,18 @@ void trivialControlledR(QCOProgramBuilder& b) { void inverseR(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.r(-0.123, 0.456, qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.r(-0.123, 0.456, qubits[0])}; }); } void inverseMultipleControlledR(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcr(-0.123, 0.456, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -1137,14 +1118,13 @@ void multipleControlledU2(QCOProgramBuilder& b) { void nestedControlledU2(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{ - b.u2(0.234, 0.567, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.u2(0.234, 0.567, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1156,20 +1136,19 @@ void trivialControlledU2(QCOProgramBuilder& b) { void inverseU2(QCOProgramBuilder& b) { constexpr double pi = std::numbers::pi; auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{ - b.u2(-0.567 + pi, -0.234 - pi, qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.u2(-0.567 + pi, -0.234 - pi, qubits[0])}; }); } void inverseMultipleControlledU2(QCOProgramBuilder& b) { constexpr double pi = std::numbers::pi; auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcu2(-0.567 + pi, -0.234 - pi, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } void canonicalizeU2ToH(QCOProgramBuilder& b) { @@ -1203,14 +1182,13 @@ void multipleControlledU(QCOProgramBuilder& b) { void nestedControlledU(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(3); - b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](mlir::ValueRange targets) { + b.ctrl({reg[0]}, {reg[1], reg[2]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1]}, [&](mlir::ValueRange innerTargets) { - return llvm::SmallVector{ - b.u(0.1, 0.2, 0.3, innerTargets[0])}; + b.ctrl({targets[0]}, {targets[1]}, [&](ValueRange innerTargets) { + return SmallVector{b.u(0.1, 0.2, 0.3, innerTargets[0])}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1221,18 +1199,18 @@ void trivialControlledU(QCOProgramBuilder& b) { void inverseU(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.u(-0.1, -0.3, -0.2, qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.u(-0.1, -0.3, -0.2, qubits[0])}; }); } void inverseMultipleControlledU(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetOut] = b.mcu(-0.1, -0.3, -0.2, {qubits[0], qubits[1]}, qubits[2]); return llvm::to_vector( - llvm::concat(controlsOut, mlir::ValueRange{targetOut})); + llvm::concat(controlsOut, ValueRange{targetOut})); }); } @@ -1273,15 +1251,14 @@ void multipleControlledSwap(QCOProgramBuilder& b) { void nestedControlledSwap(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.swap(innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.swap(innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1292,20 +1269,19 @@ void trivialControlledSwap(QCOProgramBuilder& b) { void inverseSwap(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.swap(qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledSwap(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcswap({qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1338,15 +1314,14 @@ void multipleControlledIswap(QCOProgramBuilder& b) { void nestedControlledIswap(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.iswap(innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.iswap(innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1357,20 +1332,19 @@ void trivialControlledIswap(QCOProgramBuilder& b) { void inverseIswap(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.iswap(qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledIswap(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mciswap({qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1391,15 +1365,14 @@ void multipleControlledDcx(QCOProgramBuilder& b) { void nestedControlledDcx(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.dcx(innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.dcx(innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1410,20 +1383,19 @@ void trivialControlledDcx(QCOProgramBuilder& b) { void inverseDcx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[1], q[0]}, [&](mlir::ValueRange qubits) { + b.inv({q[1], q[0]}, [&](ValueRange qubits) { auto res = b.dcx(qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledDcx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[3], q[2]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[3], q[2]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcdcx({qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1456,15 +1428,14 @@ void multipleControlledEcr(QCOProgramBuilder& b) { void nestedControlledEcr(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.ecr(innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.ecr(innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1475,20 +1446,19 @@ void trivialControlledEcr(QCOProgramBuilder& b) { void inverseEcr(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.ecr(qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledEcr(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcecr({qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1515,15 +1485,14 @@ void multipleControlledRxx(QCOProgramBuilder& b) { void nestedControlledRxx(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.rxx(0.123, innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.rxx(0.123, innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1534,20 +1503,19 @@ void trivialControlledRxx(QCOProgramBuilder& b) { void inverseRxx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.rxx(-0.123, qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledRxx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcrxx(-0.123, {qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1604,15 +1572,14 @@ void multipleControlledRyy(QCOProgramBuilder& b) { void nestedControlledRyy(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.ryy(0.123, innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.ryy(0.123, innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1623,20 +1590,19 @@ void trivialControlledRyy(QCOProgramBuilder& b) { void inverseRyy(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.ryy(-0.123, qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledRyy(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcryy(-0.123, {qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1683,15 +1649,14 @@ void multipleControlledRzx(QCOProgramBuilder& b) { void nestedControlledRzx(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.rzx(0.123, innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.rzx(0.123, innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1702,20 +1667,19 @@ void trivialControlledRzx(QCOProgramBuilder& b) { void inverseRzx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.rzx(-0.123, qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledRzx(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcrzx(-0.123, {qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1742,15 +1706,14 @@ void multipleControlledRzz(QCOProgramBuilder& b) { void nestedControlledRzz(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.rzz(0.123, innerTargets[0], innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = b.rzz(0.123, innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1761,20 +1724,19 @@ void trivialControlledRzz(QCOProgramBuilder& b) { void inverseRzz(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.rzz(-0.123, qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledRzz(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcrzz(-0.123, {qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1821,16 +1783,15 @@ void multipleControlledXxPlusYY(QCOProgramBuilder& b) { void nestedControlledXxPlusYY(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.xx_plus_yy(0.123, 0.456, innerTargets[0], - innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = + b.xx_plus_yy(0.123, 0.456, innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1841,20 +1802,19 @@ void trivialControlledXxPlusYY(QCOProgramBuilder& b) { void inverseXxPlusYY(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.xx_plus_yy(-0.123, 0.456, qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledXxPlusYY(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcxx_plus_yy( -0.123, 0.456, {qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1881,16 +1841,15 @@ void multipleControlledXxMinusYY(QCOProgramBuilder& b) { void nestedControlledXxMinusYY(QCOProgramBuilder& b) { auto reg = b.allocQubitRegister(4); - b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto res = b.xx_minus_yy(0.123, 0.456, innerTargets[0], - innerTargets[1]); - return llvm::SmallVector{res.first, res.second}; - }); + b.ctrl({reg[0]}, {reg[1], reg[2], reg[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto res = + b.xx_minus_yy(0.123, 0.456, innerTargets[0], innerTargets[1]); + return SmallVector{res.first, res.second}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } @@ -1901,20 +1860,19 @@ void trivialControlledXxMinusYY(QCOProgramBuilder& b) { void inverseXxMinusYY(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1]}, [&](ValueRange qubits) { auto res = b.xx_minus_yy(-0.123, 0.456, qubits[0], qubits[1]); - return llvm::SmallVector{res.first, res.second}; + return SmallVector{res.first, res.second}; }); } void inverseMultipleControlledXxMinusYY(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.inv({q[0], q[1], q[2], q[3]}, [&](mlir::ValueRange qubits) { + b.inv({q[0], q[1], q[2], q[3]}, [&](ValueRange qubits) { const auto& [controlsOut, targetsOut] = b.mcxx_minus_yy( -0.123, 0.456, {qubits[0], qubits[1]}, qubits[2], qubits[3]); - llvm::SmallVector targets{targetsOut.first, - targetsOut.second}; - return llvm::to_vector(llvm::concat(controlsOut, targets)); + SmallVector targets{targetsOut.first, targetsOut.second}; + return llvm::to_vector(llvm::concat(controlsOut, targets)); }); } @@ -1941,15 +1899,15 @@ void barrierMultipleQubits(QCOProgramBuilder& b) { void singleControlledBarrier(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.ctrl({q[1]}, {q[0]}, [&](mlir::ValueRange targets) { - return llvm::SmallVector{b.barrier(targets[0])}; + b.ctrl({q[1]}, {q[0]}, [&](ValueRange targets) { + return SmallVector{b.barrier(targets[0])}; }); } void inverseBarrier(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); - b.inv({q[0]}, [&](mlir::ValueRange qubits) { - return llvm::SmallVector{b.barrier(qubits[0])}; + b.inv({q[0]}, [&](ValueRange qubits) { + return SmallVector{b.barrier(qubits[0])}; }); } @@ -1963,124 +1921,119 @@ void twoBarrier(QCOProgramBuilder& b) { void trivialCtrl(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.ctrl({}, {q[0], q[1]}, [&](mlir::ValueRange targets) { + b.ctrl({}, {q[0], q[1]}, [&](ValueRange targets) { auto [q0, q1] = b.rxx(0.123, targets[0], targets[1]); - return llvm::SmallVector{q0, q1}; + return SmallVector{q0, q1}; }); } void nestedCtrl(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.ctrl({q[0]}, {q[1], q[2], q[3]}, [&](mlir::ValueRange targets) { - const auto& [innerControlsOut, innerTargetsOut] = - b.ctrl({targets[0]}, {targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { - auto [q0, q1] = b.rxx(0.123, innerTargets[0], innerTargets[1]); - return llvm::SmallVector{q0, q1}; - }); + b.ctrl({q[0]}, {q[1], q[2], q[3]}, [&](ValueRange targets) { + const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( + {targets[0]}, {targets[1], targets[2]}, [&](ValueRange innerTargets) { + auto [q0, q1] = b.rxx(0.123, innerTargets[0], innerTargets[1]); + return SmallVector{q0, q1}; + }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } void tripleNestedCtrl(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(5); - b.ctrl({q[0]}, {q[1], q[2], q[3], q[4]}, [&](mlir::ValueRange targets) { + b.ctrl({q[0]}, {q[1], q[2], q[3], q[4]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = b.ctrl( {targets[0]}, {targets[1], targets[2], targets[3]}, - [&](mlir::ValueRange innerTargets) { + [&](ValueRange innerTargets) { const auto& [innerInnerControlsOut, innerInnerTargetsOut] = b.ctrl({innerTargets[0]}, {innerTargets[1], innerTargets[2]}, - [&](mlir::ValueRange innerInnerTargets) { + [&](ValueRange innerInnerTargets) { auto [q0, q1] = b.rxx(0.123, innerInnerTargets[0], innerInnerTargets[1]); - return llvm::SmallVector{q0, q1}; + return SmallVector{q0, q1}; }); - return llvm::to_vector(llvm::concat( - innerInnerControlsOut, innerInnerTargetsOut)); + return llvm::to_vector( + llvm::concat(innerInnerControlsOut, innerInnerTargetsOut)); }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } void doubleNestedCtrlTwoQubits(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(6); - b.ctrl({q[0], q[1]}, {q[2], q[3], q[4], q[5]}, [&](mlir::ValueRange targets) { + b.ctrl({q[0], q[1]}, {q[2], q[3], q[4], q[5]}, [&](ValueRange targets) { const auto& [innerControlsOut, innerTargetsOut] = b.ctrl({targets[0], targets[1]}, {targets[2], targets[3]}, - [&](mlir::ValueRange innerTargets) { + [&](ValueRange innerTargets) { auto [q0, q1] = b.rxx(0.123, innerTargets[0], innerTargets[1]); - return llvm::SmallVector{q0, q1}; + return SmallVector{q0, q1}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); } void ctrlInvSandwich(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(4); - b.ctrl({q[0]}, {q[1], q[2], q[3]}, [&](mlir::ValueRange targets) { + b.ctrl({q[0]}, {q[1], q[2], q[3]}, [&](ValueRange targets) { auto inner = b.inv( - {targets[0], targets[1], targets[2]}, - [&](mlir::ValueRange innerTargets) { + {targets[0], targets[1], targets[2]}, [&](ValueRange innerTargets) { auto [innerControlsOut, innerTargetsOut] = b.ctrl({innerTargets[0]}, {innerTargets[1], innerTargets[2]}, - [&](mlir::ValueRange innerInnerTargets) { + [&](ValueRange innerInnerTargets) { auto [q0, q1] = b.rxx(-0.123, innerInnerTargets[0], innerInnerTargets[1]); - return llvm::SmallVector{q0, q1}; + return SmallVector{q0, q1}; }); return llvm::to_vector( - llvm::concat(innerControlsOut, innerTargetsOut)); + llvm::concat(innerControlsOut, innerTargetsOut)); }); - return llvm::SmallVector{inner}; + return SmallVector{inner}; }); } void nestedInv(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { - auto inner = - b.inv({qubits[0], qubits[1]}, [&](mlir::ValueRange innerQubits) { - auto [q0, q1] = b.rxx(0.123, innerQubits[0], innerQubits[1]); - return llvm::SmallVector{q0, q1}; - }); - return llvm::SmallVector{inner}; + b.inv({q[0], q[1]}, [&](ValueRange qubits) { + auto inner = b.inv({qubits[0], qubits[1]}, [&](ValueRange innerQubits) { + auto [q0, q1] = b.rxx(0.123, innerQubits[0], innerQubits[1]); + return SmallVector{q0, q1}; + }); + return SmallVector{inner}; }); } void tripleNestedInv(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); - b.inv({q[0], q[1]}, [&](mlir::ValueRange qubits) { - auto inner1 = - b.inv({qubits[0], qubits[1]}, [&](mlir::ValueRange innerQubits) { - auto inner2 = b.inv({innerQubits[0], innerQubits[1]}, - [&](mlir::ValueRange innerInnerQubits) { - auto [q0, q1] = - b.rxx(-0.123, innerInnerQubits[0], - innerInnerQubits[1]); - return llvm::SmallVector{q0, q1}; - }); - return llvm::SmallVector{inner2}; - }); - return llvm::SmallVector{inner1}; + b.inv({q[0], q[1]}, [&](ValueRange qubits) { + auto inner1 = b.inv({qubits[0], qubits[1]}, [&](ValueRange innerQubits) { + auto inner2 = b.inv( + {innerQubits[0], innerQubits[1]}, [&](ValueRange innerInnerQubits) { + auto [q0, q1] = + b.rxx(-0.123, innerInnerQubits[0], innerInnerQubits[1]); + return SmallVector{q0, q1}; + }); + return SmallVector{inner2}; + }); + return SmallVector{inner1}; }); } void invCtrlSandwich(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(3); - b.inv({q[0], q[1], q[2]}, [&](mlir::ValueRange qubits) { - const auto& [controlsOut, targetsOut] = b.ctrl( - {qubits[0]}, {qubits[1], qubits[2]}, [&](mlir::ValueRange targets) { - auto inner = b.inv( - {targets[0], targets[1]}, [&](mlir::ValueRange innerQubits) { + b.inv({q[0], q[1], q[2]}, [&](ValueRange qubits) { + const auto& [controlsOut, targetsOut] = + b.ctrl({qubits[0]}, {qubits[1], qubits[2]}, [&](ValueRange targets) { + auto inner = + b.inv({targets[0], targets[1]}, [&](ValueRange innerQubits) { auto [q0, q1] = b.rxx(0.123, innerQubits[0], innerQubits[1]); - return llvm::SmallVector{q0, q1}; + return SmallVector{q0, q1}; }); - return llvm::SmallVector{inner}; + return SmallVector{inner}; }); - return llvm::to_vector(llvm::concat(controlsOut, targetsOut)); + return llvm::to_vector(llvm::concat(controlsOut, targetsOut)); }); } @@ -2088,9 +2041,9 @@ void simpleIf(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); auto q0 = b.h(q[0]); auto [measuredQubit, measureResult] = b.measure(q0); - b.qcoIf(measureResult, measuredQubit, [&](mlir::ValueRange qubits) { + b.qcoIf(measureResult, measuredQubit, [&](ValueRange qubits) { auto innerQubit = b.x(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); } @@ -2098,10 +2051,10 @@ void ifTwoQubits(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(2); auto q0 = b.h(q[0]); auto [measuredQubit, measureResult] = b.measure(q0); - b.qcoIf(measureResult, {measuredQubit, q[1]}, [&](mlir::ValueRange qubits) { + b.qcoIf(measureResult, {measuredQubit, q[1]}, [&](ValueRange qubits) { auto innerQubit0 = b.x(qubits[0]); auto innerQubit1 = b.x(qubits[1]); - return llvm::SmallVector{innerQubit0, innerQubit1}; + return SmallVector{innerQubit0, innerQubit1}; }); } @@ -2111,13 +2064,13 @@ void ifElse(QCOProgramBuilder& b) { auto [measuredQubit, measureResult] = b.measure(q0); b.qcoIf( measureResult, {measuredQubit}, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.x(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.z(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); } @@ -2125,13 +2078,13 @@ void constantTrueIf(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.qcoIf( true, q.qubits, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.x(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.z(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); } @@ -2139,13 +2092,13 @@ void constantFalseIf(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); b.qcoIf( false, q.qubits, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.x(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.z(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); } @@ -2153,11 +2106,11 @@ void nestedTrueIf(QCOProgramBuilder& b) { auto q = b.allocQubitRegister(1); auto q0 = b.h(q[0]); auto [measuredQubit, measureResult] = b.measure(q0); - b.qcoIf(measureResult, measuredQubit, [&](mlir::ValueRange outerQubits) { + b.qcoIf(measureResult, measuredQubit, [&](ValueRange outerQubits) { auto innerResult = - b.qcoIf(measureResult, outerQubits, [&](mlir::ValueRange innerQubits) { + b.qcoIf(measureResult, outerQubits, [&](ValueRange innerQubits) { auto innerQubit = b.x(innerQubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); return llvm::to_vector(innerResult); }); @@ -2169,19 +2122,19 @@ void nestedFalseIf(QCOProgramBuilder& b) { auto [measuredQubit, measureResult] = b.measure(q0); b.qcoIf( measureResult, measuredQubit, - [&](mlir::ValueRange qubits) { + [&](ValueRange qubits) { auto innerQubit = b.x(qubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }, - [&](mlir::ValueRange outerQubits) { + [&](ValueRange outerQubits) { auto innerResult = b.qcoIf( measureResult, outerQubits, - [&](mlir::ValueRange innerQubits) { + [&](ValueRange innerQubits) { return llvm::to_vector(innerQubits); }, - [&](mlir::ValueRange innerQubits) { + [&](ValueRange innerQubits) { auto innerQubit = b.z(innerQubits[0]); - return llvm::SmallVector{innerQubit}; + return SmallVector{innerQubit}; }); return llvm::to_vector(innerResult); }); diff --git a/pyproject.toml b/pyproject.toml index 551cb08104..ea92e9e203 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ authors = [ ] keywords = ["MQT", "quantum-computing", "design-automation", "decision-diagrams", "zx-calculus"] license = "MIT" -license-files = ["LICENSE.md"] +license-files = ["LICENSE"] classifiers = [ "Development Status :: 5 - Production/Stable", @@ -205,6 +205,10 @@ unsafe-fixes = true show-fixes = true src = ["python"] +[tool.ruff.format] +preview = true +docstring-code-format = true + [tool.ruff.lint] select = ["ALL"] ignore = [ @@ -229,6 +233,7 @@ known-first-party = ["mqt.core"] "test/python/**" = ["T20", "INP001"] "docs/**" = ["T20", "INP001"] "noxfile.py" = ["T20", "TID251", "PLC0415"] +"mlir/unittests/**/*.py" = ["INP001", "T201"] "*.pyi" = ["D418", "E501", "PYI021"] "*.ipynb" = [ "D", # pydocstyle @@ -242,9 +247,6 @@ known-first-party = ["mqt.core"] [tool.ruff.lint.pydocstyle] convention = "google" -[tool.ruff.format] -docstring-code-format = true - [tool.typos] default.extend-ignore-re = [ @@ -262,14 +264,13 @@ optin = "optin" nd = "nd" -[tool.repo-review] -ignore = [ - "GH200", # We use Renovate instead of Dependabot - "MY100", # We use ty instead of mypy - "PC140", # We use ty instead of mypy - "PC160", # We use a mirror of crate-ci/typos - "PC170", # We do not use rST files anymore -] +[tool.repo-review.ignore] +GH200 = "We use Renovate instead of Dependabot" +MY100 = "We use ty instead of mypy" +PC111 = "We use ruff instead of blacken-docs" +PC140 = "We use ty instead of mypy" +PC160 = "We use a mirror of crate-ci/typos" +PC170 = "We do not use rST files anymore" [tool.cibuildwheel] @@ -366,5 +367,5 @@ dev = [ {include-group = "test"}, "lit>=18.1.8", "nox>=2025.11.12", - "ty==0.0.29", + "ty==0.0.31", ] diff --git a/python/mqt/core/plugins/qiskit/provider.py b/python/mqt/core/plugins/qiskit/provider.py index f3e0378fe8..5d44c55ae9 100644 --- a/python/mqt/core/plugins/qiskit/provider.py +++ b/python/mqt/core/plugins/qiskit/provider.py @@ -110,7 +110,7 @@ def backends(self, name: str | None = None) -> list[QDMIBackend]: """ # Filter by name substring if specified if name is not None: - return [b for b in self._backends if name in b.name] + return [b for b in self._backends if b.name is not None and name in b.name] return self._backends diff --git a/src/zx/FunctionalityConstruction.cpp b/src/zx/FunctionalityConstruction.cpp index 23697afaef..519fde6fd8 100644 --- a/src/zx/FunctionalityConstruction.cpp +++ b/src/zx/FunctionalityConstruction.cpp @@ -140,6 +140,32 @@ void FunctionalityConstruction::addCphase(ZXDiagram& diag, addZSpider(diag, target, qubits, newPhase); } +void FunctionalityConstruction::addMcphase(ZXDiagram& diag, + const PiExpression& phase, + const std::vector& controls, + const Qubit target, + std::vector& qubits) { + auto newConst = phase.getConst() / 2; + auto newPhase = phase / 2.0; + newPhase.setConst(newConst); + addZSpider(diag, target, qubits, newPhase); + addMcx(diag, controls, target, qubits); + addZSpider(diag, target, qubits, -newPhase); + addMcx(diag, controls, target, qubits); + switch (controls.size()) { + case 1: + addZSpider(diag, controls[0], qubits, newPhase); + return; + case 2: + addCphase(diag, newPhase, controls[0], controls[1], qubits); + return; + default: + addMcphase(diag, newPhase, + std::vector(controls.begin(), controls.end() - 1), + controls.back(), qubits); + } +} + void FunctionalityConstruction::addRzz( ZXDiagram& diag, const PiExpression& phase, const Qubit target, const Qubit target2, std::vector& qubits, @@ -207,6 +233,23 @@ void FunctionalityConstruction::addRzx( } } +void FunctionalityConstruction::addMcrzz( + ZXDiagram& diag, const PiExpression& phase, + const std::vector& controls, const Qubit target, const Qubit target2, + std::vector& qubits, + const std::optional& unconvertedPhase) { + addRzz(diag, phase / 2, target, target2, qubits, + unconvertedPhase.has_value() + ? std::optional(unconvertedPhase.value() / 2) + : std::nullopt); + addMcx(diag, controls, target, qubits); + addRzz(diag, -phase / 2, target, target2, qubits, + unconvertedPhase.has_value() + ? std::optional(-unconvertedPhase.value() / 2) + : std::nullopt); + addMcx(diag, controls, target, qubits); +} + void FunctionalityConstruction::addDcx(ZXDiagram& diag, const Qubit qubit1, const Qubit qubit2, std::vector& qubits) { @@ -263,10 +306,10 @@ void FunctionalityConstruction::addXXminusYY( addRz(diag, PiExpression(-PiRational(1, 2)), qubit1, qubits); } -void FunctionalityConstruction::addSwap(ZXDiagram& diag, const Qubit target, +void FunctionalityConstruction::addSwap(ZXDiagram& diag, const Qubit target1, const Qubit target2, std::vector& qubits) { - const auto c = static_cast(target); + const auto c = static_cast(target1); const auto t = static_cast(target2); const auto s0 = qubits[t]; @@ -279,13 +322,27 @@ void FunctionalityConstruction::addSwap(ZXDiagram& diag, const Qubit target, const auto col = vData->col + 1; const auto t0 = diag.addVertex(target2, col); - const auto t1 = diag.addVertex(target, col); + const auto t1 = diag.addVertex(target1, col); diag.addEdge(s0, t1); diag.addEdge(s1, t0); qubits[t] = t0; qubits[c] = t1; } +void FunctionalityConstruction::addMcswap(ZXDiagram& diag, + const std::vector& controls, + const Qubit target1, + const Qubit target2, + std::vector& qubits) { + + std::vector mcxControls = controls; + mcxControls.emplace_back(target2); + + addCnot(diag, target1, target2, qubits); + addMcx(diag, mcxControls, target1, qubits); + addCnot(diag, target1, target2, qubits); +} + void FunctionalityConstruction::addCcx(ZXDiagram& diag, const Qubit ctrl0, const Qubit ctrl1, const Qubit target, std::vector& qubits) { @@ -307,6 +364,118 @@ void FunctionalityConstruction::addCcx(ZXDiagram& diag, const Qubit ctrl0, addCnot(diag, ctrl0, ctrl1, qubits); } +void FunctionalityConstruction::addCcz(ZXDiagram& diag, const Qubit ctrl0, + const Qubit ctrl1, const Qubit target, + std::vector& qubits) { + addCnot(diag, ctrl1, target, qubits); + addZSpider(diag, target, qubits, PiExpression(PiRational(-1, 4))); + addCnot(diag, ctrl0, target, qubits); + addZSpider(diag, target, qubits, PiExpression(PiRational(1, 4))); + addCnot(diag, ctrl1, target, qubits); + addZSpider(diag, ctrl1, qubits, PiExpression(PiRational(1, 4))); + addZSpider(diag, target, qubits, PiExpression(PiRational(-1, 4))); + addCnot(diag, ctrl0, target, qubits); + addZSpider(diag, target, qubits, PiExpression(PiRational(1, 4))); + addCnot(diag, ctrl0, ctrl1, qubits); + addZSpider(diag, ctrl0, qubits, PiExpression(PiRational(1, 4))); + addZSpider(diag, ctrl1, qubits, PiExpression(PiRational(-1, 4))); + addCnot(diag, ctrl0, ctrl1, qubits); +} + +void FunctionalityConstruction::addCrx(ZXDiagram& diag, + const PiExpression& phase, + const Qubit control, const Qubit target, + std::vector& qubits) { + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addCrz(diag, phase, control, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); +} + +void FunctionalityConstruction::addMcrx(ZXDiagram& diag, + const PiExpression& phase, + const std::vector& controls, + const Qubit target, + std::vector& qubits) { + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addMcrz(diag, phase, controls, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); +} + +void FunctionalityConstruction::addCrz(ZXDiagram& diag, + const PiExpression& phase, + const Qubit control, const Qubit target, + std::vector& qubits) { + // CRZ decomposition uses reversed CNOT direction + addZSpider(diag, target, qubits, phase / 2); + // NOLINTNEXTLINE(readability-suspicious-call-argument) + addCnot(diag, target, control, qubits); + addZSpider(diag, control, qubits, -phase / 2); + // NOLINTNEXTLINE(readability-suspicious-call-argument) + addCnot(diag, target, control, qubits); +} + +void FunctionalityConstruction::addMcrz(ZXDiagram& diag, + const PiExpression& phase, + std::vector controls, + const Qubit target, + std::vector& qubits) { + const Qubit nextControl = controls.back(); + controls.pop_back(); + + addCrz(diag, phase / 2, nextControl, target, qubits); + addMcx(diag, controls, target, qubits); + addCrz(diag, -phase / 2, nextControl, target, qubits); + addMcx(diag, controls, target, qubits); +} + +void FunctionalityConstruction::addMcx(ZXDiagram& diag, + std::vector controls, + const Qubit target, + std::vector& qubits) { + switch (controls.size()) { + case 1: + addCnot(diag, controls.front(), target, qubits); + return; + case 2: + addCcx(diag, controls.front(), controls.back(), target, qubits); + return; + default: + const auto half = static_cast((controls.size() + 1) / 2); + const std::vector first(controls.begin(), controls.begin() + half); + const std::vector second(controls.begin() + half, controls.end()); + + addRx(diag, PiExpression(PiRational(1, 4)), target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addMcx(diag, first, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addRx(diag, PiExpression(-PiRational(1, 4)), target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addMcx(diag, second, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addRx(diag, PiExpression(PiRational(1, 4)), target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addMcx(diag, first, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addRx(diag, PiExpression(-PiRational(1, 4)), target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addMcx(diag, second, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + const Qubit lastControl = controls.back(); + controls.pop_back(); + addMcphase(diag, PiExpression(PiRational(1, 2)), controls, lastControl, + qubits); + } +} + +void FunctionalityConstruction::addMcz(ZXDiagram& diag, + const std::vector& controls, + const Qubit target, + std::vector& qubits) { + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); + addMcx(diag, controls, target, qubits); + addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); +} + FunctionalityConstruction::op_it FunctionalityConstruction::parseOp(ZXDiagram& diag, op_it it, op_it end, std::vector& qubits, @@ -516,7 +685,6 @@ FunctionalityConstruction::parseOp(ZXDiagram& diag, op_it it, op_it end, qc::toString(op->getType())); } } else if (op->getNcontrols() == 1 && op->getNtargets() == 1) { - // two-qubit controlled gates const auto target = static_cast(p.at(op->getTargets().front())); const auto ctrl = static_cast(p.at((*op->getControls().begin()).qubit)); @@ -538,10 +706,15 @@ FunctionalityConstruction::parseOp(ZXDiagram& diag, op_it it, op_it end, qubits[static_cast(target)], EdgeType::Hadamard); break; + case qc::OpType::RZ: + addCrz(diag, parseParam(op.get(), 0), ctrl, target, qubits); + break; + case qc::OpType::RX: + addCrx(diag, parseParam(op.get(), 0), ctrl, target, qubits); + break; case qc::OpType::I: break; - case qc::OpType::P: addCphase(diag, parseParam(op.get(), 0), ctrl, target, qubits); break; @@ -565,8 +738,7 @@ FunctionalityConstruction::parseOp(ZXDiagram& diag, op_it it, op_it end, throw ZXException("Unsupported Controlled Operation: " + qc::toString(op->getType())); } - } else if (op->getNcontrols() == 2) { - // three-qubit controlled gates (ccx or ccz) + } else if (op->getNcontrols() == 2 && op->getNtargets() == 1) { Qubit ctrl0 = 0; Qubit ctrl1 = 0; const auto target = static_cast(p.at(op->getTargets().front())); @@ -582,20 +754,142 @@ FunctionalityConstruction::parseOp(ZXDiagram& diag, op_it it, op_it end, case qc::OpType::X: addCcx(diag, ctrl0, ctrl1, target, qubits); break; - case qc::OpType::Z: + addCcz(diag, ctrl0, ctrl1, target, qubits); + break; + case qc::OpType::P: + addMcphase(diag, parseParam(op.get(), 0), {ctrl0, ctrl1}, target, qubits); + break; + case qc::OpType::T: + addMcphase(diag, PiExpression{PiRational(1, 4)}, {ctrl0, ctrl1}, target, + qubits); + break; + case qc::OpType::Tdg: + addMcphase(diag, PiExpression{PiRational(-1, 4)}, {ctrl0, ctrl1}, target, + qubits); + break; + case qc::OpType::S: + addMcphase(diag, PiExpression{PiRational(1, 2)}, {ctrl0, ctrl1}, target, + qubits); + break; + case qc::OpType::Sdg: + addMcphase(diag, PiExpression{PiRational(-1, 2)}, {ctrl0, ctrl1}, target, + qubits); + break; + case qc::OpType::RZ: + addMcrz(diag, parseParam(op.get(), 0), {ctrl0, ctrl1}, target, qubits); + break; + case qc::OpType::RX: + addMcrx(diag, parseParam(op.get(), 0), {ctrl0, ctrl1}, target, qubits); + break; + default: + throw ZXException("Unsupported multi-control operation (" + + std::to_string(op->getNcontrols()) + + " ctrls): " + qc::toString(op->getType())); + } + } else if (op->getNtargets() == 1) { + const auto target = static_cast(p.at(op->getTargets().front())); + std::vector controls; + controls.reserve(op->getNcontrols()); + for (const auto& ctrl : op->getControls()) { + controls.emplace_back(static_cast(p.at(ctrl.qubit))); + } + switch (op->getType()) { + case qc::OpType::X: + addMcx(diag, controls, target, qubits); + break; + case qc::OpType::Z: + addMcz(diag, controls, target, qubits); + break; + case qc::OpType::P: + addMcphase(diag, parseParam(op.get(), 0), controls, target, qubits); + break; + case qc::OpType::T: + addMcphase(diag, PiExpression{PiRational(1, 4)}, controls, target, + qubits); + break; + case qc::OpType::Tdg: + addMcphase(diag, PiExpression{PiRational(-1, 4)}, controls, target, + qubits); + break; + case qc::OpType::S: + addMcphase(diag, PiExpression{PiRational(1, 2)}, controls, target, + qubits); + break; + case qc::OpType::Sdg: + addMcphase(diag, PiExpression{PiRational(-1, 2)}, controls, target, + qubits); + break; + case qc::OpType::RZ: + addMcrz(diag, parseParam(op.get(), 0), controls, target, qubits); + break; + case qc::OpType::RX: + addMcrx(diag, parseParam(op.get(), 0), controls, target, qubits); + break; + default: + throw ZXException("Unsupported multi-control operation (" + + std::to_string(op->getNcontrols()) + + " ctrls): " + qc::toString(op->getType())); + } + } else if (op->getNtargets() == 2) { + // at this point, op must have getNtargets() == 2 + // all 1-target cases handled above + const auto target = static_cast(p.at(op->getTargets().front())); + const auto target2 = static_cast(p.at(op->getTargets()[1])); + std::vector controls; + controls.reserve(op->getNcontrols()); + for (const auto& ctrl : op->getControls()) { + controls.emplace_back(static_cast(p.at(ctrl.qubit))); + } + switch (op->getType()) { + case qc::OpType::SWAP: + addMcswap(diag, controls, target, target2, qubits); + break; + case qc::OpType::RZZ: { + const auto& phase = parseParam(op.get(), 0); + if (phase.isConstant()) { + addMcrzz(diag, phase, controls, target, target2, qubits, + op->getParameter().at(0)); + } else { + addMcrzz(diag, phase, controls, target, target2, qubits); + } + break; + } + case qc::OpType::RXX: { + const auto& phase = parseParam(op.get(), 0); addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); - addCcx(diag, ctrl0, ctrl1, target, qubits); + addZSpider(diag, target2, qubits, PiExpression(), EdgeType::Hadamard); + if (phase.isConstant()) { + addMcrzz(diag, phase, controls, target, target2, qubits, + op->getParameter().at(0)); + } else { + addMcrzz(diag, phase, controls, target, target2, qubits); + } + addZSpider(diag, target2, qubits, PiExpression(), EdgeType::Hadamard); addZSpider(diag, target, qubits, PiExpression(), EdgeType::Hadamard); break; + } + case qc::OpType::RZX: { + const auto& phase = parseParam(op.get(), 0); + addZSpider(diag, target2, qubits, PiExpression(), EdgeType::Hadamard); + if (phase.isConstant()) { + addMcrzz(diag, phase, controls, target, target2, qubits, + op->getParameter().at(0)); + } else { + addMcrzz(diag, phase, controls, target, target2, qubits); + } + addZSpider(diag, target2, qubits, PiExpression(), EdgeType::Hadamard); + break; + } default: - throw ZXException("Unsupported Multi-control operation: " + - qc::toString(op->getType())); + throw ZXException("Unsupported multi-control operation (" + + std::to_string(op->getNcontrols()) + + " ctrls): " + qc::toString(op->getType())); } } else { - throw ZXException("Unsupported Multi-control operation (" + - std::to_string(op->getNcontrols()) + " ctrls)" + - qc::toString(op->getType())); + throw ZXException("Unsupported multi-control operation (" + + std::to_string(op->getNcontrols()) + + " ctrls): " + qc::toString(op->getType())); } return it + 1; } @@ -634,6 +928,7 @@ ZXDiagram FunctionalityConstruction::buildFunctionality( } return diag; } + bool FunctionalityConstruction::transformableToZX( const qc::QuantumComputation* qc) { return std::ranges::all_of( @@ -698,18 +993,36 @@ bool FunctionalityConstruction::transformableToZX(const qc::Operation* op) { case qc::OpType::I: case qc::OpType::P: case qc::OpType::T: - case qc::OpType::S: case qc::OpType::Tdg: + case qc::OpType::S: case qc::OpType::Sdg: + case qc::OpType::RX: + case qc::OpType::RZ: return true; - default: return false; } - } else if (op->getNcontrols() == 2) { + } else if (op->getNtargets() == 1) { switch (op->getType()) { case qc::OpType::X: case qc::OpType::Z: + case qc::OpType::P: + case qc::OpType::T: + case qc::OpType::Tdg: + case qc::OpType::S: + case qc::OpType::Sdg: + case qc::OpType::RZ: + case qc::OpType::RX: + return true; + default: + return false; + } + } else if (op->getNtargets() == 2) { + switch (op->getType()) { + case qc::OpType::SWAP: + case qc::OpType::RZZ: + case qc::OpType::RXX: + case qc::OpType::RZX: return true; default: return false; @@ -725,6 +1038,7 @@ PiExpression FunctionalityConstruction::parseParam(const qc::Operation* op, } return PiExpression{PiRational{op->getParameter().at(i)}}; } + PiExpression FunctionalityConstruction::toPiExpr(const qc::SymbolOrNumber& param) { if (std::holds_alternative(param)) { @@ -732,4 +1046,5 @@ FunctionalityConstruction::toPiExpr(const qc::SymbolOrNumber& param) { } return std::get(param).convert(); } + } // namespace zx diff --git a/test/python/plugins/qiskit/test_provider.py b/test/python/plugins/qiskit/test_provider.py index 50f0e63b4c..7584f275ef 100644 --- a/test/python/plugins/qiskit/test_provider.py +++ b/test/python/plugins/qiskit/test_provider.py @@ -49,12 +49,16 @@ def test_provider_backends_filter_by_substring() -> None: # Filter by "QDMI" substring (should match "MQT Core DDSIM QDMI Device") filtered = provider.backends(name="QDMI") assert len(filtered) > 0 - assert all("QDMI" in b.name for b in filtered) + for backend in filtered: + assert backend.name is not None + assert "QDMI" in backend.name # Filter by "DDSIM" substring filtered_ddsim = provider.backends(name="DDSIM") assert len(filtered_ddsim) > 0 - assert all("DDSIM" in b.name for b in filtered_ddsim) + for backend in filtered_ddsim: + assert backend.name is not None + assert "DDSIM" in backend.name def test_provider_backends_filter_nonexistent_name() -> None: diff --git a/test/zx/test_zx_functionality.cpp b/test/zx/test_zx_functionality.cpp index 79edeec944..3667a47e1b 100644 --- a/test/zx/test_zx_functionality.cpp +++ b/test/zx/test_zx_functionality.cpp @@ -29,6 +29,7 @@ #include #include #include +#include namespace zx { @@ -39,6 +40,25 @@ class ZXFunctionalityTest : public ::testing::Test { qc::QuantumComputation qc; }; +void checkEquivalence(const qc::QuantumComputation& qc1, + const qc::QuantumComputation& qc2, + const std::vector& qubits) { + ASSERT_TRUE(FunctionalityConstruction::transformableToZX(&qc1)); + ASSERT_TRUE(FunctionalityConstruction::transformableToZX(&qc2)); + ASSERT_EQ(qc1.getNqubits(), qc2.getNqubits()); + + auto d1 = FunctionalityConstruction::buildFunctionality(&qc1); + auto d2 = FunctionalityConstruction::buildFunctionality(&qc2); + d1.concat(d2.invert()); + fullReduce(d1); + EXPECT_TRUE(d1.isIdentity()); + EXPECT_TRUE(d1.globalPhaseIsZero()); + for (const auto q : qubits) { + ASSERT_LT(q, qc1.getNqubits()); + EXPECT_TRUE(d1.connected(d1.getInput(q), d1.getOutput(q))); + } +} + } // namespace TEST_F(ZXFunctionalityTest, parseQasm) { @@ -97,70 +117,29 @@ TEST_F(ZXFunctionalityTest, complexCircuit) { std::stringstream ss{}; ss << "// i 1 0 2\n" << "// o 0 1 2\n" - << "OPENQASM 2.0;" - << "include \"qelib1.inc\";" - << "qreg q[3];" - << "sx q[0];" - << "sxdg q[0];" - << "h q[0];" - << "cx q[0],q[1];" - << "z q[1];" - << "x q[2];" - << "y q[0];" - << "rx(pi/4) q[0];" - << "rz(0.1) q[1];" - << "p(0.1) q[1];" - << "ry(pi/4) q[2];" - << "t q[0];" - << "s q[2];" - << "u2(pi/4, pi/4) q[1];" - << "u3(pi/4, pi/4, pi/4) q[2];" - << "barrier q[0],q[1],q[2];" - << "swap q[0],q[1];" - << "cz q[1],q[2];" - << "cp(pi/4) q[0],q[1];" - << "ctrl(2) @ x q[0],q[1],q[2];" - << "ctrl(2) @ z q[1],q[2],q[0];" - << "cp(pi/2) q[0], q[1];" - << "cp(pi/4) q[0], q[1];" - << "cp(pi/8) q[0], q[1];" - << "rzz(pi/4) q[0], q[1];" - << "rxx(pi/4) q[0], q[1];" - << "ryy(pi/4) q[0], q[1];" - << "rzx(pi/4) q[0], q[1];" - << "ecr q[0], q[1];" - << "dcx q[0], q[1];" - << "r(pi/8, pi/4) q[2];" - << "r(-pi/8, pi/4) q[2];" - << "dcx q[1], q[0];" - << "ecr q[0], q[1];" - << "rzx(-pi/4) q[0], q[1];" - << "ryy(-pi/4) q[0], q[1];" - << "rxx(-pi/4) q[0], q[1];" - << "rzz(-pi/4) q[0], q[1];" - << "cp(-pi/8) q[0], q[1];" - << "cp(-pi/4) q[0], q[1];" - << "cp(-pi/2) q[0], q[1];" - << "ctrl(2) @ z q[1],q[2],q[0];" - << "ctrl(2) @ x q[0],q[1],q[2];" - << "cp(-pi/4) q[0],q[1];" - << "cz q[1],q[2];" - << "cx q[1],q[0];" - << "cx q[0],q[1];" - << "cx q[1],q[0];" - << "u3(-pi/4,-pi/4,-pi/4) q[2];" - << "u2(-5*pi/4,3*pi/4) q[1];" - << "sdg q[2];" - << "tdg q[0];" - << "ry(-pi/4) q[2];" - << "p(-0.1) q[1];" - << "rz(-0.1) q[1];" - << "rx(-pi/4) q[0];" - << "y q[0];" - << "x q[2];" - << "z q[1];" - << "cx q[0],q[1];" - << "h q[0];\n"; + << "OPENQASM 2.0;" << "include \"qelib1.inc\";" << "qreg q[3];" + << "sx q[0];" << "sxdg q[0];" << "h q[0];" << "cx q[0],q[1];" << "z q[1];" + << "x q[2];" << "y q[0];" << "rx(pi/4) q[0];" << "rz(0.1) q[1];" + << "p(0.1) q[1];" << "ry(pi/4) q[2];" << "t q[0];" << "s q[2];" + << "u2(pi/4, pi/4) q[1];" << "u3(pi/4, pi/4, pi/4) q[2];" + << "barrier q[0],q[1],q[2];" << "swap q[0],q[1];" << "cz q[1],q[2];" + << "cp(pi/4) q[0],q[1];" << "ctrl(2) @ x q[0],q[1],q[2];" + << "ctrl(2) @ z q[1],q[2],q[0];" << "cp(pi/2) q[0], q[1];" + << "cp(pi/4) q[0], q[1];" << "cp(pi/8) q[0], q[1];" + << "rzz(pi/4) q[0], q[1];" << "rxx(pi/4) q[0], q[1];" + << "ryy(pi/4) q[0], q[1];" << "rzx(pi/4) q[0], q[1];" << "ecr q[0], q[1];" + << "dcx q[0], q[1];" << "r(pi/8, pi/4) q[2];" << "r(-pi/8, pi/4) q[2];" + << "dcx q[1], q[0];" << "ecr q[0], q[1];" << "rzx(-pi/4) q[0], q[1];" + << "ryy(-pi/4) q[0], q[1];" << "rxx(-pi/4) q[0], q[1];" + << "rzz(-pi/4) q[0], q[1];" << "cp(-pi/8) q[0], q[1];" + << "cp(-pi/4) q[0], q[1];" << "cp(-pi/2) q[0], q[1];" + << "ctrl(2) @ z q[1],q[2],q[0];" << "ctrl(2) @ x q[0],q[1],q[2];" + << "cp(-pi/4) q[0],q[1];" << "cz q[1],q[2];" << "cx q[1],q[0];" + << "cx q[0],q[1];" << "cx q[1],q[0];" << "u3(-pi/4,-pi/4,-pi/4) q[2];" + << "u2(-5*pi/4,3*pi/4) q[1];" << "sdg q[2];" << "tdg q[0];" + << "ry(-pi/4) q[2];" << "p(-0.1) q[1];" << "rz(-0.1) q[1];" + << "rx(-pi/4) q[0];" << "y q[0];" << "x q[2];" << "z q[1];" + << "cx q[0],q[1];" << "h q[0];\n"; qc = qasm3::Importer::import(ss); EXPECT_TRUE(FunctionalityConstruction::transformableToZX(&qc)); @@ -193,7 +172,7 @@ TEST_F(ZXFunctionalityTest, nestedCompoundGate) { } TEST_F(ZXFunctionalityTest, Phase) { - using namespace qc::literals; + qc = qc::QuantumComputation(2); qc.p(PI / 4, 0); qc.cp(PI / 4, 1, 0); @@ -225,18 +204,149 @@ TEST_F(ZXFunctionalityTest, Compound) { EXPECT_TRUE(diag.isIdentity()); } -TEST_F(ZXFunctionalityTest, UnsupportedMultiControl) { - using namespace qc::literals; +TEST_F(ZXFunctionalityTest, CRZ) { + qc = qc::QuantumComputation(2); + qc.crz(PI / 2, 0, 1); + + auto qcPrime = qc::QuantumComputation(2); + + qcPrime.cx(0, 1); + qcPrime.rz(-PI / 4, 1); + qcPrime.cx(0, 1); + qcPrime.rz(PI / 4, 1); + + checkEquivalence(qc, qcPrime, {0, 1}); +} + +TEST_F(ZXFunctionalityTest, CCZ) { + + qc = qc::QuantumComputation(3); + qc.mcz({1, 2}, 0); + + auto qcPrime = qc::QuantumComputation(3); + qcPrime.h(0); + qcPrime.mcx({1, 2}, 0); + qcPrime.h(0); + + checkEquivalence(qc, qcPrime, {0, 1, 2}); +} + +TEST_F(ZXFunctionalityTest, MCX) { + qc = qc::QuantumComputation(4); qc.mcx({1, 2, 3}, 0); - EXPECT_FALSE(FunctionalityConstruction::transformableToZX(&qc)); - EXPECT_THROW(const ZXDiagram diag = - FunctionalityConstruction::buildFunctionality(&qc), - ZXException); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcx({3, 2, 1}, 0); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCX0) { + + qc = qc::QuantumComputation(1); + qc.mcx({}, 0); + + auto qcPrime = qc::QuantumComputation(1); + + qcPrime.x(0); + checkEquivalence(qc, qcPrime, {0}); +} + +TEST_F(ZXFunctionalityTest, MCX1) { + + qc = qc::QuantumComputation(2); + qc.mcx({1}, 0); + + auto qcPrime = qc::QuantumComputation(2); + + qcPrime.cx(1, 0); + + checkEquivalence(qc, qcPrime, {0, 1}); +} + +TEST_F(ZXFunctionalityTest, MCZ) { + + qc = qc::QuantumComputation(4); + qc.mcz({1, 2, 3}, 0); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcz({1, 2, 3}, 0); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCZ0) { + + qc = qc::QuantumComputation(1); + qc.mcz({}, 0); + + auto qcPrime = qc::QuantumComputation(1); + qcPrime.z(0); + + checkEquivalence(qc, qcPrime, {0}); +} + +TEST_F(ZXFunctionalityTest, MCZ1) { + + qc = qc::QuantumComputation(2); + qc.mcz({1}, 0); + + auto qcPrime = qc::QuantumComputation(2); + qcPrime.cz(1, 0); + + checkEquivalence(qc, qcPrime, {0, 1}); +} + +TEST_F(ZXFunctionalityTest, MCZ2) { + + qc = qc::QuantumComputation(4); + qc.mcz({1, 2}, 0); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.h(0); + qcPrime.mcx({1, 2}, 0); + qcPrime.h(0); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCRZ) { + + qc = qc::QuantumComputation(4); + qc.mcrz(PI / 4, {1, 2, 3}, 0); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.h(0); + qcPrime.mcrx(PI / 4, {1, 2, 3}, 0); + qcPrime.h(0); + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCRZ0) { + + qc = qc::QuantumComputation(1); + qc.mcrz(PI / 4, {}, 0); + + auto qcPrime = qc::QuantumComputation(1); + qcPrime.rz(PI / 4, 0); + + checkEquivalence(qc, qcPrime, {0}); +} + +TEST_F(ZXFunctionalityTest, MCRZ1) { + + qc = qc::QuantumComputation(2); + qc.mcrz(PI / 4, {1}, 0); + + auto qcPrime = qc::QuantumComputation(2); + qcPrime.crz(PI / 4, 1, 0); + + checkEquivalence(qc, qcPrime, {0, 1}); } TEST_F(ZXFunctionalityTest, UnsupportedControl) { - using namespace qc::literals; + qc = qc::QuantumComputation(2); qc.cy(1, 0); EXPECT_FALSE(FunctionalityConstruction::transformableToZX(&qc)); @@ -246,7 +356,7 @@ TEST_F(ZXFunctionalityTest, UnsupportedControl) { } TEST_F(ZXFunctionalityTest, UnsupportedControl2) { - using namespace qc::literals; + qc = qc::QuantumComputation(3); qc.mcy({1, 2}, 0); EXPECT_FALSE(FunctionalityConstruction::transformableToZX(&qc)); @@ -255,6 +365,26 @@ TEST_F(ZXFunctionalityTest, UnsupportedControl2) { ZXException); } +TEST_F(ZXFunctionalityTest, UnsupportedControl3) { + + qc = qc::QuantumComputation(4); + qc.mcy({1, 2, 3}, 0); + EXPECT_FALSE(FunctionalityConstruction::transformableToZX(&qc)); + EXPECT_THROW(const ZXDiagram diag = + FunctionalityConstruction::buildFunctionality(&qc), + ZXException); +} + +TEST_F(ZXFunctionalityTest, UnsupportedTwoTargetControl) { + + qc = qc::QuantumComputation(4); + qc.mcdcx({2, 3}, 0, 1); + EXPECT_FALSE(FunctionalityConstruction::transformableToZX(&qc)); + EXPECT_THROW(const ZXDiagram diag = + FunctionalityConstruction::buildFunctionality(&qc), + ZXException); +} + TEST_F(ZXFunctionalityTest, InitialLayout) { qc = qc::QuantumComputation(2); qc::Permutation layout{}; @@ -268,13 +398,7 @@ TEST_F(ZXFunctionalityTest, InitialLayout) { qcPrime.x(1); qcPrime.z(0); - auto d = FunctionalityConstruction::buildFunctionality(&qc); - auto dPrime = FunctionalityConstruction::buildFunctionality(&qcPrime); - - d.concat(dPrime); - - fullReduce(d); - EXPECT_TRUE(d.isIdentity()); + checkEquivalence(qc, qcPrime, {0, 1}); } TEST_F(ZXFunctionalityTest, FromSymbolic) { @@ -295,21 +419,13 @@ TEST_F(ZXFunctionalityTest, RZ) { qc.rz(PI / 8, 0); auto qcPrime = qc::QuantumComputation(1); - qcPrime.p(PI / 8, 0); - - auto d = FunctionalityConstruction::buildFunctionality(&qc); - auto dPrime = FunctionalityConstruction::buildFunctionality(&qcPrime); + qcPrime.rz(PI / 8, 0); - d.concat(dPrime.invert()); - - fullReduce(d); - EXPECT_FALSE(d.isIdentity()); - EXPECT_FALSE(d.globalPhaseIsZero()); - EXPECT_TRUE(d.connected(d.getInput(0), d.getOutput(0))); + checkEquivalence(qc, qcPrime, {0}); } TEST_F(ZXFunctionalityTest, ISWAP) { - using namespace qc::literals; + qc = qc::QuantumComputation(2); qc.iswap(0, 1); @@ -319,17 +435,9 @@ TEST_F(ZXFunctionalityTest, ISWAP) { qcPrime.h(0); qcPrime.cx(0, 1); qcPrime.cx(1, 0); - qc.h(1); + qcPrime.h(1); - auto d = FunctionalityConstruction::buildFunctionality(&qc); - auto dPrime = FunctionalityConstruction::buildFunctionality(&qcPrime); - - d.concat(dPrime.invert()); - - fullReduce(d); - EXPECT_TRUE(d.isIdentity()); - EXPECT_TRUE(d.globalPhaseIsZero()); - EXPECT_TRUE(d.connected(d.getInput(0), d.getOutput(0))); + checkEquivalence(qc, qcPrime, {0, 1}); } TEST_F(ZXFunctionalityTest, XXplusYY) { @@ -355,17 +463,7 @@ TEST_F(ZXFunctionalityTest, XXplusYY) { qcPrime.rz(qc::PI_2, 0); qcPrime.rz(-beta, 1); - auto d = FunctionalityConstruction::buildFunctionality(&qc); - - auto dPrime = FunctionalityConstruction::buildFunctionality(&qcPrime); - - d.concat(dPrime.invert()); - - fullReduce(d); - - EXPECT_TRUE(d.isIdentity()); - EXPECT_TRUE(d.globalPhaseIsZero()); - EXPECT_TRUE(d.connected(d.getInput(0), d.getOutput(0))); + checkEquivalence(qc, qcPrime, {0, 1}); } TEST_F(ZXFunctionalityTest, XXminusYY) { @@ -391,16 +489,191 @@ TEST_F(ZXFunctionalityTest, XXminusYY) { qcPrime.rz(qc::PI_2, 0); qcPrime.rz(beta, 1); - auto d = FunctionalityConstruction::buildFunctionality(&qc); + checkEquivalence(qc, qcPrime, {0, 1}); +} + +TEST_F(ZXFunctionalityTest, SWAP) { + qc = qc::QuantumComputation(2); + qc.swap(0, 1); + + auto qcPrime = qc::QuantumComputation(2); + qcPrime.cx(1, 0); + qcPrime.cx(0, 1); + qcPrime.cx(1, 0); + + checkEquivalence(qc, qcPrime, {0, 1}); +} - auto dPrime = FunctionalityConstruction::buildFunctionality(&qcPrime); +TEST_F(ZXFunctionalityTest, CSWAP) { + qc = qc::QuantumComputation(3); + qc.mcswap({0}, 1, 2); + + auto qcPrime = qc::QuantumComputation(3); + qcPrime.cx(1, 2); + qcPrime.mcx({0, 2}, 1); + qcPrime.cx(1, 2); + + checkEquivalence(qc, qcPrime, {0, 1, 2}); +} + +TEST_F(ZXFunctionalityTest, MCSWAP) { + qc = qc::QuantumComputation(4); + qc.mcswap({0, 1}, 2, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.cx(2, 3); + qcPrime.mcx({0, 1, 3}, 2); + qcPrime.cx(2, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCRzz) { + qc = qc::QuantumComputation(4); + qc.mcrzz(qc::PI_2 / 3, {0, 1}, 2, 3); + qc.mcrzz(2 * qc::PI, {0, 1}, 2, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcrzz(qc::PI_2 / 3, {0, 1}, 2, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCRxx) { + qc = qc::QuantumComputation(4); + qc.mcrxx(qc::PI_2 / 3, {0, 1}, 2, 3); + qc.mcrxx(2 * qc::PI, {0, 1}, 2, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcrxx(qc::PI_2 / 3, {0, 1}, 2, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCRzx) { + qc = qc::QuantumComputation(4); + qc.mcrzx(qc::PI_2 / 3, {0, 1}, 2, 3); + qc.mcrzx(2 * qc::PI, {0, 1}, 2, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcrzx(qc::PI_2 / 3, {0, 1}, 2, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCRx0) { + qc = qc::QuantumComputation(1); + qc.mcrx(PI / 4, {}, 0); - d.concat(dPrime.invert()); + auto qcPrime = qc::QuantumComputation(1); + qcPrime.mcrx(PI / 4, {}, 0); + checkEquivalence(qc, qcPrime, {0}); +} + +TEST_F(ZXFunctionalityTest, MCRx1) { + qc = qc::QuantumComputation(2); + qc.mcrx(PI / 4, {0}, 1); + + auto qcPrime = qc::QuantumComputation(2); + qcPrime.mcrx(PI / 4, {0}, 1); + checkEquivalence(qc, qcPrime, {0, 1}); +} + +TEST_F(ZXFunctionalityTest, MCRx2) { + qc = qc::QuantumComputation(3); + qc.mcrx(PI / 4, {0, 1}, 2); + + auto qcPrime = qc::QuantumComputation(3); + qcPrime.mcrx(PI / 4, {0, 1}, 2); + checkEquivalence(qc, qcPrime, {0, 1, 2}); +} + +TEST_F(ZXFunctionalityTest, MCRx3) { + qc = qc::QuantumComputation(4); + qc.mcrx(PI / 4, {0, 1, 2}, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcrx(PI / 4, {0, 1, 2}, 3); + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCPhase) { + qc = qc::QuantumComputation(4); + qc.mcp(PI / 2, {0, 1, 2}, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcp(PI / 2, {0, 1, 2}, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCS) { + qc = qc::QuantumComputation(4); + qc.mcs({0, 1, 2}, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcs({0, 1, 2}, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCSdg) { + qc = qc::QuantumComputation(4); + qc.mcsdg({0, 1, 2}, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mcsdg({0, 1, 2}, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCT) { + qc = qc::QuantumComputation(4); + qc.mct({0, 1, 2}, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mct({0, 1, 2}, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCTdg) { + qc = qc::QuantumComputation(4); + qc.mctdg({0, 1, 2}, 3); + + auto qcPrime = qc::QuantumComputation(4); + qcPrime.mctdg({0, 1, 2}, 3); + + checkEquivalence(qc, qcPrime, {0, 1, 2, 3}); +} + +TEST_F(ZXFunctionalityTest, MCPhase2) { + qc = qc::QuantumComputation(3); + qc.mcp(PI / 2, {0, 1}, 2); + + auto qcPrime = qc::QuantumComputation(3); + qcPrime.mcp(PI / 2, {0, 1}, 2); + + checkEquivalence(qc, qcPrime, {0, 1, 2}); +} + +TEST_F(ZXFunctionalityTest, MCS2) { + qc = qc::QuantumComputation(3); + qc.mcs({0, 1}, 2); + + auto qcPrime = qc::QuantumComputation(3); + qcPrime.mcs({0, 1}, 2); + + checkEquivalence(qc, qcPrime, {0, 1, 2}); +} + +TEST_F(ZXFunctionalityTest, MCT2) { + qc = qc::QuantumComputation(3); + qc.mct({0, 1}, 2); - fullReduce(d); + auto qcPrime = qc::QuantumComputation(3); + qcPrime.mct({0, 1}, 2); - EXPECT_TRUE(d.isIdentity()); - EXPECT_TRUE(d.globalPhaseIsZero()); - EXPECT_TRUE(d.connected(d.getInput(0), d.getOutput(0))); + checkEquivalence(qc, qcPrime, {0, 1, 2}); } } // namespace zx diff --git a/uv.lock b/uv.lock index dad4804cee..5e00eb850d 100644 --- a/uv.lock +++ b/uv.lock @@ -133,11 +133,11 @@ wheels = [ [[package]] name = "certifi" -version = "2026.2.25" +version = "2026.4.22" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, + { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" }, ] [[package]] @@ -329,14 +329,14 @@ wheels = [ [[package]] name = "click" -version = "8.3.2" +version = "8.3.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/57/75/31212c6bf2503fdf920d87fee5d7a86a2e3bcf444984126f13d8e4016804/click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5", size = 302856, upload-time = "2026-04-03T19:14:45.118Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/63/f9e1ea081ce35720d8b92acde70daaedace594dc93b693c869e0d5910718/click-8.3.3.tar.gz", hash = "sha256:398329ad4837b2ff7cbe1dd166a4c0f8900c3ca3a218de04466f38f6497f18a2", size = 328061, upload-time = "2026-04-22T15:11:27.506Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d", size = 108379, upload-time = "2026-04-03T19:14:43.505Z" }, + { url = "https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl", hash = "sha256:a2bf429bb3033c89fa4936ffb35d5cb471e3719e1f3c8a7c3fff0b8314305613", size = 110502, upload-time = "2026-04-22T15:11:25.044Z" }, ] [[package]] @@ -807,11 +807,11 @@ wheels = [ [[package]] name = "filelock" -version = "3.25.2" +version = "3.29.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/b8/00651a0f559862f3bb7d6f7477b192afe3f583cc5e26403b44e59a55ab34/filelock-3.25.2.tar.gz", hash = "sha256:b64ece2b38f4ca29dd3e810287aa8c48182bbecd1ae6e9ae126c9b35f1382694", size = 40480, upload-time = "2026-03-11T20:45:38.487Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/fe/997687a931ab51049acce6fa1f23e8f01216374ea81374ddee763c493db5/filelock-3.29.0.tar.gz", hash = "sha256:69974355e960702e789734cb4871f884ea6fe50bd8404051a3530bc07809cf90", size = 57571, upload-time = "2026-04-19T15:39:10.068Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a4/a5/842ae8f0c08b61d6484b52f99a03510a3a72d23141942d216ebe81fefbce/filelock-3.25.2-py3-none-any.whl", hash = "sha256:ca8afb0da15f229774c9ad1b455ed96e85a81373065fb10446672f64444ddf70", size = 26759, upload-time = "2026-03-11T20:45:37.437Z" }, + { url = "https://files.pythonhosted.org/packages/81/47/dd9a212ef6e343a6857485ffe25bba537304f1913bdbed446a23f7f592e1/filelock-3.29.0-py3-none-any.whl", hash = "sha256:96f5f6344709aa1572bbf631c640e4ebeeb519e08da902c39a001882f30ac258", size = 39812, upload-time = "2026-04-19T15:39:08.752Z" }, ] [[package]] @@ -963,11 +963,11 @@ wheels = [ [[package]] name = "idna" -version = "3.11" +version = "3.13" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/cc/762dfb036166873f0059f3b7de4565e1b5bc3d6f28a414c13da27e442f99/idna-3.13.tar.gz", hash = "sha256:585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242", size = 194210, upload-time = "2026-04-22T16:42:42.314Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/5d/13/ad7d7ca3808a898b4612b6fe93cde56b53f3034dcde235acb1f0e1df24c6/idna-3.13-py3-none-any.whl", hash = "sha256:892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3", size = 68629, upload-time = "2026-04-22T16:42:40.909Z" }, ] [[package]] @@ -1009,8 +1009,7 @@ dependencies = [ { name = "comm" }, { name = "debugpy" }, { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "ipython", version = "9.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "ipython", version = "9.13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyter-client" }, { name = "jupyter-core" }, { name = "matplotlib-inline" }, @@ -1053,34 +1052,7 @@ wheels = [ [[package]] name = "ipython" -version = "9.10.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version == '3.11.*' and sys_platform == 'win32'", - "python_full_version == '3.11.*' and sys_platform == 'emscripten'", - "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", -] -dependencies = [ - { name = "colorama", marker = "python_full_version == '3.11.*' and sys_platform == 'win32'" }, - { name = "decorator", marker = "python_full_version == '3.11.*'" }, - { name = "ipython-pygments-lexers", marker = "python_full_version == '3.11.*'" }, - { name = "jedi", marker = "python_full_version == '3.11.*'" }, - { name = "matplotlib-inline", marker = "python_full_version == '3.11.*'" }, - { name = "pexpect", marker = "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit", marker = "python_full_version == '3.11.*'" }, - { name = "pygments", marker = "python_full_version == '3.11.*'" }, - { name = "stack-data", marker = "python_full_version == '3.11.*'" }, - { name = "traitlets", marker = "python_full_version == '3.11.*'" }, - { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c5/25/daae0e764047b0a2480c7bbb25d48f4f509b5818636562eeac145d06dfee/ipython-9.10.1.tar.gz", hash = "sha256:e170e9b2a44312484415bdb750492699bf329233b03f2557a9692cce6466ada4", size = 4426663, upload-time = "2026-03-27T09:53:26.244Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/01/09/ba70f8d662d5671687da55ad2cc0064cf795b15e1eea70907532202e7c97/ipython-9.10.1-py3-none-any.whl", hash = "sha256:82d18ae9fb9164ded080c71ef92a182ee35ee7db2395f67616034bebb020a232", size = 622827, upload-time = "2026-03-27T09:53:24.566Z" }, -] - -[[package]] -name = "ipython" -version = "9.12.0" +version = "9.13.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.14' and sys_platform == 'win32'", @@ -1092,22 +1064,27 @@ resolution-markers = [ "python_full_version == '3.12.*' and sys_platform == 'win32'", "python_full_version == '3.12.*' and sys_platform == 'emscripten'", "python_full_version == '3.12.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "colorama", marker = "python_full_version >= '3.12' and sys_platform == 'win32'" }, - { name = "decorator", marker = "python_full_version >= '3.12'" }, - { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.12'" }, - { name = "jedi", marker = "python_full_version >= '3.12'" }, - { name = "matplotlib-inline", marker = "python_full_version >= '3.12'" }, - { name = "pexpect", marker = "python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit", marker = "python_full_version >= '3.12'" }, - { name = "pygments", marker = "python_full_version >= '3.12'" }, - { name = "stack-data", marker = "python_full_version >= '3.12'" }, - { name = "traitlets", marker = "python_full_version >= '3.12'" }, + { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version >= '3.11'" }, + { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" }, + { name = "jedi", marker = "python_full_version >= '3.11'" }, + { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" }, + { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" }, + { name = "psutil", marker = "python_full_version >= '3.11'" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, + { name = "stack-data", marker = "python_full_version >= '3.11'" }, + { name = "traitlets", marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3a/73/7114f80a8f9cabdb13c27732dce24af945b2923dcab80723602f7c8bc2d8/ipython-9.12.0.tar.gz", hash = "sha256:01daa83f504b693ba523b5a407246cabde4eb4513285a3c6acaff11a66735ee4", size = 4428879, upload-time = "2026-03-27T09:42:45.312Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/c4/87cda5842cf5c31837c06ddb588e11c3c35d8ece89b7a0108c06b8c9b00a/ipython-9.13.0.tar.gz", hash = "sha256:7e834b6afc99f020e3f05966ced34792f40267d64cb1ea9043886dab0dde5967", size = 4430549, upload-time = "2026-04-24T12:24:55.221Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/22/906c8108974c673ebef6356c506cebb6870d48cedea3c41e949e2dd556bb/ipython-9.12.0-py3-none-any.whl", hash = "sha256:0f2701e8ee86e117e37f50563205d36feaa259d2e08d4a6bc6b6d74b18ce128d", size = 625661, upload-time = "2026-03-27T09:42:42.831Z" }, + { url = "https://files.pythonhosted.org/packages/b9/86/3060e8029b7cc505cce9a0137431dda81d0a3fde93a8f0f50ee0bf37a795/ipython-9.13.0-py3-none-any.whl", hash = "sha256:57f9d4639e20818d328d287c7b549af3d05f12486ea8f2e7f73e52a36ec4d201", size = 627274, upload-time = "2026-04-24T12:24:53.038Z" }, ] [[package]] @@ -1491,7 +1468,7 @@ wheels = [ [[package]] name = "matplotlib" -version = "3.10.8" +version = "3.10.9" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, @@ -1506,62 +1483,62 @@ dependencies = [ { name = "pyparsing" }, { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", size = 8239828, upload-time = "2025-12-10T22:55:02.313Z" }, - { url = "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", size = 8128050, upload-time = "2025-12-10T22:55:04.997Z" }, - { url = "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", size = 8700452, upload-time = "2025-12-10T22:55:07.47Z" }, - { url = "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", size = 9534928, upload-time = "2025-12-10T22:55:10.566Z" }, - { url = "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", size = 9586377, upload-time = "2025-12-10T22:55:12.362Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", size = 8128127, upload-time = "2025-12-10T22:55:14.436Z" }, - { url = "https://files.pythonhosted.org/packages/f8/86/de7e3a1cdcfc941483af70609edc06b83e7c8a0e0dc9ac325200a3f4d220/matplotlib-3.10.8-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6be43b667360fef5c754dda5d25a32e6307a03c204f3c0fc5468b78fa87b4160", size = 8251215, upload-time = "2025-12-10T22:55:16.175Z" }, - { url = "https://files.pythonhosted.org/packages/fd/14/baad3222f424b19ce6ad243c71de1ad9ec6b2e4eb1e458a48fdc6d120401/matplotlib-3.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2b336e2d91a3d7006864e0990c83b216fcdca64b5a6484912902cef87313d78", size = 8139625, upload-time = "2025-12-10T22:55:17.712Z" }, - { url = "https://files.pythonhosted.org/packages/8f/a0/7024215e95d456de5883e6732e708d8187d9753a21d32f8ddb3befc0c445/matplotlib-3.10.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efb30e3baaea72ce5928e32bab719ab4770099079d66726a62b11b1ef7273be4", size = 8712614, upload-time = "2025-12-10T22:55:20.8Z" }, - { url = "https://files.pythonhosted.org/packages/5a/f4/b8347351da9a5b3f41e26cf547252d861f685c6867d179a7c9d60ad50189/matplotlib-3.10.8-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d56a1efd5bfd61486c8bc968fa18734464556f0fb8e51690f4ac25d85cbbbbc2", size = 9540997, upload-time = "2025-12-10T22:55:23.258Z" }, - { url = "https://files.pythonhosted.org/packages/9e/c0/c7b914e297efe0bc36917bf216b2acb91044b91e930e878ae12981e461e5/matplotlib-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238b7ce5717600615c895050239ec955d91f321c209dd110db988500558e70d6", size = 9596825, upload-time = "2025-12-10T22:55:25.217Z" }, - { url = "https://files.pythonhosted.org/packages/6f/d3/a4bbc01c237ab710a1f22b4da72f4ff6d77eb4c7735ea9811a94ae239067/matplotlib-3.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:18821ace09c763ec93aef5eeff087ee493a24051936d7b9ebcad9662f66501f9", size = 8135090, upload-time = "2025-12-10T22:55:27.162Z" }, - { url = "https://files.pythonhosted.org/packages/89/dd/a0b6588f102beab33ca6f5218b31725216577b2a24172f327eaf6417d5c9/matplotlib-3.10.8-cp311-cp311-win_arm64.whl", hash = "sha256:bab485bcf8b1c7d2060b4fcb6fc368a9e6f4cd754c9c2fea281f4be21df394a2", size = 8012377, upload-time = "2025-12-10T22:55:29.185Z" }, - { url = "https://files.pythonhosted.org/packages/9e/67/f997cdcbb514012eb0d10cd2b4b332667997fb5ebe26b8d41d04962fa0e6/matplotlib-3.10.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:64fcc24778ca0404ce0cb7b6b77ae1f4c7231cdd60e6778f999ee05cbd581b9a", size = 8260453, upload-time = "2025-12-10T22:55:30.709Z" }, - { url = "https://files.pythonhosted.org/packages/7e/65/07d5f5c7f7c994f12c768708bd2e17a4f01a2b0f44a1c9eccad872433e2e/matplotlib-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9a5ca4ac220a0cdd1ba6bcba3608547117d30468fefce49bb26f55c1a3d5c58", size = 8148321, upload-time = "2025-12-10T22:55:33.265Z" }, - { url = "https://files.pythonhosted.org/packages/3e/f3/c5195b1ae57ef85339fd7285dfb603b22c8b4e79114bae5f4f0fcf688677/matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04", size = 8716944, upload-time = "2025-12-10T22:55:34.922Z" }, - { url = "https://files.pythonhosted.org/packages/00/f9/7638f5cc82ec8a7aa005de48622eecc3ed7c9854b96ba15bd76b7fd27574/matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f", size = 9550099, upload-time = "2025-12-10T22:55:36.789Z" }, - { url = "https://files.pythonhosted.org/packages/57/61/78cd5920d35b29fd2a0fe894de8adf672ff52939d2e9b43cb83cd5ce1bc7/matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466", size = 9613040, upload-time = "2025-12-10T22:55:38.715Z" }, - { url = "https://files.pythonhosted.org/packages/30/4e/c10f171b6e2f44d9e3a2b96efa38b1677439d79c99357600a62cc1e9594e/matplotlib-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:dd80ecb295460a5d9d260df63c43f4afbdd832d725a531f008dad1664f458adf", size = 8142717, upload-time = "2025-12-10T22:55:41.103Z" }, - { url = "https://files.pythonhosted.org/packages/f1/76/934db220026b5fef85f45d51a738b91dea7d70207581063cd9bd8fafcf74/matplotlib-3.10.8-cp312-cp312-win_arm64.whl", hash = "sha256:3c624e43ed56313651bc18a47f838b60d7b8032ed348911c54906b130b20071b", size = 8012751, upload-time = "2025-12-10T22:55:42.684Z" }, - { url = "https://files.pythonhosted.org/packages/3d/b9/15fd5541ef4f5b9a17eefd379356cf12175fe577424e7b1d80676516031a/matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6", size = 8261076, upload-time = "2025-12-10T22:55:44.648Z" }, - { url = "https://files.pythonhosted.org/packages/8d/a0/2ba3473c1b66b9c74dc7107c67e9008cb1782edbe896d4c899d39ae9cf78/matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1", size = 8148794, upload-time = "2025-12-10T22:55:46.252Z" }, - { url = "https://files.pythonhosted.org/packages/75/97/a471f1c3eb1fd6f6c24a31a5858f443891d5127e63a7788678d14e249aea/matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486", size = 8718474, upload-time = "2025-12-10T22:55:47.864Z" }, - { url = "https://files.pythonhosted.org/packages/01/be/cd478f4b66f48256f42927d0acbcd63a26a893136456cd079c0cc24fbabf/matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce", size = 9549637, upload-time = "2025-12-10T22:55:50.048Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7c/8dc289776eae5109e268c4fb92baf870678dc048a25d4ac903683b86d5bf/matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6", size = 9613678, upload-time = "2025-12-10T22:55:52.21Z" }, - { url = "https://files.pythonhosted.org/packages/64/40/37612487cc8a437d4dd261b32ca21fe2d79510fe74af74e1f42becb1bdb8/matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149", size = 8142686, upload-time = "2025-12-10T22:55:54.253Z" }, - { url = "https://files.pythonhosted.org/packages/66/52/8d8a8730e968185514680c2a6625943f70269509c3dcfc0dcf7d75928cb8/matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645", size = 8012917, upload-time = "2025-12-10T22:55:56.268Z" }, - { url = "https://files.pythonhosted.org/packages/b5/27/51fe26e1062f298af5ef66343d8ef460e090a27fea73036c76c35821df04/matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077", size = 8305679, upload-time = "2025-12-10T22:55:57.856Z" }, - { url = "https://files.pythonhosted.org/packages/2c/1e/4de865bc591ac8e3062e835f42dd7fe7a93168d519557837f0e37513f629/matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22", size = 8198336, upload-time = "2025-12-10T22:55:59.371Z" }, - { url = "https://files.pythonhosted.org/packages/c6/cb/2f7b6e75fb4dce87ef91f60cac4f6e34f4c145ab036a22318ec837971300/matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39", size = 8731653, upload-time = "2025-12-10T22:56:01.032Z" }, - { url = "https://files.pythonhosted.org/packages/46/b3/bd9c57d6ba670a37ab31fb87ec3e8691b947134b201f881665b28cc039ff/matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565", size = 9561356, upload-time = "2025-12-10T22:56:02.95Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3d/8b94a481456dfc9dfe6e39e93b5ab376e50998cddfd23f4ae3b431708f16/matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a", size = 9614000, upload-time = "2025-12-10T22:56:05.411Z" }, - { url = "https://files.pythonhosted.org/packages/bd/cd/bc06149fe5585ba800b189a6a654a75f1f127e8aab02fd2be10df7fa500c/matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958", size = 8220043, upload-time = "2025-12-10T22:56:07.551Z" }, - { url = "https://files.pythonhosted.org/packages/e3/de/b22cf255abec916562cc04eef457c13e58a1990048de0c0c3604d082355e/matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5", size = 8062075, upload-time = "2025-12-10T22:56:09.178Z" }, - { url = "https://files.pythonhosted.org/packages/3c/43/9c0ff7a2f11615e516c3b058e1e6e8f9614ddeca53faca06da267c48345d/matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f", size = 8262481, upload-time = "2025-12-10T22:56:10.885Z" }, - { url = "https://files.pythonhosted.org/packages/6f/ca/e8ae28649fcdf039fda5ef554b40a95f50592a3c47e6f7270c9561c12b07/matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b", size = 8151473, upload-time = "2025-12-10T22:56:12.377Z" }, - { url = "https://files.pythonhosted.org/packages/f1/6f/009d129ae70b75e88cbe7e503a12a4c0670e08ed748a902c2568909e9eb5/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d", size = 9553896, upload-time = "2025-12-10T22:56:14.432Z" }, - { url = "https://files.pythonhosted.org/packages/f5/26/4221a741eb97967bc1fd5e4c52b9aa5a91b2f4ec05b59f6def4d820f9df9/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008", size = 9824193, upload-time = "2025-12-10T22:56:16.29Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/3abf75f38605772cf48a9daf5821cd4f563472f38b4b828c6fba6fa6d06e/matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c", size = 9615444, upload-time = "2025-12-10T22:56:18.155Z" }, - { url = "https://files.pythonhosted.org/packages/93/a5/de89ac80f10b8dc615807ee1133cd99ac74082581196d4d9590bea10690d/matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11", size = 8272719, upload-time = "2025-12-10T22:56:20.366Z" }, - { url = "https://files.pythonhosted.org/packages/69/ce/b006495c19ccc0a137b48083168a37bd056392dee02f87dba0472f2797fe/matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8", size = 8144205, upload-time = "2025-12-10T22:56:22.239Z" }, - { url = "https://files.pythonhosted.org/packages/68/d9/b31116a3a855bd313c6fcdb7226926d59b041f26061c6c5b1be66a08c826/matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50", size = 8305785, upload-time = "2025-12-10T22:56:24.218Z" }, - { url = "https://files.pythonhosted.org/packages/1e/90/6effe8103f0272685767ba5f094f453784057072f49b393e3ea178fe70a5/matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908", size = 8198361, upload-time = "2025-12-10T22:56:26.787Z" }, - { url = "https://files.pythonhosted.org/packages/d7/65/a73188711bea603615fc0baecca1061429ac16940e2385433cc778a9d8e7/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a", size = 9561357, upload-time = "2025-12-10T22:56:28.953Z" }, - { url = "https://files.pythonhosted.org/packages/f4/3d/b5c5d5d5be8ce63292567f0e2c43dde9953d3ed86ac2de0a72e93c8f07a1/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1", size = 9823610, upload-time = "2025-12-10T22:56:31.455Z" }, - { url = "https://files.pythonhosted.org/packages/4d/4b/e7beb6bbd49f6bae727a12b270a2654d13c397576d25bd6786e47033300f/matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c", size = 9614011, upload-time = "2025-12-10T22:56:33.85Z" }, - { url = "https://files.pythonhosted.org/packages/7c/e6/76f2813d31f032e65f6f797e3f2f6e4aab95b65015924b1c51370395c28a/matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b", size = 8362801, upload-time = "2025-12-10T22:56:36.107Z" }, - { url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" }, - { url = "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", size = 8237252, upload-time = "2025-12-10T22:56:39.529Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", size = 8124693, upload-time = "2025-12-10T22:56:41.758Z" }, - { url = "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", size = 8702205, upload-time = "2025-12-10T22:56:43.415Z" }, - { url = "https://files.pythonhosted.org/packages/04/30/3afaa31c757f34b7725ab9d2ba8b48b5e89c2019c003e7d0ead143aabc5a/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6da7c2ce169267d0d066adcf63758f0604aa6c3eebf67458930f9d9b79ad1db1", size = 8249198, upload-time = "2025-12-10T22:56:45.584Z" }, - { url = "https://files.pythonhosted.org/packages/48/2f/6334aec331f57485a642a7c8be03cb286f29111ae71c46c38b363230063c/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9153c3292705be9f9c64498a8872118540c3f4123d1a1c840172edf262c8be4a", size = 8136817, upload-time = "2025-12-10T22:56:47.339Z" }, - { url = "https://files.pythonhosted.org/packages/73/e4/6d6f14b2a759c622f191b2d67e9075a3f56aaccb3be4bb9bb6890030d0a0/matplotlib-3.10.8-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ae029229a57cd1e8fe542485f27e7ca7b23aa9e8944ddb4985d0bc444f1eca2", size = 8713867, upload-time = "2025-12-10T22:56:48.954Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233, upload-time = "2026-04-24T00:14:13.539Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/6f/340b04986e67aac6f66c5145ce68bf72c64bed30f92c8913499a6e6b8f99/matplotlib-3.10.9-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77210dce9cb8153dffc967efaae990543392563d5a376d4dd8539bebcb0ed217", size = 8296625, upload-time = "2026-04-24T00:11:43.376Z" }, + { url = "https://files.pythonhosted.org/packages/bb/2f/127081eb83162053ebb9678ceac64220b93a663e0167432566e9c7c82aab/matplotlib-3.10.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1e7698ac9868428e84d2c967424803b2472ff7167d9d6590d4204ed775343c3b", size = 8188790, upload-time = "2026-04-24T00:11:46.556Z" }, + { url = "https://files.pythonhosted.org/packages/fc/b7/d8bcec2626c35f96972bff656299fef4578113ea6193c8fdad324710410c/matplotlib-3.10.9-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1aa972116abb4c9d201bf245620b433726cb6856f3bef6a78f776a00f5c92d37", size = 8769389, upload-time = "2026-04-24T00:11:48.959Z" }, + { url = "https://files.pythonhosted.org/packages/12/49/b78e214a527ea732033b7f4d37f7afb504d74ba9d134bd47938230dfb8b1/matplotlib-3.10.9-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae2f11957b27ce53497dd4d7b235c4d4f1faf383dfb39d0c5beb833bff883294", size = 9589657, upload-time = "2026-04-24T00:11:51.915Z" }, + { url = "https://files.pythonhosted.org/packages/5f/15/5246f7b43beae19c74dfee651d58d6cc8112e06f77adb4e88cc04f2e3a23/matplotlib-3.10.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b049278ddce116aaa1c1377ebf58adea909132dfce0281cf7e3a1ea9fc2e2c65", size = 9651983, upload-time = "2026-04-24T00:11:54.766Z" }, + { url = "https://files.pythonhosted.org/packages/75/77/5acecfe672ba0fa1b8c0454f69ce155d1e6fc5852fa7206bf9afaf767121/matplotlib-3.10.9-cp310-cp310-win_amd64.whl", hash = "sha256:82834c3c292d24d3a8aae77cd2d20019de69d692a34a970e4fdb8d33e2ea3dda", size = 8199701, upload-time = "2026-04-24T00:11:58.389Z" }, + { url = "https://files.pythonhosted.org/packages/4c/8c/290f021104741fea63769c31494f5324c0cd249bf536a65a4350767b1f22/matplotlib-3.10.9-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:68cfdcede415f7c8f5577b03303dd94526cdb6d11036cecdc205e08733b2d2bb", size = 8306860, upload-time = "2026-04-24T00:12:01.207Z" }, + { url = "https://files.pythonhosted.org/packages/51/18/325cd32ece1120d1da51cc4e4294c6580190699490183fc2fe8cb6d61ec5/matplotlib-3.10.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfca0129678bd56379db26c52b5d77ed7de314c047492fbdc763aa7501710cfb", size = 8199254, upload-time = "2026-04-24T00:12:04.239Z" }, + { url = "https://files.pythonhosted.org/packages/79/db/e28c1b83e3680740aa78925f5fb2ae4d16207207419ad75ea9fe604f8676/matplotlib-3.10.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e436d155fa8a3399dc62683f8f5d0e2e50d25d0144a73edd73f82eec8f4abfb", size = 8777092, upload-time = "2026-04-24T00:12:06.793Z" }, + { url = "https://files.pythonhosted.org/packages/55/fa/3ce7adfe9ba101748f465211660d9c6374c876b671bdb8c2bb6d347e8b94/matplotlib-3.10.9-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56fc0bd271b00025c6edfdc7c2dcd247372c8e1544971d62e1dc7c17367e8bf9", size = 9595691, upload-time = "2026-04-24T00:12:09.706Z" }, + { url = "https://files.pythonhosted.org/packages/36/c4/6960a76686ed668f2c60f84e9799ba4c0d56abdb36b1577b60c1d061d1ec/matplotlib-3.10.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5a6104ed666402ba5106d7f36e0e0cdca4e8d7fa4d39708ca88019e2835a2eb", size = 9659771, upload-time = "2026-04-24T00:12:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0d/271aace3342157c64700c9ff4c59c7b392f3dbab393692e8db6fbe7ab96c/matplotlib-3.10.9-cp311-cp311-win_amd64.whl", hash = "sha256:d730e984eddf56974c3e72b6129c7ca462ac38dc624338f4b0b23eb23ecba00f", size = 8205112, upload-time = "2026-04-24T00:12:15.773Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ee/cb57ad4754f3e7b9174ce6ce66d9205fb827067e48a9f58ac09d7e7d6b77/matplotlib-3.10.9-cp311-cp311-win_arm64.whl", hash = "sha256:51bf0ddbdc598e060d46c16b5590708f81a1624cefbaaf62f6a81bf9285b8c80", size = 8132310, upload-time = "2026-04-24T00:12:18.645Z" }, + { url = "https://files.pythonhosted.org/packages/35/c6/5581e26c72233ebb2a2a6fed2d24fb7c66b4700120b813f51b0555acf0b6/matplotlib-3.10.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f0c3c28d9fbcc1fe7a03be236d73430cf6409c41fb2383a7ac52fe932b072cb1", size = 8319908, upload-time = "2026-04-24T00:12:21.323Z" }, + { url = "https://files.pythonhosted.org/packages/b7/18/4880dd762e40cd360c1bf06e890c5a97b997e91cb324602b1a19950ad5ce/matplotlib-3.10.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:41cb28c2bd769aa3e98322c6ab09854cbcc52ab69d2759d681bba3e327b2b320", size = 8216016, upload-time = "2026-04-24T00:12:23.4Z" }, + { url = "https://files.pythonhosted.org/packages/32/91/d024616abdba99e83120e07a20658976f6a343646710760c4a51df126029/matplotlib-3.10.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae20801130378b82d647ff5047c07316295b68dc054ca6b3c13519d0ea624285", size = 8789336, upload-time = "2026-04-24T00:12:26.096Z" }, + { url = "https://files.pythonhosted.org/packages/5c/04/030a2f61ef2158f5e4c259487a92ac877732499fb33d871585d89e03c42d/matplotlib-3.10.9-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c63ebcd8b4b169eb2f5c200552ae6b8be8999a005b6b507ed76fb8d7d674fe2", size = 9604602, upload-time = "2026-04-24T00:12:29.052Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c2/541e4d09d87bb6b5830fc28b4c887a9a8cf4e1c6cee698a8c05552ae2003/matplotlib-3.10.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d75d11c949914165976c621b2324f9ef162af7ebf4b057ddf95dd1dba7e5edcf", size = 9670966, upload-time = "2026-04-24T00:12:32.131Z" }, + { url = "https://files.pythonhosted.org/packages/04/a1/4571fc46e7702de8d0c2dc54ad1b2f8e29328dea3ee90831181f7353d93c/matplotlib-3.10.9-cp312-cp312-win_amd64.whl", hash = "sha256:d091f9d758b34aaaaa6331d13574bf01891d903b3dec59bfff458ef7551de5d6", size = 8217462, upload-time = "2026-04-24T00:12:35.226Z" }, + { url = "https://files.pythonhosted.org/packages/4b/d0/2269edb12aa30c13c8bcc9382892e39943ce1d28aab4ec296e0381798e81/matplotlib-3.10.9-cp312-cp312-win_arm64.whl", hash = "sha256:10cc5ce06d10231c36f40e875f3c7e8050362a4ee8f0ee5d29a6b3277d57bb42", size = 8136688, upload-time = "2026-04-24T00:12:37.442Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d3/8d4f6afbecb49fc04e060a57c0fce39ea51cc163a6bd87303ccd698e4fa6/matplotlib-3.10.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b580440f1ff81a0e34122051a3dfabb7e4b7f9e380629929bde0eff9af72165f", size = 8320331, upload-time = "2026-04-24T00:12:39.688Z" }, + { url = "https://files.pythonhosted.org/packages/63/d9/9e14bc7564bf92d5ffa801ae5fac819ce74b925dfb55e3ebde61a3bbad3e/matplotlib-3.10.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b1b745c489cd1a77a0dc1120a05dc87af9798faebc913601feb8c73d89bf2d1e", size = 8216461, upload-time = "2026-04-24T00:12:42.494Z" }, + { url = "https://files.pythonhosted.org/packages/8a/17/4402d0d14ccf1dfc70932600b68097fbbf9c898a4871d2cbbe79c7801a32/matplotlib-3.10.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8f3bcac1ca5ed000a6f4337d47ba67dfddf37ed6a46c15fd7f014997f7bf865f", size = 8790091, upload-time = "2026-04-24T00:12:44.789Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0b/322aeec06dd9b91411f92028b37d447342770a24392aa4813e317064dad5/matplotlib-3.10.9-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a8d66a55def891c33147ba3ba9bfcabf0b526a43764c818acbb4525e5ed0838", size = 9605027, upload-time = "2026-04-24T00:12:47.583Z" }, + { url = "https://files.pythonhosted.org/packages/74/88/5f13482f55e7b00bcfc09838b093c2456e1379978d2a146844aae05350ad/matplotlib-3.10.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d843374407c4017a6403b59c6c81606773d136f3259d5b6da3131bc814542cc2", size = 9671269, upload-time = "2026-04-24T00:12:50.878Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e0/0840fd2f93da988ec660b8ad1984abe9f25d2aed22a5e394ff1c68c88307/matplotlib-3.10.9-cp313-cp313-win_amd64.whl", hash = "sha256:f4399f64b3e94cd500195490972ae1ee81170df1636fa15364d157d5bdd7b921", size = 8217588, upload-time = "2026-04-24T00:12:53.784Z" }, + { url = "https://files.pythonhosted.org/packages/47/b9/d706d06dd605c49b9f83a2aed8c13e3e5db70697d7a80b7e3d7915de6b17/matplotlib-3.10.9-cp313-cp313-win_arm64.whl", hash = "sha256:ba7b3b8ef09eab7df0e86e9ae086faa433efbfbdb46afcb3aa16aabf779469a8", size = 8136913, upload-time = "2026-04-24T00:12:56.501Z" }, + { url = "https://files.pythonhosted.org/packages/9b/45/6e32d96978264c8ca8c4b1010adb955a1a49cfaf314e212bbc8908f04a61/matplotlib-3.10.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:09218df8a93712bd6ea133e83a153c755448cf7868316c531cffcc43f69d1cc9", size = 8368019, upload-time = "2026-04-24T00:12:58.896Z" }, + { url = "https://files.pythonhosted.org/packages/86/0a/c8e3d3bba245f0f7fc424937f8ff7ef77291a36af3edb97ccd78aa93d84f/matplotlib-3.10.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:82368699727bfb7b0182e1aa13082e3c08e092fa1a25d3e1fd92405bff96f6d4", size = 8264645, upload-time = "2026-04-24T00:13:01.406Z" }, + { url = "https://files.pythonhosted.org/packages/3d/aa/5bf5a14fe4fed73a4209a155606f8096ff797aad89c6c35179026571133e/matplotlib-3.10.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3225f4e1edcb8c86c884ddf79ebe20ecd0a67d30188f279897554ccd8fded4dc", size = 8802194, upload-time = "2026-04-24T00:13:03.702Z" }, + { url = "https://files.pythonhosted.org/packages/dd/5e/b4be852d6bba6fd15893fadf91ff26ae49cb91aac789e95dde9d342e664f/matplotlib-3.10.9-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de2445a0c6690d21b7eb6ce071cebad6d40a2e9bdf10d039074a96ba19797b99", size = 9622684, upload-time = "2026-04-24T00:13:06.647Z" }, + { url = "https://files.pythonhosted.org/packages/4c/3d/ed428c971139112ef730f62770654d609467346d09d4b62617e1afd68a5a/matplotlib-3.10.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b2b9516251cb89ff618d757daec0e2ed1bf21248013844a853d87ef85ab3081d", size = 9680790, upload-time = "2026-04-24T00:13:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/e7/09/052e884aaf2b985c63cb79f715f1d5b6a3eaa7de78f6a52b9dbc077d5b53/matplotlib-3.10.9-cp313-cp313t-win_amd64.whl", hash = "sha256:e9fae004b941b23ff2edcf1567a857ed77bafc8086ffa258190462328434faf8", size = 8287571, upload-time = "2026-04-24T00:13:13.087Z" }, + { url = "https://files.pythonhosted.org/packages/f4/38/ae27288e788c35a4250491422f3db7750366fc8c97d6f36fbdecfc1f5518/matplotlib-3.10.9-cp313-cp313t-win_arm64.whl", hash = "sha256:6b63d9c7c769b88ab81e10dc86e4e0607cf56817b9f9e6cf24b2a5f1693b8e38", size = 8188292, upload-time = "2026-04-24T00:13:15.546Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e6/3bd8afd04949f02eabc1c17115ea5255e19cacd4d06fc5abdde4eeb0052c/matplotlib-3.10.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:172db52c9e683f5d12eaf57f0f54834190e12581fe1cc2a19595a8f5acb4e77d", size = 8321276, upload-time = "2026-04-24T00:13:18.318Z" }, + { url = "https://files.pythonhosted.org/packages/41/86/86231232fff41c9f8e4a1a7d7a597d349a02527109c3af7d618366122139/matplotlib-3.10.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97e35e8d39ccc85859095e01a53847432ba9a53ddf7986f7a54a11b73d0e143f", size = 8218218, upload-time = "2026-04-24T00:13:20.974Z" }, + { url = "https://files.pythonhosted.org/packages/85/8f/becc9722cafc64f5d2eb0b7c1bf5f585271c618a45dbd8fabeb021f898b6/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aba1615dabe83188e19d4f75a253c6a08423e04c1425e64039f800050a69de6b", size = 9608145, upload-time = "2026-04-24T00:13:23.228Z" }, + { url = "https://files.pythonhosted.org/packages/32/5d/f7e914f7d9325abff4057cee62c0fa70263683189f774473cbfb534cd13b/matplotlib-3.10.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34cf8167e023ad956c15f36302911d5406bd99a9862c1a8499ea6f7c0e015dc2", size = 9885085, upload-time = "2026-04-24T00:13:25.849Z" }, + { url = "https://files.pythonhosted.org/packages/a5/fd/fa69f2221534e80cc5772ac2b7d222011a2acafc2ec7216d5dd174c864ae/matplotlib-3.10.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:59476c6d29d612b8e9bb6ce8c5b631be6ba8f9e3a2421f22a02b192c7dd28716", size = 9672358, upload-time = "2026-04-24T00:13:28.906Z" }, + { url = "https://files.pythonhosted.org/packages/ab/1a/5a4f747a8b271cbb024946d2dd3c913ab5032ba430626f8c3528ada96b4b/matplotlib-3.10.9-cp314-cp314-win_amd64.whl", hash = "sha256:336b9acc64d309063126edcdaca00db9373af3c476bb94388fe9c5a53ad13e6f", size = 8349970, upload-time = "2026-04-24T00:13:31.904Z" }, + { url = "https://files.pythonhosted.org/packages/64/dc/95d60ecaefe30680a154b52ea96ab4b0dab547f1fd6aa12f5fb655e89cae/matplotlib-3.10.9-cp314-cp314-win_arm64.whl", hash = "sha256:2dc9477819ffd78ad12a20df1d9d6a6bd4fec6aaa9072681465fddca052f1456", size = 8272785, upload-time = "2026-04-24T00:13:34.511Z" }, + { url = "https://files.pythonhosted.org/packages/70/a0/005d68bc8b8418300ce6591f18586910a8526806e2ab663933d9f20a41e9/matplotlib-3.10.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:da4e09638420548f31c354032a6250e473c68e5a4e96899b4844cf39ddea23fe", size = 8367999, upload-time = "2026-04-24T00:13:36.962Z" }, + { url = "https://files.pythonhosted.org/packages/22/05/1236cc9290be70b2498af20ca348add76e3fffe7f67b477db5133a84f3ea/matplotlib-3.10.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:345f6f68ecc8da0ca56fad2ea08fde1a115eda530079eca185d50a7bc3e146c6", size = 8264543, upload-time = "2026-04-24T00:13:39.851Z" }, + { url = "https://files.pythonhosted.org/packages/cd/c2/071f5a5ff6c5bd63aaaf2f45c811d9bf2ced94bde188d9e1a519e21d0cba/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4edcfbd8565339aa62f1cd4012f7180926fdbe71850f7b0d3c379c175cd6b66c", size = 9622800, upload-time = "2026-04-24T00:13:42.296Z" }, + { url = "https://files.pythonhosted.org/packages/95/57/da7d1f10a85624b9e7db68e069dd94e58dc41dbf9463c5921632ecbe3661/matplotlib-3.10.9-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be157fe17fc37cb95ac1d7374cf717ce9259616edec911a78d9d26dae8522d4", size = 9888561, upload-time = "2026-04-24T00:13:45.026Z" }, + { url = "https://files.pythonhosted.org/packages/67/b2/ef8d6bb59b0edb6c16c968b70f548aa13b54348972def5aa6ac85df67145/matplotlib-3.10.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:4e42042d54db34fda4e95a7bd3e5789c2a995d2dad3eb8850232ee534092fbbf", size = 9680884, upload-time = "2026-04-24T00:13:48.066Z" }, + { url = "https://files.pythonhosted.org/packages/61/1c/d21bfeb9931881ebe96bcfcff27c7ae4b160ae0ec291a714c42641a56d75/matplotlib-3.10.9-cp314-cp314t-win_amd64.whl", hash = "sha256:c27df8b3848f32a83d1767566595e43cfaa4460380974da06f4279a7ec143c39", size = 8432333, upload-time = "2026-04-24T00:13:51.008Z" }, + { url = "https://files.pythonhosted.org/packages/78/23/92493c3e6e1b635ccfff146f7b99e674808787915420373ac399283764c2/matplotlib-3.10.9-cp314-cp314t-win_arm64.whl", hash = "sha256:a49f1eadc84ca85fd72fa4e89e70e61bf86452df6f971af04b12c60761a0772c", size = 8324785, upload-time = "2026-04-24T00:13:53.633Z" }, + { url = "https://files.pythonhosted.org/packages/2c/2b/0e92ad0ac446633f928a1563db4aa8add407e1924faf0ded5b95b35afb27/matplotlib-3.10.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1872fb212a05b729e649754a72d5da61d03e0554d76e80303b6f83d1d2c0552b", size = 8293058, upload-time = "2026-04-24T00:13:56.339Z" }, + { url = "https://files.pythonhosted.org/packages/4b/23/74682fd369f5299ceda438fea2a0662e6383b85c9383fb9cdfcf04713e07/matplotlib-3.10.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:985f2238880e2e69093f588f5fe2e46771747febf0649f3cf7f7b7480875317f", size = 8186627, upload-time = "2026-04-24T00:13:58.623Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e8/368aab88f3c4cd8992800f31abfe0670c3e47540ba20a97e9fdbcde594b3/matplotlib-3.10.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6640f75af2c6148293caa0a2b39dd806a492dd66c8a8b04035813e33d0fd2585", size = 8764117, upload-time = "2026-04-24T00:14:01.684Z" }, + { url = "https://files.pythonhosted.org/packages/63/e2/9f66ca6a651a52abfe0d4964ce01439ed34f3f1e119de10ff3a07f403043/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:42fb814efabe95c06c1994d8ab5a8385f43a249e23badd3ba931d4308e5bca20", size = 8304420, upload-time = "2026-04-24T00:14:04.57Z" }, + { url = "https://files.pythonhosted.org/packages/e8/e8/467c03568218792906aa87b5e7bb379b605e056ed0c74fe00c051786d925/matplotlib-3.10.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f76e640a5268850bfda54b5131b1b1941cc685e42c5fa98ed9f2d64038308cba", size = 8197981, upload-time = "2026-04-24T00:14:07.233Z" }, + { url = "https://files.pythonhosted.org/packages/6f/87/afead29192170917537934c6aff4b008c805fff7b1ccea0c79120d96beda/matplotlib-3.10.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3fc0364dfbe1d07f6d15c5ebd0c5bf89e126916e5a8667dd4a7a6e84c36653d4", size = 8774002, upload-time = "2026-04-24T00:14:09.816Z" }, ] [[package]] @@ -1703,7 +1680,7 @@ dev = [ { name = "qiskit", extras = ["qasm3-import"], specifier = ">=1.0.0" }, { name = "scikit-build-core", specifier = ">=0.12.2" }, { name = "setuptools-scm", specifier = ">=9.2.2" }, - { name = "ty", specifier = "==0.0.29" }, + { name = "ty", specifier = "==0.0.31" }, ] docs = [ { name = "breathe", specifier = ">=4.36.0" }, @@ -1742,8 +1719,7 @@ dependencies = [ { name = "importlib-metadata" }, { name = "ipykernel" }, { name = "ipython", version = "8.39.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "ipython", version = "9.10.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, - { name = "ipython", version = "9.12.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "ipython", version = "9.13.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "jupyter-cache" }, { name = "myst-parser", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "myst-parser", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, @@ -2065,11 +2041,11 @@ parser = [ [[package]] name = "packaging" -version = "26.0" +version = "26.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] [[package]] @@ -2221,11 +2197,11 @@ wheels = [ [[package]] name = "pathspec" -version = "1.0.4" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2e/17/9c3094b822982b9f1ea666d8580ce59000f61f87c1663556fb72031ad9ec/pathspec-1.1.0.tar.gz", hash = "sha256:f5d7c555da02fd8dde3e4a2354b6aba817a89112fa8f333f7917a2a4834dd080", size = 133918, upload-time = "2026-04-23T01:46:22.298Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c9/8eed0486f074e9f1ca7f8ce5ad663e65f12fdab344028d658fa1b03d35e0/pathspec-1.1.0-py3-none-any.whl", hash = "sha256:574b128f7456bd899045ccd142dd446af7e6cfd0072d63ad73fbc55fbb4aaa42", size = 56264, upload-time = "2026-04-23T01:46:20.606Z" }, ] [[package]] @@ -2729,7 +2705,7 @@ wheels = [ [[package]] name = "qiskit" -version = "2.3.1" +version = "2.4.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "dill" }, @@ -2741,15 +2717,15 @@ dependencies = [ { name = "stevedore" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f9/9a/a366dd56b6a4520e5b2856e47d573c6813b4c3f62f28969a37205fd845ab/qiskit-2.3.1.tar.gz", hash = "sha256:7b3b7e1c8a50f7f423204143a1bd9f21bf27659c57459d582eaff4035d8d7f75", size = 3909915, upload-time = "2026-03-13T00:43:03.257Z" } +sdist = { url = "https://files.pythonhosted.org/packages/39/82/7a90609d283f8707d46e1a6771e16462922789223817b6e7033490dffcaa/qiskit-2.4.1.tar.gz", hash = "sha256:90203e0241184642fc4a55cf88e20633d412b00d0aa89e3af780648744e508cf", size = 4081678, upload-time = "2026-04-24T20:34:14.13Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/47/70/dba6afec66d2bf4df64b7059cd144bf0c775d998cb57b941d1065b331d28/qiskit-2.3.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:25a85465e8c860627e8a565cb9af824b2df1a2c42f5958b1a7b014aa085b32d6", size = 8614500, upload-time = "2026-03-13T02:30:24.247Z" }, - { url = "https://files.pythonhosted.org/packages/55/0b/47d67d58b4de120e10d49255b635429bc162a982b0cd58cf75b1a9f594fa/qiskit-2.3.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:b1404b72987d0c7db32203a87565775115f6a77b2a3539d40eb1e354df6d08bb", size = 7819922, upload-time = "2026-03-13T00:42:54.205Z" }, - { url = "https://files.pythonhosted.org/packages/02/f0/2af86ce0e11b06248bc6a66550acf98c19e606cb317c27bab4fbed2ce37f/qiskit-2.3.1-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68eec14320654065a158bf73b3d6fffaa31e865fe61b0db1510cfeeea76c87b0", size = 8176336, upload-time = "2026-03-13T00:42:56.891Z" }, - { url = "https://files.pythonhosted.org/packages/8a/fc/0bf71353c68b9090ed05e5c1d08730301c58a2b00f3001f0050f032bdcc9/qiskit-2.3.1-cp310-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:96545ad531c5e2ff6eecd348501620e0db0f3d73f49c20037ec3b5b2e9268f94", size = 8915234, upload-time = "2026-03-13T02:30:27.055Z" }, - { url = "https://files.pythonhosted.org/packages/1b/0c/19ffdb15d2e72156de406fe27cad26c6015071a385e6b48c6db87d8ce676/qiskit-2.3.1-cp310-abi3-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a1a030b4a0cd75f3299c9866a3e9489a02078efc343abd42eec74ad2a5f2fa80", size = 8513126, upload-time = "2026-03-13T02:30:30.288Z" }, - { url = "https://files.pythonhosted.org/packages/11/06/41f4197fcb3c38e99f22f400ea25375c9057190cd094cd6c0f97be730796/qiskit-2.3.1-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:350027279bb171e9746fee5756c674872df97f4608b079129d4edb25b5bb1fdd", size = 8833222, upload-time = "2026-03-13T00:42:59.092Z" }, - { url = "https://files.pythonhosted.org/packages/a5/db/6ddc52eaf22ab1f95ee6251ff7a375c0a1a7453b5864528992303b166198/qiskit-2.3.1-cp310-abi3-win_amd64.whl", hash = "sha256:f884abd92c0173e246705f89f8b4808badaa0e1b167ed3277a3cb83c2564b4b8", size = 8639580, upload-time = "2026-03-13T00:43:01.022Z" }, + { url = "https://files.pythonhosted.org/packages/dc/08/f9f3d0e6f51059b0da75aba5e3dee08a4ab85c1518b1efcc56c46db800e7/qiskit-2.4.1-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:421c4649418d96b8996b798f7984b29e53343ed7c13ebde38dad3af5625ea1c0", size = 9350133, upload-time = "2026-04-24T22:41:34.805Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ad/2bef5daf53bb18b735ec278ee3a98b83fe10167ea156e62fb562f0141404/qiskit-2.4.1-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:b0d14760caf84b9ce81b9380005947d028e993c8752e7f68a372b533a129a4bc", size = 8328821, upload-time = "2026-04-24T20:34:03.32Z" }, + { url = "https://files.pythonhosted.org/packages/e2/86/ed5929218f7943d33f37229406b42a413c1fd40c1312fe628371cc8ef563/qiskit-2.4.1-cp310-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:90ffe85b64d197e42cc90f21dd086bedd0609658e407f9a82b1ce3e3b4145eed", size = 8626333, upload-time = "2026-04-24T20:34:06.009Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c9/a7e3150e327c4328fbd66d42f72b6ecac201c6d75729c62972b2b5964101/qiskit-2.4.1-cp310-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:3314f8fe5742a1cb7bd67f9fe8302647fc78ffe9b20f22b30ee4a6c2d0d6d411", size = 9679264, upload-time = "2026-04-24T22:41:37.878Z" }, + { url = "https://files.pythonhosted.org/packages/f2/f8/c1b00ac0843089771152e650c6143825746c9c85d4dac855207e15c9e26e/qiskit-2.4.1-cp310-abi3-manylinux_2_28_s390x.whl", hash = "sha256:a46572a39c3fbb01561e97e2ac7ec3afe29e96090e8d8040e90c2059ab5b3055", size = 9187719, upload-time = "2026-04-24T22:41:40.925Z" }, + { url = "https://files.pythonhosted.org/packages/90/e8/9f1859218193ed89aef5d54b5385d891e808ff8830667bbc490d85507afd/qiskit-2.4.1-cp310-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:bb85390b4d6878ecc28fb54b78b128a24642a42eb3a5a58c4b144ee0730fece7", size = 9319379, upload-time = "2026-04-24T20:34:09.075Z" }, + { url = "https://files.pythonhosted.org/packages/11/6a/c9065d0f74178275963b44e57fd93530ed36089682f918553c3bceb9a8ba/qiskit-2.4.1-cp310-abi3-win_amd64.whl", hash = "sha256:91c8c8b0582a8d0dc46c0d3fd37896b2facfd99c54671ac557da9f643114e5e3", size = 9108769, upload-time = "2026-04-24T20:34:11.888Z" }, ] [package.optional-dependencies] @@ -3674,26 +3650,26 @@ wheels = [ [[package]] name = "ty" -version = "0.0.29" +version = "0.0.31" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/47/d5/853561de49fae38c519e905b2d8da9c531219608f1fccc47a0fc2c896980/ty-0.0.29.tar.gz", hash = "sha256:e7936cca2f691eeda631876c92809688dbbab68687c3473f526cd83b6a9228d8", size = 5469221, upload-time = "2026-04-05T15:01:21.328Z" } +sdist = { url = "https://files.pythonhosted.org/packages/31/cc/5ea5d3a72216c8c2bf77d83066dd4f3553532d0aacc03d4a8397dd9845e1/ty-0.0.31.tar.gz", hash = "sha256:4a4094292d9671caf3b510c7edf36991acd9c962bb5d97205374ffed9f541c45", size = 5516619, upload-time = "2026-04-15T15:47:59.87Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/03/b7/911f9962115acfa24e3b2ec9d4992dd994c38e8769e1b1d7680bb4d28a51/ty-0.0.29-py3-none-linux_armv6l.whl", hash = "sha256:b8a40955f7660d3eaceb0d964affc81b790c0765e7052921a5f861ff8a471c30", size = 10568206, upload-time = "2026-04-05T15:01:19.165Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c3/fcae2167d4c77a97269f92f11d1b43b03617f81de1283d5d05b43432110c/ty-0.0.29-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6b6849adae15b00bbe2d3c5b078967dcb62eba37d38936b8eeb4c81a82d2e3b8", size = 10442530, upload-time = "2026-04-05T15:01:28.471Z" }, - { url = "https://files.pythonhosted.org/packages/97/33/5a6bfa240cfcb9c36046ae2459fa9ea23238d20130d8656ff5ac4d6c012a/ty-0.0.29-py3-none-macosx_11_0_arm64.whl", hash = "sha256:dcdd9b17209788152f7b7ea815eda07989152325052fe690013537cc7904ce49", size = 9915735, upload-time = "2026-04-05T15:01:10.365Z" }, - { url = "https://files.pythonhosted.org/packages/b3/1e/318f45fae232118e81a6306c30f50de42c509c412128d5bd231eab699ffb/ty-0.0.29-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d8ed4789bae78ffaf94462c0d25589a734cab0366b86f2bbcb1bb90e1a7a169", size = 10419748, upload-time = "2026-04-05T15:01:32.375Z" }, - { url = "https://files.pythonhosted.org/packages/a9/a8/5687872e2ab5a0f7dd4fd8456eac31e9381ad4dc74961f6f29965ad4dd91/ty-0.0.29-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91ec374b8565e0ad0900011c24641ebbef2da51adbd4fb69ff3280c8a7eceb02", size = 10394738, upload-time = "2026-04-05T15:01:06.473Z" }, - { url = "https://files.pythonhosted.org/packages/de/68/015d118097eeb95e6a44c4abce4c0a28b7b9dfb3085b7f0ee48e4f099633/ty-0.0.29-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:298a8d5faa2502d3810bbbb47a030b9455495b9921594206043c785dd61548cf", size = 10910613, upload-time = "2026-04-05T15:01:17.17Z" }, - { url = "https://files.pythonhosted.org/packages/1c/01/47ce3c6c53e0670eadbe80756b167bf80ed6681d1ba57cfde2e8065a13d1/ty-0.0.29-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c8fba1a3524c6109d1e020d92301c79d41bf442fa8d335b9fa366239339cb70", size = 11475750, upload-time = "2026-04-05T15:01:30.461Z" }, - { url = "https://files.pythonhosted.org/packages/c4/cf/e361845b1081c9264ad5b7c963231bab03f2666865a9f2a115c4233f2137/ty-0.0.29-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c48adf88a70d264128c39ee922ed14a947817fced1e93c08c1a89c9244edcde", size = 11190055, upload-time = "2026-04-05T15:01:12.369Z" }, - { url = "https://files.pythonhosted.org/packages/79/12/0fb0857e9a62cb11586e9a712103877bbf717f5fb570d16634408cfdefee/ty-0.0.29-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ce0a7a0e96bc7b42518cd3a1a6a6298ef64ff40ca4614355c1aa807059b5c6f", size = 11020539, upload-time = "2026-04-05T15:01:37.022Z" }, - { url = "https://files.pythonhosted.org/packages/20/36/5a26753802083f80cd125db6c4348ad42b3c982ec36e718e0bf4c18f75e5/ty-0.0.29-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a6ac86a05b4a3731d45365ab97780acc7b8146fa62fccb3cbe94fe6546c67a97", size = 10396399, upload-time = "2026-04-05T15:01:26.167Z" }, - { url = "https://files.pythonhosted.org/packages/00/e6/b4e75b5752239ab3ab400f19faef4dbef81d05aab5d3419fda0c062a3765/ty-0.0.29-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6bbbf53141af0f3150bf288d716263f1a3550054e4b3551ca866d38192ba9891", size = 10421461, upload-time = "2026-04-05T15:01:08.367Z" }, - { url = "https://files.pythonhosted.org/packages/c0/21/1084b5b609f9abed62070ec0b31c283a403832a6310c8bbc208bd45ee1e6/ty-0.0.29-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1c9e06b770c1d0ff5efc51e34312390db31d53fcf3088163f413030b42b74f84", size = 10599187, upload-time = "2026-04-05T15:01:23.52Z" }, - { url = "https://files.pythonhosted.org/packages/ab/a1/ce19a2ca717bbcc1ee11378aba52ef70b6ce5b87245162a729d9fdc2360f/ty-0.0.29-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0307fe37e3f000ef1a4ae230bbaf511508a78d24a5e51b40902a21b09d5e6037", size = 11121198, upload-time = "2026-04-05T15:01:15.22Z" }, - { url = "https://files.pythonhosted.org/packages/6b/6b/f1430b279af704321566ce7ec2725d3d8258c2f815ebd93e474c64cd4543/ty-0.0.29-py3-none-win32.whl", hash = "sha256:7a2a898217960a825f8bc0087e1fdbaf379606175e98f9807187221d53a4a8ed", size = 9995331, upload-time = "2026-04-05T15:01:01.32Z" }, - { url = "https://files.pythonhosted.org/packages/d2/ef/3ef01c17785ff9a69378465c7d0faccd48a07b163554db0995e5d65a5a23/ty-0.0.29-py3-none-win_amd64.whl", hash = "sha256:fc1294200226b91615acbf34e0a9ad81caf98c081e9c6a912a31b0a7b603bc3f", size = 11023644, upload-time = "2026-04-05T15:01:04.432Z" }, - { url = "https://files.pythonhosted.org/packages/2c/55/87280a994d6a2d2647c65e12abbc997ed49835794366153c04c4d9304d76/ty-0.0.29-py3-none-win_arm64.whl", hash = "sha256:f9794bbd1bb3ce13f78c191d0c89ae4c63f52c12b6daa0c6fe220b90d019d12c", size = 10428165, upload-time = "2026-04-05T15:01:34.665Z" }, + { url = "https://files.pythonhosted.org/packages/b0/10/ea805cbbd75d5d50792551a2b383de8521eeab0c44f38c73e12819ced65e/ty-0.0.31-py3-none-linux_armv6l.whl", hash = "sha256:761651dc17ad7bc0abfc1b04b3f0e84df263ed435d34f29760b3da739ab02d35", size = 10834749, upload-time = "2026-04-15T15:48:14.877Z" }, + { url = "https://files.pythonhosted.org/packages/d9/4c/fabf951850401d24d36b21bced088a366c6827e1c37dab4523afff84c4b2/ty-0.0.31-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c529922395a07231c27488f0290651e05d27d149f7e0aa807678f1f7e9c58a5e", size = 10626012, upload-time = "2026-04-15T15:48:22.554Z" }, + { url = "https://files.pythonhosted.org/packages/04/b0/4a5aff88d2544f19514a59c8f693d63144aa7307fe2ee5df608333ab5460/ty-0.0.31-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5f345df2b87d747859e72c2cbc9be607ea1bbc8bc93dd32fa3d03ea091cb4fee", size = 10075790, upload-time = "2026-04-15T15:47:46.959Z" }, + { url = "https://files.pythonhosted.org/packages/d5/73/9d4dcad12cd4e85274014f2c0510ef93f590b2a1e5148de3a9f276098dad/ty-0.0.31-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4b207eddcfbafd376132689d3435b14efcb531289cb59cd961c6a611133bd54", size = 10590286, upload-time = "2026-04-15T15:48:06.222Z" }, + { url = "https://files.pythonhosted.org/packages/47/45/fe40adde18692359ded174ae7ddbfac056e876eb0f43b65be74fde7f6072/ty-0.0.31-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:663778b220f357067488ce68bfc52335ccbd161549776f70dcbde6bbde82f77a", size = 10623824, upload-time = "2026-04-15T15:48:12.965Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e8/0ffa2e09b548e6daa9ebc368d68b767dc2405ca4cbeadb7ede0e2cb21059/ty-0.0.31-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3506cfe87dfade0fb2960dd4fffd4fd8089003587b3445c0a1a295c9d83764fb", size = 11156864, upload-time = "2026-04-15T15:48:08.473Z" }, + { url = "https://files.pythonhosted.org/packages/08/e9/fd44c2075115d569593ee9473d7e2a38b750fd7e783421c95eb528c15df5/ty-0.0.31-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8b3f3d8492f08e81916026354c1d1599e9ddfa1241804141a74d5662fc710085", size = 11696401, upload-time = "2026-04-15T15:48:17.355Z" }, + { url = "https://files.pythonhosted.org/packages/4e/50/35aad8eadf964d23e2a4faa5b38a206aa85c78833c8ce335dddd2c34ba63/ty-0.0.31-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a97de32ee6a619393a4c495e056a1c547de7877510f3152e61345c71d774d2d0", size = 11374903, upload-time = "2026-04-15T15:47:55.893Z" }, + { url = "https://files.pythonhosted.org/packages/c8/37/01eccd25d23f5aaa7f7ff1a87b5b215469f6b202cf689a1812b71c1e7f6b/ty-0.0.31-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c906354ce441e342646582bc9b8f48a676f79f3d061e25de15ff870e015ca14e", size = 11206624, upload-time = "2026-04-15T15:47:51.778Z" }, + { url = "https://files.pythonhosted.org/packages/f4/70/baad2914cb097453f127a221f8addb2b41926098059cd773c75e6a662fc4/ty-0.0.31-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:275bb7c82afcbf89fe2dbef1b2692f2bc98451f1ee2c8eb809ddd91317822388", size = 10575089, upload-time = "2026-04-15T15:47:49.448Z" }, + { url = "https://files.pythonhosted.org/packages/83/12/bae3a7bba2e785eb72ce00f9da70eedcb8c5e8299efecbd16e6e436abd82/ty-0.0.31-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:405da247027c6efd1e264886b6ac4a86ab3a4f09200b02e33630efe85f119e53", size = 10642315, upload-time = "2026-04-15T15:48:19.661Z" }, + { url = "https://files.pythonhosted.org/packages/93/9e/cad04d5d839bc60355cea98c7e09d724ea65f47184def0fae8b90dc54591/ty-0.0.31-py3-none-musllinux_1_2_i686.whl", hash = "sha256:54d9835608eed196853d6643f645c50ce83bcc7fe546cdb3e210c1bcf7c58c09", size = 10834473, upload-time = "2026-04-15T15:48:02.091Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ba/84112d280182d37690d3d2b4018b2667e42bc281585e607015635310016a/ty-0.0.31-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5ee11be9b07e8c0c6b455ff075a0abe4f194de9476f57624db98eec9df618355", size = 11315785, upload-time = "2026-04-15T15:48:10.754Z" }, + { url = "https://files.pythonhosted.org/packages/50/9f/ac42dc223d7e0950e97a1854567a8b3e7fe09ad7375adbf91bfb43290482/ty-0.0.31-py3-none-win32.whl", hash = "sha256:7286587aacf3eef0956062d6492b893b02f82b0f22c5e230008e13ff0d216a8b", size = 10187657, upload-time = "2026-04-15T15:48:04.264Z" }, + { url = "https://files.pythonhosted.org/packages/75/3e/57ba7ea7ecb2f4751644ba91756e2be70e33ef5952c0c41a256a0e4c2437/ty-0.0.31-py3-none-win_amd64.whl", hash = "sha256:81134e25d2a2562ab372f24de8f9bd05034d27d30377a5d7540f259791c6234c", size = 11205258, upload-time = "2026-04-15T15:47:53.759Z" }, + { url = "https://files.pythonhosted.org/packages/88/39/bca669095ccf0a400af941fdf741578d4c2d6719f1b7f10e6dbec10aa862/ty-0.0.31-py3-none-win_arm64.whl", hash = "sha256:e9cb15fad26545c6a608f40f227af3a5513cb376998ca6feddd47ca7d93ffafa", size = 10590392, upload-time = "2026-04-15T15:47:57.968Z" }, ] [[package]] @@ -3707,11 +3683,11 @@ wheels = [ [[package]] name = "tzdata" -version = "2026.1" +version = "2026.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/f5/cd531b2d15a671a40c0f66cf06bc3570a12cd56eef98960068ebbad1bf5a/tzdata-2026.1.tar.gz", hash = "sha256:67658a1903c75917309e753fdc349ac0efd8c27db7a0cb406a25be4840f87f98", size = 197639, upload-time = "2026-04-03T11:25:22.002Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/70/d460bd685a170790ec89317e9bd33047988e4bce507b831f5db771e142de/tzdata-2026.1-py2.py3-none-any.whl", hash = "sha256:4b1d2be7ac37ceafd7327b961aa3a54e467efbdb563a23655fbfe0d39cfc42a9", size = 348952, upload-time = "2026-04-03T11:25:20.313Z" }, + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, ] [[package]] @@ -3739,7 +3715,7 @@ wheels = [ [[package]] name = "virtualenv" -version = "21.2.1" +version = "21.2.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "distlib" }, @@ -3748,9 +3724,9 @@ dependencies = [ { name = "python-discovery" }, { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/c5/aff062c66b42e2183201a7ace10c6b2e959a9a16525c8e8ca8e59410d27a/virtualenv-21.2.1.tar.gz", hash = "sha256:b66ffe81301766c0d5e2208fc3576652c59d44e7b731fc5f5ed701c9b537fa78", size = 5844770, upload-time = "2026-04-09T18:47:11.482Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0c/98/3a7e644e19cb26133488caff231be390579860bbbb3da35913c49a1d0a46/virtualenv-21.2.4.tar.gz", hash = "sha256:b294ef68192638004d72524ce7ef303e9d0cf5a44c95ce2e54a7500a6381cada", size = 5850742, upload-time = "2026-04-14T22:15:31.438Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/0e/f083a76cb590e60dff3868779558eefefb8dfb7c9ed020babc7aa014ccbf/virtualenv-21.2.1-py3-none-any.whl", hash = "sha256:bd16b49c53562b28cf1a3ad2f36edb805ad71301dee70ddc449e5c88a9f919a2", size = 5828326, upload-time = "2026-04-09T18:47:09.331Z" }, + { url = "https://files.pythonhosted.org/packages/27/8d/edd0bd910ff803c308ee9a6b7778621af0d10252219ad9f19ef4d4982a61/virtualenv-21.2.4-py3-none-any.whl", hash = "sha256:29d21e941795206138d0f22f4e45ff7050e5da6c6472299fb7103318763861ac", size = 5831232, upload-time = "2026-04-14T22:15:29.342Z" }, ] [[package]] @@ -3764,9 +3740,9 @@ wheels = [ [[package]] name = "zipp" -version = "3.23.0" +version = "3.23.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +sdist = { url = "https://files.pythonhosted.org/packages/30/21/093488dfc7cc8964ded15ab726fad40f25fd3d788fd741cc1c5a17d78ee8/zipp-3.23.1.tar.gz", hash = "sha256:32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110", size = 25965, upload-time = "2026-04-13T23:21:46.6Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, + { url = "https://files.pythonhosted.org/packages/08/8a/0861bec20485572fbddf3dfba2910e38fe249796cb73ecdeb74e07eeb8d3/zipp-3.23.1-py3-none-any.whl", hash = "sha256:0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc", size = 10378, upload-time = "2026-04-13T23:21:45.386Z" }, ]