diff --git a/.clang-tidy b/.clang-tidy index 7006d009eb..861fe73436 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,4 +1,5 @@ FormatStyle: file +HeaderFilterRegex: "^(?!.*plugins/).*" Checks: | bugprone-*, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e66b47b565..341717cd8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -155,7 +155,7 @@ jobs: name: 🇨‌ Lint needs: change-detection if: fromJSON(needs.change-detection.outputs.run-cpp-linter) - uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-linter.yml@56cf3608b07dc10bda5b98d77ed6ad21ecf7ef5d # v1.17.0 + uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-linter.yml@33b0da0cc448ad3abc9df3716ccbd89332d3d491 # v1.17.0 with: cmake-args: -DBUILD_MQT_CORE_BENCHMARKS=ON -DBUILD_MQT_CORE_MLIR=ON -DBUILD_MQT_CORE_BINDINGS=ON clang-version: 21 @@ -164,6 +164,7 @@ jobs: setup-python: true install-pkgs: "pybind11==3.0.0" cpp-linter-extra-args: "-std=c++20" + cpp-linter-ignore-extra: "plugins/**" python-tests: name: 🐍 Test diff --git a/.github/workflows/ci_mlir_plugin.yml b/.github/workflows/ci_mlir_plugin.yml new file mode 100644 index 0000000000..8697e3c459 --- /dev/null +++ b/.github/workflows/ci_mlir_plugin.yml @@ -0,0 +1,171 @@ +name: MLIR Plugin CI +on: + push: + branches: + - main + paths: + - "plugins/**" + pull_request: + paths: + - "plugins/**" + - ".github/workflows/ci_mlir_plugin.yml" + merge_group: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + cpp-test-mlir-catalyst-plugin-exact-mlir: + name: 🧩 Test MLIR Catalyst Plugin on ${{ matrix.runs-on }} from exact MLIR source + runs-on: ${{ matrix.runs-on }} + strategy: + matrix: + runs-on: [ubuntu-24.04, ubuntu-24.04-arm, macos-14] + fail-fast: false + env: + CMAKE_BUILD_PARALLEL_LEVEL: 4 + CTEST_PARALLEL_LEVEL: 4 + FORCE_COLOR: 3 + steps: + # Check out the repository + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # set up gcc as compiler + - name: Set up GCC as compiler + if: runner.os == 'Linux' + run: | + echo "C_COMPILER=gcc-14" >> $GITHUB_ENV + echo "CXX_COMPILER=g++-14" >> $GITHUB_ENV + echo "CC=gcc-14" >> $GITHUB_ENV + echo "CXX=g++-14" >> $GITHUB_ENV + + # set up ccache for faster C++ builds + - name: Setup ccache + uses: Chocobo1/setup-ccache-action@v1 + with: + prepend_symlinks_to_path: false + override_cache_key: mlir-plugin-${{ matrix.runs-on }}-exact-mlir + + # Set up uv for faster Python package management + - name: Install the latest version of uv + uses: astral-sh/setup-uv@v6 + with: + python-version: 3.13 + activate-environment: true + + # Check out the LLVM commit Catalyst is based on + - name: Checkout llvm + uses: actions/checkout@v4 + with: + repository: llvm/llvm-project + ref: f8cb7987c64dcffb72414a40560055cb717dbf74 + path: llvm-project + + # Install LLVM + - name: Install LLVM + working-directory: llvm-project + run: | + cmake \ + -S llvm \ + -B build_llvm \ + -G Ninja \ + -DLLVM_ENABLE_PROJECTS=mlir \ + -DLLVM_BUILD_EXAMPLES=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DLLVM_BUILD_TESTS=OFF \ + -DLLVM_INCLUDE_TESTS=OFF \ + -DLLVM_INCLUDE_EXAMPLES=OFF \ + -DLLVM_ENABLE_ASSERTIONS=ON \ + -DLLVM_ENABLE_ZLIB=FORCE_ON \ + -DLLVM_ENABLE_ZSTD=OFF \ + -DCMAKE_CXX_VISIBILITY_PRESET=default + cmake --build build_llvm --config Release + echo "MLIR_DIR=$(pwd)/build_llvm/lib/cmake/mlir" >> $GITHUB_ENV + echo "LLVM_DIR=$(pwd)/build_llvm/lib/cmake/llvm" >> $GITHUB_ENV + + # Build the plugin package + - name: Build mqt-core-catalyst-plugin + working-directory: plugins/catalyst + run: uv sync --verbose --active + + # Print shared library dependencies for the plugin + - name: Print shared library dependencies for mqt-core-catalyst-plugin + run: | + if [ "$(uname)" = "Linux" ]; then + ldd .venv/lib/python3.13/site-packages/mqt/core/plugins/catalyst/mqt-core-catalyst-plugin.so || true + elif [ "$(uname)" = "Darwin" ]; then + otool -l .venv/lib/python3.13/site-packages/mqt/core/plugins/catalyst/mqt-core-catalyst-plugin.dylib || true + fi + + # Print shared library dependencies for Catalyst + - name: Print shared library dependencies for Catalyst + run: | + if [ "$(uname)" = "Linux" ]; then + ldd $(which catalyst) || true + elif [ "$(uname)" = "Darwin" ]; then + otool -l $(which catalyst) || true + fi + + # Reconfigure CMake for LIT + - name: Reconfigure CMake for LIT + working-directory: plugins/catalyst + run: | + BUILD_DIR=$(find build -type d -name Release | head -n1) + cmake -S . \ + -B "$BUILD_DIR" \ + -DLLVM_EXTERNAL_LIT=$(which lit) \ + -DPython3_EXECUTABLE=$(which python) \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + + # Run clang-tidy on plugin code + - name: Run clang-tidy on plugin + working-directory: plugins/catalyst + run: | + BUILD_DIR=$(find build -type d -name Release | head -n1) + + # Check if compilation database exists + if [ ! -f "$BUILD_DIR/compile_commands.json" ]; then + echo "No compilation database found, skipping clang-tidy" + exit 0 + fi + + # Find all C++ source files in the plugin + FILES=$(find lib include -name "*.cpp" -o -name "*.h" 2>/dev/null || true) + if [ -z "$FILES" ]; then + echo "No C++ files found to analyze" + exit 0 + fi + + # Find Catalyst include directory + CATALYST_INCLUDE=$(find .venv -path "*/catalyst/include" -type d | head -n1) + if [ -z "$CATALYST_INCLUDE" ]; then + echo "Catalyst include directory not found, running without extra includes" + EXTRA_ARGS="" + else + echo "Found Catalyst includes at: $CATALYST_INCLUDE" + EXTRA_ARGS="--extra-arg=-I$CATALYST_INCLUDE" + fi + + echo "Running clang-tidy on plugin files..." + echo "$FILES" | xargs clang-tidy \ + -p="$BUILD_DIR" \ + --config-file=.clang-tidy \ + --format-style=file \ + $EXTRA_ARGS \ + || echo "clang-tidy completed with warnings" + + # Run the MLIR LIT tests + - name: Run LIT tests + working-directory: plugins/catalyst + run: | + BUILD_DIR=$(find build -type d -name Release | head -n1) + ninja -C "$BUILD_DIR" check-mqt-core-catalyst-plugin + + # Run the plugin Python tests + - name: Run pytest for mqt-core-catalyst-plugin + working-directory: plugins/catalyst + run: python -m pytest -s --verbose diff --git a/.gitignore b/.gitignore index d6a4939889..b0737937f0 100644 --- a/.gitignore +++ b/.gitignore @@ -136,8 +136,8 @@ python/**/_version.py # SKBuild cache dir _skbuild/ -# Any build dirs in the tests -test/**/build/ +# Any build dirs +**/build/ # Common editor files *~ @@ -167,9 +167,7 @@ Thumbs.db /distro/plans/main.fmf /distro/tests/main.fmf -/docs/**/build .vs -out/build node_modules/ wheelhouse/ diff --git a/cmake/SetupMLIR.cmake b/cmake/SetupMLIR.cmake index 6ff928c446..e8e80fe2d5 100644 --- a/cmake/SetupMLIR.cmake +++ b/cmake/SetupMLIR.cmake @@ -16,7 +16,9 @@ set(MQT_MLIR_MIN_VERSION # MLIR must be installed on the system find_package(MLIR REQUIRED CONFIG) if(MLIR_VERSION VERSION_LESS MQT_MLIR_MIN_VERSION) - message(FATAL_ERROR "MLIR version must be at least ${MQT_MLIR_MIN_VERSION}") + message( + FATAL_ERROR "MLIR version must be at least ${MQT_MLIR_MIN_VERSION} but found ${MLIR_VERSION}") + endif() message(STATUS "Using MLIRConfig.cmake in: ${MLIR_DIR}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") diff --git a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h index 5f4d08084c..ba7c5ad160 100644 --- a/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h +++ b/mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h @@ -247,6 +247,8 @@ class [[nodiscard]] Layout : public ThinLayout { struct QubitInfo { QubitIndex prog; QubitIndex hw; + + QubitInfo(QubitIndex p, QubitIndex h) : prog(p), hw(h) {} }; /** diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/SwapReconstructionAndElision.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/SwapReconstructionAndElision.cpp index 4d54c603d6..25d155e712 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/SwapReconstructionAndElision.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/SwapReconstructionAndElision.cpp @@ -15,6 +15,19 @@ #include #include +namespace mqt::ir::opt::detail { +// Prefer the setter if present (newer MLIR 21/22+), otherwise fall back to the +// public field (older 20/21). +template +auto setTopDown(T& cfg, int) + -> decltype((void)cfg.setUseTopDownTraversal(true), void()) { + cfg.setUseTopDownTraversal(true); +} +template void setTopDown(T& cfg, long) { + cfg.useTopDownTraversal = true; +} +} // namespace mqt::ir::opt::detail + namespace mqt::ir::opt { #define GEN_PASS_DEF_SWAPRECONSTRUCTIONANDELISION @@ -31,15 +44,13 @@ struct SwapReconstructionAndElision final auto op = getOperation(); auto* ctx = &getContext(); - // Define the set of patterns to use. mlir::RewritePatternSet patterns(ctx); populateSwapReconstructionAndElisionPatterns(patterns); - // Configure greedy driver mlir::GreedyRewriteConfig config; - config.setUseTopDownTraversal(true); + mqt::ir::opt::detail::setTopDown(config, + 0); // tries setter first, else field - // Apply patterns in an iterative and greedy manner. if (mlir::failed( mlir::applyPatternsGreedily(op, std::move(patterns), config))) { signalPassFailure(); diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/PlacementPass.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/PlacementPass.cpp index 1dc40f57ce..010a8c9c41 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/PlacementPass.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/PlacementPass.cpp @@ -37,7 +37,6 @@ #include #include #include -#include #include #include diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/RoutingPass.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/RoutingPass.cpp index d94e714122..d1234f7723 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/RoutingPass.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/RoutingPass.cpp @@ -41,7 +41,6 @@ #include #include #include -#include #include #include diff --git a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/RoutingVerificationPass.cpp b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/RoutingVerificationPass.cpp index a692719ede..94c41f2d42 100644 --- a/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/RoutingVerificationPass.cpp +++ b/mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/sc/RoutingVerificationPass.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #define DEBUG_TYPE "routing-verification-sc" diff --git a/plugins/catalyst/.clang-format b/plugins/catalyst/.clang-format new file mode 100644 index 0000000000..0be56514be --- /dev/null +++ b/plugins/catalyst/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: InheritParentConfig diff --git a/plugins/catalyst/.clang-tidy b/plugins/catalyst/.clang-tidy new file mode 100644 index 0000000000..3e1c8ce6de --- /dev/null +++ b/plugins/catalyst/.clang-tidy @@ -0,0 +1,2 @@ +InheritParentConfig: true +HeaderFilterRegex: ".*" diff --git a/plugins/catalyst/CMakeLists.txt b/plugins/catalyst/CMakeLists.txt new file mode 100644 index 0000000000..c7c7120cf5 --- /dev/null +++ b/plugins/catalyst/CMakeLists.txt @@ -0,0 +1,65 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +# set required cmake version +cmake_minimum_required(VERSION 3.24...4.0) + +project( + mqt-core-catalyst-plugin + LANGUAGES C CXX + VERSION 0.1.0 + DESCRIPTION "MQT Core MLIR Plugin for Catalyst") + +set(CMAKE_CXX_STANDARD + 20 + CACHE STRING "C++ standard to conform to") + +if(ENABLE_COVERAGE) + add_compile_options(--coverage -O0) + add_link_options(--coverage) +endif() + +include(cmake/ExternalDependencies.cmake) + +set(MQT_MLIR_PLUGIN_SOURCE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") +set(MQT_MLIR_PLUGIN_BUILD_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}/include") + +# MLIR must be installed on the system +find_package(MLIR REQUIRED CONFIG) + +# Add the paths to the MLIR and LLVM CMake modules. +list(APPEND CMAKE_MODULE_PATH "${MLIR_CMAKE_DIR}") +list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") + +# Include the TableGen, LLVM and MLIR CMake modules. +include(TableGen) +include(AddLLVM) +include(AddMLIR) +include(HandleLLVMOptions) + +include_directories(${LLVM_INCLUDE_DIRS}) +include_directories(${MLIR_INCLUDE_DIRS}) +include_directories(BEFORE ${MQT_MLIR_PLUGIN_SOURCE_INCLUDE_DIR}) +include_directories(BEFORE ${MQT_MLIR_PLUGIN_BUILD_INCLUDE_DIR}) +include_directories(${CATALYST_INCLUDE_DIRS}) + +link_directories(${LLVM_BUILD_LIBRARY_DIR}) +add_definitions(${LLVM_DEFINITIONS}) + +if(APPLE) + set(BASEPOINT @loader_path) +else() + set(BASEPOINT $ORIGIN) +endif() +set(CMAKE_INSTALL_RPATH ${BASEPOINT} ${BASEPOINT}/${CMAKE_INSTALL_LIBDIR}) +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + +add_subdirectory(include) +add_subdirectory(lib) +add_subdirectory(test) diff --git a/plugins/catalyst/LICENSE.md b/plugins/catalyst/LICENSE.md new file mode 100644 index 0000000000..1215c7de72 --- /dev/null +++ b/plugins/catalyst/LICENSE.md @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +Copyright (c) 2025 Munich Quantum Software Company GmbH + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/catalyst/README.md b/plugins/catalyst/README.md new file mode 100644 index 0000000000..76753be545 --- /dev/null +++ b/plugins/catalyst/README.md @@ -0,0 +1,186 @@ +[![PyPI](https://img.shields.io/pypi/v/mqt.core?logo=pypi&style=flat-square)](https://pypi.org/project/mqt.core/) +![OS](https://img.shields.io/badge/os-linux%20%7C%20macos%20%7C%20windows-blue?style=flat-square) +[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://opensource.org/licenses/MIT) +[![DOI](https://img.shields.io/badge/JOSS-10.21105/joss.07478-blue.svg?style=flat-square)](https://doi.org/10.21105/joss.07478) +[![CI](https://img.shields.io/github/actions/workflow/status/munich-quantum-toolkit/core/ci.yml?branch=main&style=flat-square&logo=github&label=ci)](https://github.com/munich-quantum-toolkit/core/actions/workflows/ci.yml) +[![CD](https://img.shields.io/github/actions/workflow/status/munich-quantum-toolkit/core/cd.yml?style=flat-square&logo=github&label=cd)](https://github.com/munich-quantum-toolkit/core/actions/workflows/cd.yml) +[![Documentation](https://img.shields.io/readthedocs/mqt-core?logo=readthedocs&style=flat-square)](https://mqt.readthedocs.io/projects/core) +[![codecov](https://img.shields.io/codecov/c/github/munich-quantum-toolkit/core?style=flat-square&logo=codecov)](https://codecov.io/gh/munich-quantum-toolkit/core) + +

+ + + + MQT Logo + + +

+ +# MQT Core Catalyst MLIR Plugin + +This sub-package of MQT Core provides a [Catalyst](https://github.com/PennyLaneAI/catalyst) plugin based on [MLIR](https://mlir.llvm.org/). +It allows you to use MQT Core's MLIR dialects and transformations within the Catalyst framework, enabling advanced quantum circuit optimizations and transformations. + +If you have any questions, feel free to create a [discussion](https://github.com/munich-quantum-toolkit/core/discussions) or an [issue](https://github.com/munich-quantum-toolkit/core/issues) on [GitHub](https://github.com/munich-quantum-toolkit/core). + +## Contributors and Supporters + +The _[Munich Quantum Toolkit (MQT)](https://mqt.readthedocs.io)_ is developed by the [Chair for Design Automation](https://www.cda.cit.tum.de/) at the [Technical University of Munich](https://www.tum.de/) and supported by the [Munich Quantum Software Company (MQSC)](https://munichquantum.software). +Among others, it is part of the [Munich Quantum Software Stack (MQSS)](https://www.munich-quantum-valley.de/research/research-areas/mqss) ecosystem, which is being developed as part of the [Munich Quantum Valley (MQV)](https://www.munich-quantum-valley.de) initiative. + +

+ + + MQT Partner Logos + +

+ +Thank you to all the contributors who have helped make MQT Core a reality! + +

+ + + +

+ +## Getting Started + +`mqt.core.catalyst` is **NOT YET** available on [PyPI](https://pypi.org/project/mqt.core/). + +Because `pennylane-catalyst` pins to a specific LLVM/MLIR revision, you must build that LLVM/MLIR locally and point CMake at it. + +### 1) Build the exact LLVM/MLIR revision (locally) + +```bash +# Pick a workspace (optional) +mkdir -p ~/dev && cd ~/dev + +# Clone the exact LLVM revision Catalyst expects +git clone https://github.com/llvm/llvm-project.git +cd llvm-project +git checkout f8cb7987c64dcffb72414a40560055cb717dbf74 + +# Configure & build MLIR (Release is recommended) +cmake -S llvm -B build_llvm -G Ninja \ + -DLLVM_ENABLE_PROJECTS=mlir \ + -DLLVM_BUILD_EXAMPLES=OFF \ + -DLLVM_BUILD_TESTS=OFF \ + -DLLVM_INCLUDE_TESTS=OFF \ + -DLLVM_INCLUDE_EXAMPLES=OFF \ + -DLLVM_ENABLE_ASSERTIONS=ON \ + -DLLVM_ENABLE_ZLIB=FORCE_ON \ + -DLLVM_ENABLE_ZSTD=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_VISIBILITY_PRESET=default + +cmake --build build_llvm --config Release + +# Export these for your shell/session +export MLIR_DIR="$PWD/build_llvm/lib/cmake/mlir" +export LLVM_DIR="$PWD/build_llvm/lib/cmake/llvm" +``` + +### 2) Create a local env and build the plugin + +```console +# From your repo root +cd /path/to/your/core/plugins/catalyst + +# Create and activate a venv (optional) +uv venv .venv +. .venv/bin/activate + +# Install Catalyst and build the plugin +uv pip install pennylane-catalyst>0.12.0 + +uv sync --verbose --active + --config-settings=cmake.define.CMAKE_BUILD_TYPE=Release + --config-settings=cmake.define.Python3_EXECUTABLE="$(which python)" + --config-settings=cmake.define.MLIR_DIR="$MLIR_DIR" + --config-settings=cmake.define.LLVM_DIR="$LLVM_DIR" +``` + +### 3) Use the MQT plugin with your PennyLane code + +The MQT plugin provides device configuration utilities to prevent Catalyst from decomposing gates into unitary matrices, enabling lossless roundtrip conversions. + +**Important:** Use `get_device()` from the MQT plugin instead of `qml.device()` directly: + +```python3 +import catalyst +import pennylane as qml +from catalyst.passes import apply_pass +from mqt.core.plugins.catalyst import get_device + +# Use get_device() to configure the device for MQT plugin compatibility +# This prevents gates from being decomposed into unitary matrices +device = get_device("lightning.qubit", wires=2) + + +@apply_pass("mqt.mqtopt-to-catalystquantum") +@apply_pass("mqt.catalystquantum-to-mqtopt") +@qml.qnode(device) +def circuit() -> None: + qml.Hadamard(wires=[0]) + qml.CNOT(wires=[0, 1]) + # Controlled gates will NOT be decomposed to matrices + qml.ctrl(qml.PauliX(wires=0), control=1) + catalyst.measure(0) + catalyst.measure(1) + + +@qml.qjit(target="mlir", autograph=True) +def module() -> None: + return circuit() + + +# Get the optimized MLIR representation +mlir_output = module.mlir_opt +``` + +**Alternative:** You can also configure an existing device: + +```python3 +from mqt.core.plugins.catalyst import configure_device_for_mqt + +device = qml.device("lightning.qubit", wires=2) +device = configure_device_for_mqt(device) +``` + +## System Requirements + +Building (and running) is continuously tested under Linux, macOS, and Windows using the [latest available system versions for GitHub Actions](https://github.com/actions/runner-images). +However, the implementation should be compatible with any current C++ compiler supporting C++20 and a minimum CMake version of 3.24. + +MQT Core relies on some external dependencies: + +- [llvm/llvm-project](https://github.com/llvm/llvm-project): A toolkit for the construction of highly optimized compilers, optimizers, and run-time environments. +- [PennyLaneAI/catalyst](https://github.com/PennyLaneAI/catalyst): A package that enables just-in-time (JIT) compilation of hybrid quantum-classical programs implemented with PennyLane. + +Note, both dependencies are currently restricted to a specific version. + +CMake will automatically look for installed versions of these libraries. If it does not find them, they will be fetched automatically at configure time via the [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) module (check out the documentation for more information on how to customize this behavior). + +## Cite This + +If you want to cite MQT Core's MLIR Plugin, please use the following BibTeX entry: + +```bibtex +TODO +``` + +--- + +## Acknowledgements + +The Munich Quantum Toolkit has been supported by the European +Research Council (ERC) under the European Union's Horizon 2020 research and innovation program (grant agreement +No. 101001318), the Bavarian State Ministry for Science and Arts through the Distinguished Professorship Program, as well as the +Munich Quantum Valley, which is supported by the Bavarian state government with funds from the Hightech Agenda Bayern Plus. + +

+ + + MQT Funding Footer + +

diff --git a/plugins/catalyst/cmake/ExternalDependencies.cmake b/plugins/catalyst/cmake/ExternalDependencies.cmake new file mode 100644 index 0000000000..149a0f2d45 --- /dev/null +++ b/plugins/catalyst/cmake/ExternalDependencies.cmake @@ -0,0 +1,119 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +# Declare all external dependencies and make sure that they are available. + +include(FetchContent) + +if(DEFINED Python3_EXECUTABLE AND Python3_EXECUTABLE) + set(CATALYST_VERSION 0.13.0) + # Check if the pennylane-catalyst package is installed in the python environment. + execute_process( + COMMAND "${Python3_EXECUTABLE}" -c "import catalyst; print(catalyst.__version__)" + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE FOUND_CATALYST_VERSION) + if(FOUND_CATALYST_VERSION) + message(STATUS "Found pennylane-catalyst ${FOUND_CATALYST_VERSION} in python environment.") + # Check if the version is compatible. + if(FOUND_CATALYST_VERSION VERSION_LESS ${CATALYST_VERSION}) + message( + WARNING + "pennylane-catalyst version ${FOUND_CATALYST_VERSION} in python environment is not compatible." + ) + else() + # Detect the installed catalyst include files. + execute_process( + COMMAND "${Python3_EXECUTABLE}" -c + "import catalyst.utils.runtime_environment as c; print(c.get_include_path())" + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE CATALYST_INCLUDE_DIRS) + + message(STATUS "Catalyst include path resolved to: ${CATALYST_INCLUDE_DIRS}") + + string(FIND "${CATALYST_INCLUDE_DIRS}" "site-packages" SITEPKG_IDX) + + if(SITEPKG_IDX EQUAL -1) + # In case of a installation from source Assume include path looks like: /mlir/include + # Derive /mlir/build/include and /mlir/build/lib/cmake/catalyst + get_filename_component(CATALYST_MLIR_ROOT "${CATALYST_INCLUDE_DIRS}/.." ABSOLUTE) + set(CATALYST_BUILD_DIR "${CATALYST_MLIR_ROOT}/build") + set(CATALYST_BUILD_INCLUDE_DIR "${CATALYST_BUILD_DIR}/include") + set(Catalyst_DIR "${CATALYST_BUILD_DIR}/lib/cmake/catalyst") + + include_directories("${CATALYST_INCLUDE_DIRS}") + include_directories("${CATALYST_BUILD_INCLUDE_DIR}") + include_directories("${CATALYST_INCLUDE_DIRS}") + include_directories("${CATALYST_BUILD_INCLUDE_DIR}") + else() + # In case of an installation from PyPI, the include path looks like: + # /site-packages/catalyst/include Derive the site-packages root and add it to the + # CMAKE_PREFIX_PATH. + get_filename_component(CATALYST_SP_ROOT "${CATALYST_INCLUDE_DIRS}/../.." ABSOLUTE) + list(APPEND CMAKE_PREFIX_PATH "${CATALYST_SP_ROOT}") + message(STATUS "Adding Catalyst site-packages to CMAKE_PREFIX_PATH: ${CATALYST_SP_ROOT}") + endif() + + endif() + else() + # Unfortunately, the download for an individual package cannot be turned off. To avoid + # downloading the entire package, we use `find_package` instead. + find_package(Catalyst ${CATALYST_VERSION} REQUIRED) + endif() + + if(NOT CATALYST_INCLUDE_DIRS) + message( + FATAL_ERROR + "The include directory of the pennylane-catalyst package could not be retrieved. Please ensure that the catalyst is installed correctly." + ) + endif() + +else() + find_package(Catalyst ${CATALYST_VERSION} QUIET) + if(NOT Catalyst_FOUND) + message( + FATAL_ERROR + "Python3 interpreter not found and Catalyst not discoverable. Either set Python3_EXECUTABLE for in-env detection or provide Catalyst via CMAKE_PREFIX_PATH." + ) + endif() +endif() + +# cmake-format: off +set(MQT_CORE_MINIMUM_VERSION 3.1.0 + CACHE STRING "MQT Core minimum version") +set(MQT_CORE_VERSION 3.1.1 + CACHE STRING "MQT Core version") +set(MQT_CORE_REV "afdfede740bc2afbbb85558b96495fdbd60ed5bd" + CACHE STRING "MQT Core identifier (tag, branch or commit hash)") +set(MQT_CORE_REPO_OWNER "munich-quantum-toolkit" + CACHE STRING "MQT Core repository owner (change when using a fork)") +# cmake-format: on +set(BUILD_MQT_CORE_TESTS + OFF + CACHE BOOL "Build MQT Core tests") +set(BUILD_MQT_CORE_SHARED_LIBS + OFF + CACHE BOOL "Build MQT Core shared libraries") +set(BUILD_MQT_CORE_MLIR + ON + CACHE BOOL "Build MQT Core MLIR support") +set(CMAKE_POSITION_INDEPENDENT_CODE + ON + CACHE BOOL "Enable position independent code (PIC) for MQT Core") +# Fetch mqt-core from the source tree +set(FETCHCONTENT_SOURCE_DIR_MQT-CORE + "${CMAKE_CURRENT_SOURCE_DIR}/../.." + CACHE PATH "Source directory for MQT Core") +FetchContent_Declare( + mqt-core + GIT_REPOSITORY https://github.com/${MQT_CORE_REPO_OWNER}/core.git + GIT_TAG ${MQT_CORE_REV} + FIND_PACKAGE_ARGS ${MQT_CORE_MINIMUM_VERSION}) +list(APPEND FETCH_PACKAGES mqt-core) + +# Make all declared dependencies available. +FetchContent_MakeAvailable(${FETCH_PACKAGES}) diff --git a/plugins/catalyst/include/CMakeLists.txt b/plugins/catalyst/include/CMakeLists.txt new file mode 100644 index 0000000000..f674bdbc32 --- /dev/null +++ b/plugins/catalyst/include/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +add_subdirectory(mlir) diff --git a/plugins/catalyst/include/mlir/CMakeLists.txt b/plugins/catalyst/include/mlir/CMakeLists.txt new file mode 100644 index 0000000000..6a65e12703 --- /dev/null +++ b/plugins/catalyst/include/mlir/CMakeLists.txt @@ -0,0 +1,9 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +add_subdirectory(Conversion) diff --git a/plugins/catalyst/include/mlir/Conversion/CMakeLists.txt b/plugins/catalyst/include/mlir/Conversion/CMakeLists.txt new file mode 100644 index 0000000000..6acd44dea6 --- /dev/null +++ b/plugins/catalyst/include/mlir/Conversion/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +add_subdirectory(MQTOptToCatalystQuantum) +add_subdirectory(CatalystQuantumToMQTOpt) diff --git a/plugins/catalyst/include/mlir/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt b/plugins/catalyst/include/mlir/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt new file mode 100644 index 0000000000..dcfbcd0323 --- /dev/null +++ b/plugins/catalyst/include/mlir/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +set(LLVM_TARGET_DEFINITIONS CatalystQuantumToMQTOpt.td) +mlir_tablegen(CatalystQuantumToMQTOpt.h.inc -gen-pass-decls -name CatalystQuantumToMQTOpt) +add_public_tablegen_target(CatalystQuantumToMQTOptIncGen) + +add_mlir_doc(CatalystQuantumToMQTOpt CatalystQuantumToMQTOpt ./ -gen-pass-doc) diff --git a/plugins/catalyst/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h b/plugins/catalyst/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h new file mode 100644 index 0000000000..a1ec8cec28 --- /dev/null +++ b/plugins/catalyst/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include // from @llvm-project + +namespace mqt::ir::conversions { + +#define GEN_PASS_DECL +#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h.inc" + +#define GEN_PASS_REGISTRATION +#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h.inc" + +} // namespace mqt::ir::conversions diff --git a/plugins/catalyst/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.td b/plugins/catalyst/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.td new file mode 100644 index 0000000000..b4cde9a45d --- /dev/null +++ b/plugins/catalyst/include/mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.td @@ -0,0 +1,32 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +include "mlir/Pass/PassBase.td" + +def CatalystQuantumToMQTOpt : Pass<"catalystquantum-to-mqtopt"> { + let summary = "Convert Catalyst's `Quantum` to MQT's `MQTOpt` dialect."; + + let description = [{ + This pass converts Catalyst's `Quantum` to MQT's `MQTOpt` dialect. + The following operations are currently NOT converted (and instead marked legal): + - DeviceInitOp + - DeviceReleaseOp + - NamedObsOp + - ExpvalOp + - FinalizeOp + - ComputationalBasisOp + - StateOp + - InitializeOp + }]; + + // Define dependent dialects + let dependentDialects = [ + "catalyst::quantum::QuantumDialect", + "::mqt::ir::opt::MQTOptDialect" + ]; +} diff --git a/plugins/catalyst/include/mlir/Conversion/MQTOptToCatalystQuantum/CMakeLists.txt b/plugins/catalyst/include/mlir/Conversion/MQTOptToCatalystQuantum/CMakeLists.txt new file mode 100644 index 0000000000..efe8759c1e --- /dev/null +++ b/plugins/catalyst/include/mlir/Conversion/MQTOptToCatalystQuantum/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +set(LLVM_TARGET_DEFINITIONS MQTOptToCatalystQuantum.td) +mlir_tablegen(MQTOptToCatalystQuantum.h.inc -gen-pass-decls -name MQTOptToCatalystQuantum) +add_public_tablegen_target(MQTOptToCatalystQuantumIncGen) + +add_mlir_doc(MQTOptToCatalystQuantum MQTOptToCatalystQuantum ./ -gen-pass-doc) diff --git a/plugins/catalyst/include/mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h b/plugins/catalyst/include/mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h new file mode 100644 index 0000000000..72d44b123a --- /dev/null +++ b/plugins/catalyst/include/mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#pragma once + +#include // from @llvm-project + +namespace mqt::ir::conversions { + +#define GEN_PASS_DECL +#include "mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h.inc" + +#define GEN_PASS_REGISTRATION +#include "mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h.inc" + +} // namespace mqt::ir::conversions diff --git a/plugins/catalyst/include/mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.td b/plugins/catalyst/include/mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.td new file mode 100644 index 0000000000..e7cea23280 --- /dev/null +++ b/plugins/catalyst/include/mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.td @@ -0,0 +1,23 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +include "mlir/Pass/PassBase.td" + +def MQTOptToCatalystQuantum : Pass<"mqtopt-to-catalystquantum"> { + let summary = "Convert MQT's `MQTOpt` to Catalyst's `Quantum` dialect."; + + let description = [{ + This pass converts MQT's `MQTOpt` to Catalyst's `Quantum` dialect. + The following operations are currently NOT converted (and therefore not supported): + - XXminusYY + }]; + let dependentDialects = [ + "catalyst::quantum::QuantumDialect", + "::mqt::ir::opt::MQTOptDialect" + ]; +} diff --git a/plugins/catalyst/lib/CMakeLists.txt b/plugins/catalyst/lib/CMakeLists.txt new file mode 100644 index 0000000000..89e1df2aa1 --- /dev/null +++ b/plugins/catalyst/lib/CMakeLists.txt @@ -0,0 +1,42 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +# Q: Why is it add_llvm_library and not add_mlir_library? A: According to Erick from Xanadu, who +# talked with the MLIR team, the reason is probably, that the MLIR plugin uses the same +# infrastructure as the LLVM plugins. Probably due to laziness, the appropriate MLIR macros were not +# adopted yet. +# +# Note: On DLL platforms, the tool that will use this plugin must be linked, see +# https://github.com/llvm/llvm-project/blob/2acecfe65397c162958ab305dc44614ff51e748c/llvm/cmake/modules/AddLLVM.cmake#L770 +# and +# https://github.com/llvm/llvm-project/blob/2acecfe65397c162958ab305dc44614ff51e748c/llvm/cmake/modules/AddLLVM.cmake#L517 +# This would mean that it is not enough to have the corresponding header at hand, we would need to +# have the catalyst target available to link against it. + +add_subdirectory(Conversion) + +set(TARGET_NAME mqt-core-catalyst-plugin) + +add_llvm_library( + ${TARGET_NAME} + MODULE + mqt-plugin.cpp + LINK_LIBS + MLIRMQTOptTransforms + CatalystQuantumToMQTOpt + MQTOptToCatalystQuantum) + +# set required C++ standard +target_compile_features(${TARGET_NAME} PUBLIC cxx_std_20) +target_compile_options(${TARGET_NAME} PUBLIC -fexceptions) + +# install directive for scikit-build-core +install( + TARGETS ${TARGET_NAME} + DESTINATION . + COMPONENT mqt-core-catalyst_Plugin) diff --git a/plugins/catalyst/lib/Conversion/CMakeLists.txt b/plugins/catalyst/lib/Conversion/CMakeLists.txt new file mode 100644 index 0000000000..6acd44dea6 --- /dev/null +++ b/plugins/catalyst/lib/Conversion/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +add_subdirectory(MQTOptToCatalystQuantum) +add_subdirectory(CatalystQuantumToMQTOpt) diff --git a/plugins/catalyst/lib/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt b/plugins/catalyst/lib/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt new file mode 100644 index 0000000000..6ca50ceb85 --- /dev/null +++ b/plugins/catalyst/lib/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +add_mlir_library(CatalystQuantumToMQTOpt CatalystQuantumToMQTOpt.cpp LINK_LIBS MLIRMQTOpt DEPENDS + CatalystQuantumToMQTOptIncGen) + +target_compile_features(CatalystQuantumToMQTOpt PUBLIC cxx_std_20) +target_compile_options(CatalystQuantumToMQTOpt PUBLIC -fexceptions) + +file(GLOB_RECURSE CONVERSION_HEADERS_SOURCE + "${MQT_MLIR_PLUGIN_SOURCE_INCLUDE_DIR}/mlir/Conversion/CatalystQuantumToMQTOpt/*.h") +file(GLOB_RECURSE CONVERSION_HEADERS_BUILD + "${MQT_MLIR_PLUGIN_BUILD_INCLUDE_DIR}/mlir/Conversion/CatalystQuantumToMQTOpt/*.inc") + +# add public headers using file sets +target_sources( + CatalystQuantumToMQTOpt + PUBLIC FILE_SET + HEADERS + BASE_DIRS + ${MQT_MLIR_PLUGIN_SOURCE_INCLUDE_DIR} + FILES + ${CONVERSION_HEADERS_SOURCE} + FILE_SET + HEADERS + BASE_DIRS + ${MQT_MLIR_PLUGIN_BUILD_INCLUDE_DIR} + FILES + ${CONVERSION_HEADERS_BUILD}) diff --git a/plugins/catalyst/lib/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.cpp b/plugins/catalyst/lib/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.cpp new file mode 100644 index 0000000000..13ed371959 --- /dev/null +++ b/plugins/catalyst/lib/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.cpp @@ -0,0 +1,643 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h" + +#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mqt::ir::conversions { + +#define GEN_PASS_DEF_CATALYSTQUANTUMTOMQTOPT +#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h.inc" + +using namespace mlir; +using namespace mlir::arith; + +class CatalystQuantumToMQTOptTypeConverter final : public TypeConverter { +public: + explicit CatalystQuantumToMQTOptTypeConverter(MLIRContext* ctx) { + // Identity conversion: Allow all types to pass through unmodified if needed + addConversion([](const Type type) { return type; }); + + // Convert Catalyst QubitType to MQTOpt QubitType + addConversion([ctx](catalyst::quantum::QubitType /*type*/) -> Type { + return opt::QubitType::get(ctx); + }); + + // Convert Catalyst QuregType to dynamic memref as placeholder + // The actual static memref types will flow through from alloc operations + addConversion([ctx](catalyst::quantum::QuregType /*type*/) -> Type { + auto qubitType = opt::QubitType::get(ctx); + return MemRefType::get({ShapedType::kDynamic}, qubitType); + }); + + // Target materialization: converts values during pattern application + // Just returns the input - the actual memref is already created by alloc + addTargetMaterialization([](OpBuilder& builder, Type resultType, + ValueRange inputs, Location loc) -> Value { + if (inputs.size() == 1) { + return inputs[0]; + } + return nullptr; + }); + } +}; + +struct ConvertQuantumAlloc final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(catalyst::quantum::AllocOp op, OpAdaptor /*adaptor*/, + ConversionPatternRewriter& rewriter) const override { + // Get the number of qubits from the attribute + auto nqubitsAttr = op.getNqubitsAttrAttr(); + if (!nqubitsAttr) { + return op.emitError("AllocOp missing nqubits_attr"); + } + + const auto nqubits = nqubitsAttr.getValue().getZExtValue(); + + // Prepare the result type(s) + const auto qubitType = opt::QubitType::get(rewriter.getContext()); + const auto memrefType = + MemRefType::get({static_cast(nqubits)}, qubitType); + + // Create the new operation + auto allocOp = rewriter.create(op.getLoc(), memrefType); + + // Replace the original with the new operation + rewriter.replaceOp(op, allocOp.getResult()); + return success(); + } +}; + +struct ConvertQuantumDealloc final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(catalyst::quantum::DeallocOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operand(s) + Value memref = adaptor.getQreg(); + + // Unwrap unrealized_conversion_cast if present + if (auto castOp = memref.getDefiningOp()) { + if (!castOp.getInputs().empty()) { + memref = castOp.getInputs()[0]; + } + } + + // Create the new operation + rewriter.replaceOpWithNewOp(op, memref); + return success(); + } +}; + +struct ConvertQuantumMeasure final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(catalyst::quantum::MeasureOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operand(s) + const auto inQubit = adaptor.getInQubit(); + + // Prepare the result type(s) + const auto qubitType = opt::QubitType::get(rewriter.getContext()); + const auto bitType = rewriter.getI1Type(); + + // Create the new operation + // Note: quantum.measure returns (i1, !quantum.bit) + // mqtopt.measure returns (!mqtopt.Qubit, i1) + auto mqtoptOp = rewriter.create(op.getLoc(), qubitType, + bitType, inQubit); + + // Replace with results in the correct order + rewriter.replaceOp(op, {mqtoptOp.getResult(1), mqtoptOp.getResult(0)}); + return success(); + } +}; + +struct ConvertQuantumExtract final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(catalyst::quantum::ExtractOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Prepare the result type(s) + const auto qubitType = opt::QubitType::get(rewriter.getContext()); + + // Get index (either from attribute or operand) + Value indexValue; + auto idxAttr = op.getIdxAttrAttr(); + + if (idxAttr) { + // Compile-time constant index from attribute + const auto idx = idxAttr.getValue().getZExtValue(); + indexValue = rewriter.create(op.getLoc(), idx); + } else { + // Runtime dynamic index from operand + auto idxOperand = adaptor.getIdx(); + if (!idxOperand) { + return op.emitError("ExtractOp missing both idx_attr and idx operand"); + } + + // Convert i64 to index type if needed + if (isa(idxOperand.getType())) { + indexValue = rewriter.create( + op.getLoc(), rewriter.getIndexType(), idxOperand); + } else { + indexValue = idxOperand; + } + } + + // Extract operand(s) + Value memref = adaptor.getQreg(); + + // Unwrap unrealized_conversion_cast if present + if (auto castOp = memref.getDefiningOp()) { + if (!castOp.getInputs().empty()) { + memref = castOp.getInputs()[0]; + } + } + + // Verify we got a static memref type as expected + auto memrefType = dyn_cast(memref.getType()); + if (!memrefType || !memrefType.hasStaticShape()) { + return op.emitError("Expected static memref type from alloc, got: ") + << memref.getType(); + } + + // Create the new operation + auto loadOp = rewriter.create( + op.getLoc(), qubitType, memref, ValueRange{indexValue}); + + // Replace the extract operation with the loaded qubit + rewriter.replaceOp(op, loadOp.getResult()); + return success(); + } +}; + +struct ConvertQuantumInsert final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(catalyst::quantum::InsertOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Get index (either from attribute or operand) + Value indexValue; + auto idxAttr = op.getIdxAttrAttr(); + + if (idxAttr) { + // Compile-time constant index from attribute + const auto idx = idxAttr.getValue().getZExtValue(); + indexValue = rewriter.create(op.getLoc(), idx); + } else { + // Runtime dynamic index from operand + auto idxOperand = adaptor.getIdx(); + if (!idxOperand) { + return op.emitError("InsertOp missing both idx_attr and idx operand"); + } + + // Convert i64 to index type if needed + if (isa(idxOperand.getType())) { + indexValue = rewriter.create( + op.getLoc(), rewriter.getIndexType(), idxOperand); + } else { + indexValue = idxOperand; + } + } + + // Extract operand(s) + Value memref = adaptor.getInQreg(); + + // Unwrap unrealized_conversion_cast if present + if (auto castOp = memref.getDefiningOp()) { + if (!castOp.getInputs().empty()) { + memref = castOp.getInputs()[0]; + } + } + + // Create the new operation + rewriter.create(op.getLoc(), adaptor.getQubit(), memref, + ValueRange{indexValue}); + + // In the memref model, the register is modified in-place + rewriter.replaceOp(op, memref); + return success(); + } +}; + +struct ConvertQuantumGlobalPhase final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(catalyst::quantum::GlobalPhaseOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operand(s) and attribute(s) + const auto param = adaptor.getParams(); + const auto inCtrlQubits = adaptor.getInCtrlQubits(); + const auto inCtrlValues = adaptor.getInCtrlValues(); + + // Separate positive and negative control qubits + SmallVector inPosCtrlQubitsVec; + SmallVector inNegCtrlQubitsVec; + + for (size_t i = 0; i < inCtrlQubits.size(); ++i) { + if (inCtrlValues[i]) { + inPosCtrlQubitsVec.emplace_back(inCtrlQubits[i]); + } else { + inNegCtrlQubitsVec.emplace_back(inCtrlQubits[i]); + } + } + + // Create the parameter attributes + SmallVector staticParamsVec; + SmallVector paramsMaskVec; + SmallVector finalParamValues; + + // Check if parameter is a compile-time constant + if (auto constOp = param.getDefiningOp()) { + if (auto floatAttr = dyn_cast(constOp.getValue())) { + staticParamsVec.push_back(floatAttr.getValueAsDouble()); + paramsMaskVec.push_back(true); + } else { + finalParamValues.push_back(param); + paramsMaskVec.push_back(false); + } + } else { + finalParamValues.push_back(param); + paramsMaskVec.push_back(false); + } + + const auto staticParams = + DenseF64ArrayAttr::get(rewriter.getContext(), staticParamsVec); + const auto paramsMask = + DenseBoolArrayAttr::get(rewriter.getContext(), paramsMaskVec); + + // Create the new operation + auto mqtoptOp = rewriter.create( + op.getLoc(), TypeRange{}, // out_qubits + ValueRange(inPosCtrlQubitsVec).getTypes(), // pos_ctrl_out_qubits + ValueRange(inNegCtrlQubitsVec).getTypes(), // neg_ctrl_out_qubits + staticParams, paramsMask, finalParamValues, // params + ValueRange{}, // in_qubits + ValueRange(inPosCtrlQubitsVec), // pos_ctrl_in_qubits + ValueRange(inNegCtrlQubitsVec)); // neg_ctrl_in_qubits + + // Replace the original with the new operation + rewriter.replaceOp(op, mqtoptOp); + return success(); + } +}; + +struct ConvertQuantumCustomOp final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(catalyst::quantum::CustomOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operand(s) and attribute(s) + const auto gateName = op.getGateName(); + const auto paramsValues = adaptor.getParams(); + const auto inQubits = adaptor.getInQubits(); + const auto inCtrlQubits = adaptor.getInCtrlQubits(); + const auto inCtrlValues = adaptor.getInCtrlValues(); + + // Separate positive and negative control qubits + SmallVector inPosCtrlQubitsVec; + SmallVector inNegCtrlQubitsVec; + + for (size_t i = 0; i < inCtrlQubits.size(); ++i) { + if (inCtrlValues[i]) { + inPosCtrlQubitsVec.emplace_back(inCtrlQubits[i]); + } else { + inNegCtrlQubitsVec.emplace_back(inCtrlQubits[i]); + } + } + + // Process parameters (static vs dynamic) + SmallVector paramsMaskVec; + SmallVector staticParamsVec; + SmallVector finalParamValues; + + auto maskAttr = op->getAttrOfType("params_mask"); + auto staticParamsAttr = + op->getAttrOfType("static_params"); + + size_t totalParams = 0; + if (maskAttr) { + totalParams = maskAttr.size(); + } else { + totalParams = staticParamsAttr + ? staticParamsAttr.size() + paramsValues.size() + : paramsValues.size(); + } + + size_t staticIdx = 0; + size_t dynamicIdx = 0; + + for (size_t i = 0; i < totalParams; ++i) { + const bool isStatic = (maskAttr ? maskAttr[i] : false); + + paramsMaskVec.emplace_back(isStatic); + + if (isStatic) { + assert(staticParamsAttr && "Missing static_params for static mask"); + staticParamsVec.emplace_back(staticParamsAttr[staticIdx++]); + } else { + assert(dynamicIdx < paramsValues.size() && + "Too few dynamic parameters"); + finalParamValues.emplace_back(paramsValues[dynamicIdx++]); + } + } + + const auto staticParams = + DenseF64ArrayAttr::get(rewriter.getContext(), staticParamsVec); + const auto paramsMask = + DenseBoolArrayAttr::get(rewriter.getContext(), paramsMaskVec); + + // Create the new operation + Operation* mqtoptOp = nullptr; + +#define CREATE_GATE_OP(GATE_TYPE) \ + rewriter.create( \ + op.getLoc(), inQubits.getTypes(), \ + ValueRange(inPosCtrlQubitsVec).getTypes(), \ + ValueRange(inNegCtrlQubitsVec).getTypes(), staticParams, paramsMask, \ + finalParamValues, inQubits, inPosCtrlQubitsVec, inNegCtrlQubitsVec) + + if (gateName == "Hadamard") { + mqtoptOp = CREATE_GATE_OP(H); + } else if (gateName == "Identity") { + mqtoptOp = CREATE_GATE_OP(I); + } else if (gateName == "PauliX") { + mqtoptOp = CREATE_GATE_OP(X); + } else if (gateName == "PauliY") { + mqtoptOp = CREATE_GATE_OP(Y); + } else if (gateName == "PauliZ") { + mqtoptOp = CREATE_GATE_OP(Z); + } else if (gateName == "S") { + mqtoptOp = CREATE_GATE_OP(S); + } else if (gateName == "T") { + mqtoptOp = CREATE_GATE_OP(T); + } else if (gateName == "SX") { + mqtoptOp = CREATE_GATE_OP(SX); + } else if (gateName == "ECR") { + mqtoptOp = CREATE_GATE_OP(ECR); + } else if (gateName == "SWAP") { + mqtoptOp = CREATE_GATE_OP(SWAP); + } else if (gateName == "ISWAP") { + if (op.getAdjoint()) { + mqtoptOp = CREATE_GATE_OP(iSWAPdg); + } else { + mqtoptOp = CREATE_GATE_OP(iSWAP); + } + } else if (gateName == "RX") { + mqtoptOp = CREATE_GATE_OP(RX); + } else if (gateName == "RY") { + mqtoptOp = CREATE_GATE_OP(RY); + } else if (gateName == "RZ") { + mqtoptOp = CREATE_GATE_OP(RZ); + } else if (gateName == "PhaseShift") { + mqtoptOp = CREATE_GATE_OP(P); + } else if (gateName == "CRX") { + // CRX gate: 1 control qubit + 1 target qubit + // inQubits[0] is control, inQubits[1] is target + inPosCtrlQubitsVec.emplace_back(inQubits[0]); + mqtoptOp = rewriter.create( + op.getLoc(), inQubits[1].getType(), + ValueRange(inPosCtrlQubitsVec).getTypes(), + ValueRange(inNegCtrlQubitsVec).getTypes(), staticParams, paramsMask, + finalParamValues, inQubits[1], inPosCtrlQubitsVec, + inNegCtrlQubitsVec); + } else if (gateName == "CRY") { + // CRY gate: 1 control qubit + 1 target qubit + // inQubits[0] is control, inQubits[1] is target + inPosCtrlQubitsVec.emplace_back(inQubits[0]); + mqtoptOp = rewriter.create( + op.getLoc(), inQubits[1].getType(), + ValueRange(inPosCtrlQubitsVec).getTypes(), + ValueRange(inNegCtrlQubitsVec).getTypes(), staticParams, paramsMask, + finalParamValues, inQubits[1], inPosCtrlQubitsVec, + inNegCtrlQubitsVec); + } else if (gateName == "CRZ") { + // CRZ gate: 1 control qubit + 1 target qubit + // inQubits[0] is control, inQubits[1] is target + inPosCtrlQubitsVec.emplace_back(inQubits[0]); + mqtoptOp = rewriter.create( + op.getLoc(), inQubits[1].getType(), + ValueRange(inPosCtrlQubitsVec).getTypes(), + ValueRange(inNegCtrlQubitsVec).getTypes(), staticParams, paramsMask, + finalParamValues, inQubits[1], inPosCtrlQubitsVec, + inNegCtrlQubitsVec); + } else if (gateName == "ControlledPhaseShift") { + // ControlledPhaseShift gate: 1 control qubit + 1 target qubit + // inQubits[0] is control, inQubits[1] is target + inPosCtrlQubitsVec.emplace_back(inQubits[0]); + mqtoptOp = rewriter.create( + op.getLoc(), inQubits[1].getType(), + ValueRange(inPosCtrlQubitsVec).getTypes(), + ValueRange(inNegCtrlQubitsVec).getTypes(), staticParams, paramsMask, + finalParamValues, inQubits[1], inPosCtrlQubitsVec, + inNegCtrlQubitsVec); + } else if (gateName == "IsingXY") { + // PennyLane IsingXY has 1 parameter (phi), OpenQASM XXPlusYY needs 2 + // (theta, beta) Relationship: IsingXY(phi) = XXPlusYY(phi, pi) Add pi as + // second parameter + SmallVector isingxyParams(finalParamValues.begin(), + finalParamValues.end()); + auto piAttr = rewriter.getF64FloatAttr(3.141592653589793); + isingxyParams.push_back( + rewriter.create(op.getLoc(), piAttr).getResult()); + + SmallVector isingxyStaticParams(staticParamsVec.begin(), + staticParamsVec.end()); + SmallVector isingxyParamsMask(paramsMaskVec.begin(), + paramsMaskVec.end()); + isingxyParamsMask.push_back(false); // pi is a dynamic constant + + auto isingxyStaticParamsAttr = + DenseF64ArrayAttr::get(rewriter.getContext(), isingxyStaticParams); + auto isingxyParamsMaskAttr = + DenseBoolArrayAttr::get(rewriter.getContext(), isingxyParamsMask); + + mqtoptOp = rewriter.create( + op.getLoc(), inQubits.getTypes(), + ValueRange(inPosCtrlQubitsVec).getTypes(), + ValueRange(inNegCtrlQubitsVec).getTypes(), isingxyStaticParamsAttr, + isingxyParamsMaskAttr, isingxyParams, inQubits, inPosCtrlQubitsVec, + inNegCtrlQubitsVec); + } else if (gateName == "IsingXX") { + mqtoptOp = CREATE_GATE_OP(RXX); + } else if (gateName == "IsingYY") { + mqtoptOp = CREATE_GATE_OP(RYY); + } else if (gateName == "IsingZZ") { + mqtoptOp = CREATE_GATE_OP(RZZ); + } else if (gateName == "CNOT") { + inPosCtrlQubitsVec.emplace_back(inQubits[0]); + mqtoptOp = rewriter.create( + op.getLoc(), inQubits[1].getType(), + ValueRange(inPosCtrlQubitsVec).getTypes(), + ValueRange(inNegCtrlQubitsVec).getTypes(), staticParams, paramsMask, + finalParamValues, inQubits[1], inPosCtrlQubitsVec, + inNegCtrlQubitsVec); + } else if (gateName == "CY") { + inPosCtrlQubitsVec.emplace_back(inQubits[0]); + mqtoptOp = rewriter.create( + op.getLoc(), inQubits[1].getType(), + ValueRange(inPosCtrlQubitsVec).getTypes(), + ValueRange(inNegCtrlQubitsVec).getTypes(), staticParams, paramsMask, + finalParamValues, inQubits[1], inPosCtrlQubitsVec, + inNegCtrlQubitsVec); + } else if (gateName == "CZ") { + inPosCtrlQubitsVec.emplace_back(inQubits[0]); + mqtoptOp = rewriter.create( + op.getLoc(), inQubits[1].getType(), + ValueRange(inPosCtrlQubitsVec).getTypes(), + ValueRange(inNegCtrlQubitsVec).getTypes(), staticParams, paramsMask, + finalParamValues, inQubits[1], inPosCtrlQubitsVec, + inNegCtrlQubitsVec); + } else if (gateName == "Toffoli") { + // Toffoli gate: 2 control qubits + 1 target qubit + // inQubits[0] and inQubits[1] are controls, inQubits[2] is target + inPosCtrlQubitsVec.emplace_back(inQubits[0]); + inPosCtrlQubitsVec.emplace_back(inQubits[1]); + mqtoptOp = rewriter.create( + op.getLoc(), inQubits[2].getType(), + ValueRange(inPosCtrlQubitsVec).getTypes(), + ValueRange(inNegCtrlQubitsVec).getTypes(), staticParams, paramsMask, + finalParamValues, inQubits[2], inPosCtrlQubitsVec, + inNegCtrlQubitsVec); + } else if (gateName == "CSWAP") { + // CSWAP gate: 1 control qubit + 2 target qubits + // inQubits[0] is control, inQubits[1] and inQubits[2] are targets + inPosCtrlQubitsVec.emplace_back(inQubits[0]); + mqtoptOp = rewriter.create( + op.getLoc(), ValueRange{inQubits[1], inQubits[2]}, + ValueRange(inPosCtrlQubitsVec).getTypes(), + ValueRange(inNegCtrlQubitsVec).getTypes(), staticParams, paramsMask, + finalParamValues, ValueRange{inQubits[1], inQubits[2]}, + inPosCtrlQubitsVec, inNegCtrlQubitsVec); + } else { + llvm::errs() << "Unsupported gate: " << gateName << "\n"; + return failure(); + } + +#undef CREATE_GATE_OP + + // Replace the original with the new operation + rewriter.replaceOp(op, mqtoptOp); + return success(); + } +}; + +struct CatalystQuantumToMQTOpt final + : impl::CatalystQuantumToMQTOptBase { + using CatalystQuantumToMQTOptBase::CatalystQuantumToMQTOptBase; + + void runOnOperation() override { + MLIRContext* context = &getContext(); + auto* module = getOperation(); + + ConversionTarget target(*context); + target.addLegalDialect(); + target.addLegalDialect(); + target.addLegalDialect(); + target.addIllegalDialect(); + + // Mark operations legal that have no equivalent in the target dialect + target.addLegalOp< + catalyst::quantum::DeviceInitOp, catalyst::quantum::DeviceReleaseOp, + catalyst::quantum::NamedObsOp, catalyst::quantum::ExpvalOp, + catalyst::quantum::FinalizeOp, catalyst::quantum::ComputationalBasisOp, + catalyst::quantum::StateOp, catalyst::quantum::InitializeOp>(); + + RewritePatternSet patterns(context); + const CatalystQuantumToMQTOptTypeConverter typeConverter(context); + + patterns + .add(typeConverter, + context); + + // Type conversion boilerplate to handle function signatures and control + // flow See: https://www.jeremykun.com/2023/10/23/mlir-dialect-conversion + + // Convert func.func signatures to use the converted types + populateFunctionOpInterfaceTypeConversionPattern( + patterns, typeConverter); + + // Mark func.func as legal only if signature and body types are converted + target.addDynamicallyLegalOp([&](func::FuncOp op) { + return typeConverter.isSignatureLegal(op.getFunctionType()) && + typeConverter.isLegal(&op.getBody()); + }); + + // Convert return ops to match the new function result types + populateReturnOpTypeConversionPattern(patterns, typeConverter); + + // Mark func.return as legal only if operand types match converted types + target.addDynamicallyLegalOp( + [&](const func::ReturnOp op) { return typeConverter.isLegal(op); }); + + // Convert call sites to use the converted argument and result types + populateCallOpTypeConversionPattern(patterns, typeConverter); + + // Mark func.call as legal only if operand and result types are converted + target.addDynamicallyLegalOp( + [&](const func::CallOp op) { return typeConverter.isLegal(op); }); + + // Convert control-flow ops (cf.br, cf.cond_br, etc.) + populateBranchOpInterfaceTypeConversionPattern(patterns, typeConverter); + + // Mark unknown ops as legal if they don't require type conversion + target.markUnknownOpDynamicallyLegal([&](Operation* op) { + return isNotBranchOpInterfaceOrReturnLikeOp(op) || + isLegalForBranchOpInterfaceTypeConversionPattern(op, + typeConverter) || + isLegalForReturnOpTypeConversionPattern(op, typeConverter); + }); + + if (failed(applyPartialConversion(module, target, std::move(patterns)))) { + signalPassFailure(); + } + } +}; + +} // namespace mqt::ir::conversions diff --git a/plugins/catalyst/lib/Conversion/MQTOptToCatalystQuantum/CMakeLists.txt b/plugins/catalyst/lib/Conversion/MQTOptToCatalystQuantum/CMakeLists.txt new file mode 100644 index 0000000000..2c21fb770d --- /dev/null +++ b/plugins/catalyst/lib/Conversion/MQTOptToCatalystQuantum/CMakeLists.txt @@ -0,0 +1,34 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +add_mlir_library(MQTOptToCatalystQuantum MQTOptToCatalystQuantum.cpp LINK_LIBS MLIRMQTOpt DEPENDS + MQTOptToCatalystQuantumIncGen) + +target_compile_features(MQTOptToCatalystQuantum PUBLIC cxx_std_20) +target_compile_options(MQTOptToCatalystQuantum PUBLIC -fexceptions) + +file(GLOB_RECURSE CONVERSION_HEADERS_SOURCE + "${MQT_MLIR_PLUGIN_SOURCE_INCLUDE_DIR}/mlir/Conversion/MQTOptToCatalystQuantum/*.h") +file(GLOB_RECURSE CONVERSION_HEADERS_BUILD + "${MQT_MLIR_PLUGIN_BUILD_INCLUDE_DIR}/mlir/Conversion/MQTOptToCatalystQuantum/*.inc") + +# add public headers using file sets +target_sources( + MQTOptToCatalystQuantum + PUBLIC FILE_SET + HEADERS + BASE_DIRS + ${MQT_MLIR_PLUGIN_SOURCE_INCLUDE_DIR} + FILES + ${CONVERSION_HEADERS_SOURCE} + FILE_SET + HEADERS + BASE_DIRS + ${MQT_MLIR_PLUGIN_BUILD_INCLUDE_DIR} + FILES + ${CONVERSION_HEADERS_BUILD}) diff --git a/plugins/catalyst/lib/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.cpp b/plugins/catalyst/lib/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.cpp new file mode 100644 index 0000000000..94cc36d94e --- /dev/null +++ b/plugins/catalyst/lib/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.cpp @@ -0,0 +1,1503 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h" + +#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Define math constants if not available +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 +#endif +#ifndef M_PI_4 +#define M_PI_4 0.78539816339744830962 +#endif + +namespace mqt::ir::conversions { + +#define GEN_PASS_DEF_MQTOPTTOCATALYSTQUANTUM +#include "mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h.inc" + +using namespace mlir; +using namespace mlir::arith; + +// Helper functions to reduce code duplication +namespace { + +/// Helper struct to hold control qubit information +struct ControlInfo { + SmallVector ctrlQubits; + SmallVector ctrlValues; +}; + +/// Extract and concatenate control qubits and create corresponding control +/// values +ControlInfo extractControlInfo(ValueRange posCtrlQubits, + ValueRange negCtrlQubits, + ConversionPatternRewriter& rewriter, + Location loc) { + ControlInfo info; + + // Concatenate controls: [pos..., neg...] (preserve this order consistently) + info.ctrlQubits.reserve(posCtrlQubits.size() + negCtrlQubits.size()); + info.ctrlQubits.append(posCtrlQubits.begin(), posCtrlQubits.end()); + info.ctrlQubits.append(negCtrlQubits.begin(), negCtrlQubits.end()); + + if (info.ctrlQubits.empty()) { + return info; + } + + // Create control values: 1 for positive controls, 0 for negative controls + const Value one = + rewriter.create(loc, /*value=*/1, + /*width=*/1); + const Value zero = + rewriter.create(loc, /*value=*/0, + /*width=*/1); + + info.ctrlValues.reserve(info.ctrlQubits.size()); + info.ctrlValues.append(posCtrlQubits.size(), one); // +controls => 1 + info.ctrlValues.append(negCtrlQubits.size(), zero); // -controls => 0 + + return info; +} + +/// Helper function to extract operands and control info - for more complex +/// cases +template struct ExtractedOperands { + ValueRange inQubits; + ControlInfo ctrlInfo; +}; + +template +ExtractedOperands +extractOperands(OpAdaptor adaptor, ConversionPatternRewriter& rewriter, + Location loc) { + const ValueRange inQubits = adaptor.getInQubits(); + const ValueRange posCtrlQubits = adaptor.getPosCtrlInQubits(); + const ValueRange negCtrlQubits = adaptor.getNegCtrlInQubits(); + + const ControlInfo ctrlInfo = + extractControlInfo(posCtrlQubits, negCtrlQubits, rewriter, loc); + + return {inQubits, ctrlInfo}; +} + +} // anonymous namespace + +class MQTOptToCatalystQuantumTypeConverter final : public TypeConverter { +public: + explicit MQTOptToCatalystQuantumTypeConverter(MLIRContext* ctx) { + // Identity conversion for types that don't need transformation + addConversion([](const Type type) { return type; }); + + // Convert MemRef of MQTOpt QubitType to Catalyst QuregType + addConversion([ctx](MemRefType memrefType) -> Type { + if (auto qubitType = + dyn_cast(memrefType.getElementType())) { + return catalyst::quantum::QuregType::get(ctx); + } + return memrefType; + }); + + // Convert MQTOpt QubitType to Catalyst QubitType + addConversion([ctx](opt::QubitType /*type*/) -> Type { + return catalyst::quantum::QubitType::get(ctx); + }); + } +}; + +struct ConvertMQTOptAlloc final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(memref::AllocOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Only convert memrefs of qubit type + auto memrefType = cast(op.getType()); + if (!isa(memrefType.getElementType())) { + return failure(); + } + + // Prepare the result type(s) + const auto resultType = + catalyst::quantum::QuregType::get(rewriter.getContext()); + + // Get the size from memref type or dynamic operands + Value size = nullptr; + + // Check if this is a statically shaped memref + if (memrefType.hasStaticShape() && memrefType.getNumElements() >= 0) { + // For static memref like memref<2x!mqtopt.Qubit>, create constant size + auto numQubits = memrefType.getNumElements(); + auto sizeAttr = rewriter.getI64IntegerAttr(numQubits); + size = rewriter.create(op.getLoc(), sizeAttr); + } else { + // For dynamic memref, get size from dynamic operands + auto dynamicOperands = adaptor.getDynamicSizes(); + size = dynamicOperands.empty() ? nullptr : dynamicOperands[0]; + } + + // Replace with quantum alloc operation + rewriter.replaceOpWithNewOp(op, resultType, + size, nullptr); + + return success(); + } +}; + +struct ConvertMQTOptDealloc final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(memref::DeallocOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Only convert memrefs of qubit type + auto memrefType = cast(op.getMemref().getType()); + if (!isa(memrefType.getElementType())) { + return failure(); + } + + // Create the new operation + const auto catalystOp = rewriter.create( + op.getLoc(), TypeRange({}), adaptor.getMemref()); + + // Replace the original with the new operation + rewriter.replaceOp(op, catalystOp); + return success(); + } +}; + +struct ConvertMQTOptMeasure final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(opt::MeasureOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + + // Extract operand(s) + auto inQubit = adaptor.getInQubit(); + + // Prepare the result type(s) + auto qubitType = catalyst::quantum::QubitType::get(rewriter.getContext()); + auto bitType = rewriter.getI1Type(); + + // Create the new operation + const auto catalystOp = rewriter.create( + op.getLoc(), bitType, qubitType, inQubit, + /*optional::mlir::IntegerAttr postselect=*/nullptr); + + // Replace all uses of both results and then erase the operation + const auto catalystMeasure = catalystOp->getResult(0); + const auto catalystQubit = catalystOp->getResult(1); + rewriter.replaceOp(op, ValueRange{catalystQubit, catalystMeasure}); + return success(); + } +}; + +struct ConvertMQTOptLoad final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(memref::LoadOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Only convert loads of qubit type + if (!isa(op.getType())) { + return failure(); + } + + // Prepare the result type(s) + auto resultType = catalyst::quantum::QubitType::get(rewriter.getContext()); + + // Get index (assuming single index for 1D memref) + auto indices = adaptor.getIndices(); + Value index = indices.empty() ? nullptr : indices[0]; + + // Convert index type to i64 if needed + if (index && mlir::isa(index.getType())) { + index = rewriter.create(op.getLoc(), + rewriter.getI64Type(), index); + } + + // Create the new operation + auto catalystOp = rewriter.create( + op.getLoc(), resultType, adaptor.getMemref(), index, nullptr); + + // Replace the load operation with the extracted qubit + rewriter.replaceOp(op, catalystOp.getResult()); + return success(); + } +}; + +struct ConvertMQTOptStore final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(memref::StoreOp op, OpAdaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Only convert stores to memrefs with qubit element type + auto memrefType = cast(op.getMemRef().getType()); + if (!isa(memrefType.getElementType())) { + return failure(); + } + + // Get indices (assuming single index for 1D memref) + auto indices = adaptor.getIndices(); + Value index = indices.empty() ? nullptr : indices[0]; + + // Convert index type to i64 if needed + if (index && mlir::isa(index.getType())) { + index = rewriter.create(op.getLoc(), + rewriter.getI64Type(), index); + } + + // Prepare the result type(s) + auto resultType = catalyst::quantum::QuregType::get(rewriter.getContext()); + + // Create the new operation + rewriter.create(op.getLoc(), resultType, + adaptor.getMemref(), index, + nullptr, adaptor.getValue()); + + // Erase the original store operation (store has no results to replace) + rewriter.eraseOp(op); + return success(); + } +}; + +template +struct ConvertMQTOptSimpleGate final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(MQTGateOp op, typename MQTGateOp::Adaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // BarrierOp has no semantic effect. + if (std::is_same_v) { + rewriter.eraseOp(op); + return success(); + } + + // Extract operands and control information using helper function + auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); + + // Gate name may depend on number of controls + const StringRef gateName = + getGateName(extracted.ctrlInfo.ctrlQubits.size()); + if (gateName.empty()) { + op->emitError() << "Unsupported controlled gate for op: " + << op->getName(); + return failure(); + } + + // Sanity: lengths must match, or the op verifier will complain. + if (extracted.ctrlInfo.ctrlQubits.size() != + extracted.ctrlInfo.ctrlValues.size()) { + op->emitError() << "control qubits and control values size mismatch"; + return failure(); + } + + // Create CustomOp + auto custom = rewriter.create( + op.getLoc(), + /*gate=*/gateName, + /*in_qubits=*/extracted.inQubits, + /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/adaptor.getParams(), + /*adjoint=*/false); + + // ---- Replace: CustomOp results are (out_qubits, out_ctrl_qubits) ---- + SmallVector replacements; + replacements.append(custom.getOutQubits().begin(), + custom.getOutQubits().end()); + replacements.append(custom.getOutCtrlQubits().begin(), + custom.getOutCtrlQubits().end()); + + rewriter.replaceOp(op, replacements); + return success(); + } + +private: + // Is specialized for each gate type + static StringRef getGateName(std::size_t numControls); +}; + +template +struct ConvertMQTOptAdjointGate final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(MQTGateOp op, typename MQTGateOp::Adaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Get the base gate name and whether it is an adjoint version + const auto& [gateName, adjoint] = getGateInfo(); + + // Extract control information + const ControlInfo ctrlInfo = + extractControlInfo(adaptor.getPosCtrlInQubits(), + adaptor.getNegCtrlInQubits(), rewriter, op.getLoc()); + + // Create CustomOp with adjoint flag + auto catalystOp = rewriter.create( + op.getLoc(), + /*gate=*/gateName, + /*in_qubits=*/adaptor.getInQubits(), + /*in_ctrl_qubits=*/ctrlInfo.ctrlQubits, + /*in_ctrl_values=*/ctrlInfo.ctrlValues, + /*params=*/adaptor.getParams(), + /*adjoint=*/adjoint); + + rewriter.replaceOp(op, catalystOp); + return success(); + } + +private: + template static std::pair getGateInfo() { + if constexpr (std::is_same_v) { + return {"S", true}; + } else if constexpr (std::is_same_v) { + return {"T", true}; + } else if constexpr (std::is_same_v) { + return {"ISWAP", true}; + } else if constexpr (std::is_same_v) { + return {"SX", true}; + } + // Default case + return {"", false}; + } +}; + +// Conversions of unsupported gates, which need decomposition +template <> +struct ConvertMQTOptSimpleGate final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(opt::VOp op, opt::VOp::Adaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operands and control information using helper function + auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); + + // V = RZ(π/2) RY(π/2) RZ(-π/2) + auto pi2 = rewriter.create(op.getLoc(), + rewriter.getF64FloatAttr(M_PI_2)); + + // Create the decomposed operations + auto rz1 = rewriter.create( + op.getLoc(), + /*gate_name=*/"RZ", + /*in_qubits=*/extracted.inQubits, + /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{pi2}, + /*adjoint=*/false); + + auto ry = rewriter.create( + op.getLoc(), + /*gate_name=*/"RY", + /*in_qubits=*/rz1.getOutQubits(), + /*in_ctrl_qubits=*/rz1.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{pi2}, + /*adjoint=*/false); + + auto rz2 = rewriter.create( + op.getLoc(), + /*gate_name=*/"RZ", + /*in_qubits=*/ry.getOutQubits(), + /*in_ctrl_qubits=*/ry.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{pi2}, + /*adjoint=*/true); + + // ---- Replace: CustomOp results are (out_qubits, out_ctrl_qubits) ---- + SmallVector replacements; + replacements.append(rz2.getOutQubits().begin(), rz2.getOutQubits().end()); + replacements.append(rz2.getOutCtrlQubits().begin(), + rz2.getOutCtrlQubits().end()); + + rewriter.replaceOp(op, replacements); + return success(); + } +}; + +template <> +struct ConvertMQTOptSimpleGate final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(opt::VdgOp op, opt::VdgOp::Adaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operands and control information using helper function + auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); + + // V† = RZ(π/2) RY(-π/2) RZ(-π/2) + auto negPi2 = rewriter.create( + op.getLoc(), rewriter.getF64FloatAttr(-M_PI_2)); + + // Create the decomposed operations + auto rz1 = rewriter.create( + op.getLoc(), + /*gate=*/"RZ", + /*in_qubits=*/extracted.inQubits, + /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{negPi2}, + /*adjoint=*/true); + + auto ry = rewriter.create( + op.getLoc(), + /*gate=*/"RY", + /*in_qubits=*/rz1.getOutQubits(), + /*in_ctrl_qubits=*/rz1.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{negPi2}, + /*adjoint=*/false); + + auto rz2 = rewriter.create( + op.getLoc(), + /*gate=*/"RZ", + /*in_qubits=*/ry.getOutQubits(), + /*in_ctrl_qubits=*/ry.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{negPi2}, + /*adjoint=*/false); + + // ---- Replace: CustomOp results are (out_qubits, out_ctrl_qubits) ---- + SmallVector replacements; + replacements.append(rz2.getOutQubits().begin(), rz2.getOutQubits().end()); + replacements.append(rz2.getOutCtrlQubits().begin(), + rz2.getOutCtrlQubits().end()); + + rewriter.replaceOp(op, replacements); + return success(); + } +}; + +template <> +struct ConvertMQTOptSimpleGate final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(opt::DCXOp op, opt::DCXOp::Adaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operands and control information using helper function + auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); + + // DCX = CNOT(q2,q1) CNOT(q1,q2) + auto cnot1 = rewriter.create( + op.getLoc(), + /*gate=*/"CNOT", + /*in_qubits=*/extracted.inQubits, + /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{}, + /*adjoint=*/false); + + auto cnot2 = rewriter.create( + op.getLoc(), + /*gate=*/"CNOT", + /*in_qubits=*/ + ValueRange{cnot1.getOutQubits()[1], cnot1.getOutQubits()[0]}, + /*in_ctrl_qubits=*/cnot1.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{}, + /*adjoint=*/false); + + // ---- Replace: CustomOp results are (out_qubits, out_ctrl_qubits) ---- + SmallVector replacements; + replacements.append(cnot2.getOutQubits().begin(), + cnot2.getOutQubits().end()); + replacements.append(cnot2.getOutCtrlQubits().begin(), + cnot2.getOutCtrlQubits().end()); + + rewriter.replaceOp(op, replacements); + return success(); + } +}; + +template <> +struct ConvertMQTOptSimpleGate final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(opt::RZXOp op, opt::RZXOp::Adaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operands and control information using helper function + auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); + + // RZX(q0, q1; θ) = H(q1) · RZZ(q0, q1; θ) · H(q1) + // H gates can stay uncontrolled, as they cancel if control on RZZ is not + // active + + // H on q1 + auto h1 = rewriter.create( + op.getLoc(), + /*gate=*/"Hadamard", + /*in_qubits=*/ValueRange{extracted.inQubits[1]}, + /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{}, + /*adjoint=*/false); + + // RZZ on (q0, q1') + auto rzz = rewriter.create( + op.getLoc(), + /*gate=*/"IsingZZ", + /*in_qubits=*/ValueRange{extracted.inQubits[0], h1.getOutQubits()[0]}, + /*in_ctrl_qubits=*/h1.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/adaptor.getParams()[0], + /*adjoint=*/false); + + // H on q1'' + auto h2 = rewriter.create( + op.getLoc(), + /*gate=*/"Hadamard", + /*in_qubits=*/ValueRange{rzz.getOutQubits()[1]}, + /*in_ctrl_qubits=*/rzz.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{}, + /*adjoint=*/false); + + // Final results in mqt.opt ordering (targets..., controls...) + SmallVector finalResults; + finalResults.push_back(rzz.getOutQubits()[0]); // target0 final + finalResults.push_back(h2.getOutQubits()[0]); // target1 final + finalResults.append(h2.getOutCtrlQubits().begin(), + h2.getOutCtrlQubits().end()); // controls + + rewriter.replaceOp(op, finalResults); + return success(); + } +}; + +template <> +struct ConvertMQTOptSimpleGate final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(opt::GPhaseOp op, opt::GPhaseOp::Adaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract control information using helper function (no input qubits for + // GPhase) + auto ctrlInfo = + extractControlInfo(adaptor.getPosCtrlInQubits(), + adaptor.getNegCtrlInQubits(), rewriter, op.getLoc()); + const auto params = adaptor.getParams(); + + // Create output types for GlobalPhaseOp (control qubits only) + const Type qubitType = + catalyst::quantum::QubitType::get(rewriter.getContext()); + const SmallVector outCtrlTypes(ctrlInfo.ctrlQubits.size(), qubitType); + + auto gphase = rewriter.create( + op.getLoc(), TypeRange(outCtrlTypes), params[0], false, + ctrlInfo.ctrlQubits, ctrlInfo.ctrlValues); + + // Replace the original operation with the decomposition + rewriter.replaceOp(op, gphase.getResults()); + return success(); + } +}; + +template <> +struct ConvertMQTOptSimpleGate final : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(opt::UOp op, opt::UOp::Adaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operands and control information using helper function + auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); + + // Extract parameters + SmallVector paramValues; + auto dynamicParams = adaptor.getParams(); + auto staticParams = op.getStaticParams(); + auto paramMask = op.getParamsMask(); + + // There must be exactly 3 parameters + constexpr size_t numParams = 3; + for (size_t i = 0, dynIdx = 0, statIdx = 0; i < numParams; ++i) { + if (paramMask.has_value()) { + if ((*paramMask)[i]) { + // Static parameter + auto attr = (*staticParams)[statIdx++]; + auto floatAttr = rewriter.getF64FloatAttr(attr); + auto constOp = rewriter.create(op.getLoc(), floatAttr); + paramValues.push_back(constOp); + } else { + // Dynamic parameter + paramValues.push_back(dynamicParams[dynIdx++]); + } + } else if (staticParams.has_value()) { + // All static + auto attr = (*staticParams)[i]; + auto floatAttr = rewriter.getF64FloatAttr(attr); + auto constOp = rewriter.create(op.getLoc(), floatAttr); + paramValues.push_back(constOp); + } else { + // All dynamic + paramValues.push_back(dynamicParams[i]); + } + } + // Now paramValues[0] = θ, [1] = φ, [2] = λ + auto theta = paramValues[0]; + auto phi = paramValues[1]; + auto lambda = paramValues[2]; + + // Based on + // https://docs.quantum.ibm.com/api/qiskit/0.24/qiskit.circuit.library.UGate + // U(θ, φ, λ) = RZ(φ − π⁄2) ⋅ RX(π⁄2) ⋅ RZ(π − θ) ⋅ RX(π⁄2) ⋅ RZ(λ − π⁄2) + // Note: The MQT UOp uses U(θ/2, φ, λ) + auto pi = rewriter.create(op.getLoc(), + rewriter.getF64FloatAttr(M_PI)); + auto pi2 = rewriter.create(op.getLoc(), + rewriter.getF64FloatAttr(M_PI_2)); + + // Compute φ - π/2 + auto phiMinusPi2 = rewriter.create(op.getLoc(), phi, pi2); + // Compute π - θ/2 + auto two = + rewriter.create(op.getLoc(), rewriter.getF64FloatAttr(2.0)); + auto theta2 = rewriter.create(op.getLoc(), theta, two); + auto piMinusTheta2 = rewriter.create(op.getLoc(), pi, theta2); + // Compute λ - π/2 + auto lambdaMinusPi2 = rewriter.create(op.getLoc(), lambda, pi2); + + // RZ(λ − π/2) + auto rz1 = rewriter.create( + op.getLoc(), + /*gate=*/"RZ", + /*in_qubits=*/extracted.inQubits, + /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{lambdaMinusPi2}, + /*adjoint=*/false); + + // RX(π/2) + auto rx1 = rewriter.create( + op.getLoc(), + /*gate=*/"RX", + /*in_qubits=*/rz1.getOutQubits(), + /*in_ctrl_qubits=*/rz1.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{pi2}, + /*adjoint=*/false); + + // RZ(π − θ) + auto rz2 = rewriter.create( + op.getLoc(), + /*gate=*/"RZ", + /*in_qubits=*/rx1.getOutQubits(), + /*in_ctrl_qubits=*/rx1.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{piMinusTheta2}, + /*adjoint=*/false); + + // RX(π/2) + auto rx2 = rewriter.create( + op.getLoc(), + /*gate=*/"RX", + /*in_qubits=*/rz2.getOutQubits(), + /*in_ctrl_qubits=*/rz2.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{pi2}, + /*adjoint=*/false); + + // RZ(φ − π/2) + auto rz3 = rewriter.create( + op.getLoc(), + /*gate=*/"RZ", + /*in_qubits=*/rx2.getOutQubits(), + /*in_ctrl_qubits=*/rx2.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{phiMinusPi2}, + /*adjoint=*/false); + + // ---- Replace: CustomOp results are (out_qubits, out_ctrl_qubits) ---- + SmallVector replacements; + replacements.append(rz3.getOutQubits().begin(), rz3.getOutQubits().end()); + replacements.append(rz3.getOutCtrlQubits().begin(), + rz3.getOutCtrlQubits().end()); + + // Replace the original U gate with the decomposed sequence + rewriter.replaceOp(op, replacements); + return success(); + } +}; + +template <> +struct ConvertMQTOptSimpleGate final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(opt::U2Op op, opt::U2Op::Adaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operands and control information using helper function + auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); + + // Extract parameters + SmallVector paramValues; + auto dynamicParams = adaptor.getParams(); + auto staticParams = op.getStaticParams(); + auto paramMask = op.getParamsMask(); + + // There must be exactly 2 parameters + constexpr size_t numParams = 2; + for (size_t i = 0, dynIdx = 0, statIdx = 0; i < numParams; ++i) { + if (paramMask.has_value()) { + if ((*paramMask)[i]) { + // Static parameter + auto attr = (*staticParams)[statIdx++]; + auto floatAttr = rewriter.getF64FloatAttr(attr); + auto constOp = rewriter.create(op.getLoc(), floatAttr); + paramValues.push_back(constOp); + } else { + // Dynamic parameter + paramValues.push_back(dynamicParams[dynIdx++]); + } + } else if (staticParams.has_value()) { + // All static + auto attr = (*staticParams)[i]; + auto floatAttr = rewriter.getF64FloatAttr(attr); + auto constOp = rewriter.create(op.getLoc(), floatAttr); + paramValues.push_back(constOp); + } else { + // All dynamic + paramValues.push_back(dynamicParams[i]); + } + } + // Now paramValues [0] = φ, [1] = λ + auto phi = paramValues[0]; + auto lambda = paramValues[1]; + + // U2(φ, λ) = U(π/2, φ, λ) = RZ(φ − π⁄2) ⋅ RX(π⁄2) ⋅ RZ(3/4 π) ⋅ RX(π⁄2) ⋅ + // RZ(λ − π⁄2) + auto pi2 = rewriter.create(op.getLoc(), + rewriter.getF64FloatAttr(M_PI_2)); + auto pi4 = rewriter.create(op.getLoc(), + rewriter.getF64FloatAttr(M_PI_4)); + auto three = + rewriter.create(op.getLoc(), rewriter.getF64FloatAttr(3.0)); + auto pi34 = rewriter.create(op.getLoc(), pi4, three); + + // Compute φ - π/2 + auto phiMinusPi2 = rewriter.create(op.getLoc(), phi, pi2); + // Compute λ - π/2 + auto lambdaMinusPi2 = rewriter.create(op.getLoc(), lambda, pi2); + + // RZ(λ − π/2) + auto rz1 = rewriter.create( + op.getLoc(), + /*gate=*/"RZ", + /*in_qubits=*/extracted.inQubits, + /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{lambdaMinusPi2}, + /*adjoint=*/false); + + // RX(π/2) + auto rx1 = rewriter.create( + op.getLoc(), + /*gate=*/"RX", + /*in_qubits=*/rz1.getOutQubits(), + /*in_ctrl_qubits=*/rz1.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{pi2}, + /*adjoint=*/false); + + // RZ(3/4 π) + auto rz2 = rewriter.create( + op.getLoc(), + /*gate=*/"RZ", + /*in_qubits=*/rx1.getOutQubits(), + /*in_ctrl_qubits=*/rx1.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{pi34}, + /*adjoint=*/false); + + // RX(π/2) + auto rx2 = rewriter.create( + op.getLoc(), + /*gate=*/"RX", + /*in_qubits=*/rz2.getOutQubits(), + /*in_ctrl_qubits=*/rz2.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{pi2}, + /*adjoint=*/false); + + // RZ(φ − π/2) + auto rz3 = rewriter.create( + op.getLoc(), + /*gate=*/"RZ", + /*in_qubits=*/rx2.getOutQubits(), + /*in_ctrl_qubits=*/rx2.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{phiMinusPi2}, + /*adjoint=*/false); + + // ---- Replace: CustomOp results are (out_qubits, out_ctrl_qubits) ---- + SmallVector replacements; + replacements.append(rz3.getOutQubits().begin(), rz3.getOutQubits().end()); + replacements.append(rz3.getOutCtrlQubits().begin(), + rz3.getOutCtrlQubits().end()); + + // Replace the original U gate with the decomposed sequence + rewriter.replaceOp(op, replacements); + return success(); + } +}; + +template <> +struct ConvertMQTOptSimpleGate final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(opt::PeresOp op, opt::PeresOp::Adaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operands and control information using helper function + auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); + + // Peres = CNOT(q0, q1) ; X(q0) + + // CNOT(q0, q1) + auto cnot = rewriter.create( + op.getLoc(), + /*gate=*/"CNOT", + /*in_qubits=*/ValueRange{extracted.inQubits[0], extracted.inQubits[1]}, + /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{}, + /*adjoint=*/false); + + const Value q0AfterCnot = cnot.getOutQubits()[0]; + const Value q1AfterCnot = cnot.getOutQubits()[1]; + + // X(q0') + auto x = rewriter.create( + op.getLoc(), + /*gate=*/"PauliX", + /*in_qubits=*/ValueRange{q0AfterCnot}, + /*in_ctrl_qubits=*/cnot.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{}, + /*adjoint=*/false); + + const Value q0Final = x.getOutQubits()[0]; // target0 + const Value q1Final = q1AfterCnot; // target1 + + // Final: (targets..., controls...) + SmallVector finalResults; + finalResults.push_back(q0Final); + finalResults.push_back(q1Final); + finalResults.append(x.getOutCtrlQubits().begin(), + x.getOutCtrlQubits().end()); + + rewriter.replaceOp(op, finalResults); + return success(); + } +}; + +template <> +struct ConvertMQTOptSimpleGate final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(opt::PeresdgOp op, opt::PeresdgOp::Adaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operands and control information using helper function + auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); + + // Peres† = X(q0) ; CNOT(q0, q1) + + // X(q0) + auto x = rewriter.create( + op.getLoc(), + /*gate=*/"PauliX", + /*in_qubits=*/ValueRange{extracted.inQubits[0]}, + /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{}, + /*adjoint=*/false); + + const Value q0AfterX = x.getOutQubits()[0]; + + // CNOT(q0', q1) + auto cnot = rewriter.create( + op.getLoc(), + /*gate=*/"CNOT", + /*in_qubits=*/ValueRange{q0AfterX, extracted.inQubits[1]}, + /*in_ctrl_qubits=*/x.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{}, + /*adjoint=*/false); + + // Final: (targets..., controls...) + SmallVector finalResults; + finalResults.push_back(cnot.getOutQubits()[0]); // q0_final + finalResults.push_back(cnot.getOutQubits()[1]); // q1_final + finalResults.append(cnot.getOutCtrlQubits().begin(), + cnot.getOutCtrlQubits().end()); // controls + + rewriter.replaceOp(op, finalResults); + return success(); + } +}; + +// -- IOp (Identity) +template <> +StringRef ConvertMQTOptSimpleGate::getGateName( + [[maybe_unused]] std::size_t numControls) { + return "Identity"; +} + +// -- XOp (PauliX, CNOT, Toffoli) +template <> +StringRef +ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { + if (numControls == 1) { + return "CNOT"; + } + if (numControls == 2) { + return "Toffoli"; + } + // 0 or 3+ controls: use PauliX with explicit control qubits + return "PauliX"; +} + +// -- YOp (PauliY, CY for 1 control, PauliY for 2+ controls) +template <> +StringRef +ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { + // CY is the special name for exactly 1 control + if (numControls == 1) { + return "CY"; + } + // 0 or 2+ controls: use PauliY with explicit control qubits + return "PauliY"; +} + +// -- ZOp (PauliZ, CZ for 1 control, PauliZ for 2+ controls) +template <> +StringRef +ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { + // CZ is the special name for exactly 1 control + if (numControls == 1) { + return "CZ"; + } + // 0 or 2+ controls: use PauliZ with explicit control qubits + return "PauliZ"; +} + +// -- HOp (Hadamard) +template <> +StringRef ConvertMQTOptSimpleGate::getGateName( + [[maybe_unused]] std::size_t numControls) { + return "Hadamard"; +} + +// -- SOP (S) +template <> +StringRef ConvertMQTOptSimpleGate::getGateName( + [[maybe_unused]] std::size_t numControls) { + return "S"; +} + +// -- TOP (T) +template <> +StringRef ConvertMQTOptSimpleGate::getGateName( + [[maybe_unused]] std::size_t numControls) { + return "T"; +} + +// -- ECROp (ECR) +template <> +StringRef ConvertMQTOptSimpleGate::getGateName( + [[maybe_unused]] std::size_t numControls) { + return "ECR"; +} + +// -- SWAPOp (SWAP) +template <> +StringRef ConvertMQTOptSimpleGate::getGateName( + const std::size_t numControls) { + if (numControls == 0) { + return "SWAP"; + } + if (numControls == 1) { + return "CSWAP"; + } + return ""; +} + +// -- iSWAPOp (iSWAP) +template <> +StringRef ConvertMQTOptSimpleGate::getGateName( + [[maybe_unused]] std::size_t numControls) { + return "ISWAP"; +} + +// -- RXOp (RX, CRX) +template <> +StringRef +ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { + if (numControls == 0) { + return "RX"; + } + if (numControls == 1) { + return "CRX"; + } + return ""; +} + +// -- RYOp (RY, CRY) +template <> +StringRef +ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { + if (numControls == 0) { + return "RY"; + } + if (numControls == 1) { + return "CRY"; + } + return ""; +} + +// -- RZOp (RZ, CRZ) +template <> +StringRef +ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { + if (numControls == 0) { + return "RZ"; + } + if (numControls == 1) { + return "CRZ"; + } + return ""; +} + +// -- POp (PhaseShift, ControlledPhaseShift) +template <> +StringRef +ConvertMQTOptSimpleGate::getGateName(const std::size_t numControls) { + if (numControls == 0) { + return "PhaseShift"; + } + if (numControls == 1) { + return "ControlledPhaseShift"; + } + return ""; +} + +// -- RXXOp (IsingXX) +template <> +StringRef ConvertMQTOptSimpleGate::getGateName( + [[maybe_unused]] std::size_t numControls) { + return "IsingXX"; +} + +// -- RYYOp (IsingYY) +template <> +StringRef ConvertMQTOptSimpleGate::getGateName( + [[maybe_unused]] std::size_t numControls) { + return "IsingYY"; +} + +// -- RZZ (IsingZZ) +template <> +StringRef ConvertMQTOptSimpleGate::getGateName( + [[maybe_unused]] std::size_t numControls) { + return "IsingZZ"; +} + +template <> +struct ConvertMQTOptSimpleGate final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(opt::XXminusYYOp op, opt::XXminusYYOp::Adaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operands and control information. + auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); + + // Gather parameters (phi, beta) handling static/dynamic mask + auto params = adaptor.getParams(); + auto staticParams = op.getStaticParams(); + auto paramMask = op.getParamsMask(); + + SmallVector paramValues; + size_t dynamicIdx = 0; + size_t staticIdx = 0; + + for (size_t i = 0; i < 2; ++i) { + if (paramMask && (*paramMask)[i]) { + // Static parameter. + auto floatAttr = rewriter.getF64FloatAttr((*staticParams)[staticIdx++]); + paramValues.push_back( + rewriter.create(op.getLoc(), floatAttr)); + } else { + // Dynamic parameter. + paramValues.push_back(params[dynamicIdx++]); + } + } + + const Value phi = paramValues[0]; // First parameter. + const Value beta = paramValues[1]; // Second parameter. + + // Create constant for pi. + auto pi = rewriter.create(op.getLoc(), + rewriter.getF64FloatAttr(M_PI)); + + // Compute beta - pi and pi - beta. + auto betaMinusPi = rewriter.create(op.getLoc(), beta, pi); + auto piMinusBeta = rewriter.create(op.getLoc(), pi, beta); + + // Conjugation identity: + // XXminusYY = (X ⊗ I) · (XXplusYY) · (X ⊗ I) + // Apply X on qubit 0, then same decomposition as XXplusYY, then undo + // X. + + // Pre-conjugation X on qubit 0 (respect original control semantics). + auto xPre = rewriter.create( + op.getLoc(), + /*gate=*/"X", + /*in_qubits=*/ValueRange{extracted.inQubits[0]}, + /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{}, + /*adjoint=*/false); + + // Apply RZ(pi - beta) on qubit 1 (second qubit) using control output from + // X. + auto rz1 = rewriter.create( + op.getLoc(), + /*gate=*/"RZ", + /*in_qubits=*/ValueRange{extracted.inQubits[1]}, + /*in_ctrl_qubits=*/xPre.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{piMinusBeta}, + /*adjoint=*/false); + + // Apply IsingXY(phi) on both qubits. + // Use outputs from xPre and rz1 as the inputs to IsingXY to preserve SSA + // flow. + auto isingxy = rewriter.create( + op.getLoc(), + /*gate=*/"IsingXY", + /*in_qubits=*/ValueRange{xPre.getOutQubits()[0], rz1.getOutQubits()[0]}, + /*in_ctrl_qubits=*/rz1.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{phi}, + /*adjoint=*/false); + + // Apply RZ(beta - pi) on qubit 1 after IsingXY. + auto rz2 = rewriter.create( + op.getLoc(), + /*gate=*/"RZ", + /*in_qubits=*/ValueRange{isingxy.getOutQubits()[1]}, + /*in_ctrl_qubits=*/isingxy.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{betaMinusPi}, + /*adjoint=*/false); + + // Post-conjugation X on qubit 0 to undo the pre X. + auto xPost = rewriter.create( + op.getLoc(), + /*gate=*/"X", + /*in_qubits=*/ValueRange{isingxy.getOutQubits()[0]}, + /*in_ctrl_qubits=*/rz2.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{}, + /*adjoint=*/false); + + // Final results: (q0_final, q1_final, controls...) + SmallVector finalResults; + finalResults.push_back(xPost.getOutQubits()[0]); // q0 after undo-X. + finalResults.push_back(rz2.getOutQubits()[0]); // q1 after final RZ. + finalResults.append(xPost.getOutCtrlQubits().begin(), + xPost.getOutCtrlQubits().end()); + + rewriter.replaceOp(op, finalResults); + return success(); + } +}; + +// -- XXplusYY (IsingXY) - Special handling with decomposition +template <> +struct ConvertMQTOptSimpleGate final + : OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(opt::XXplusYYOp op, opt::XXplusYYOp::Adaptor adaptor, + ConversionPatternRewriter& rewriter) const override { + // Extract operands and control information + auto extracted = extractOperands(adaptor, rewriter, op.getLoc()); + + // XXplusYY(phi, beta) = (I ⊗ Rz(beta - pi)) · IsingXY(phi) · (I ⊗ Rz(pi - + // beta)) We need to extract both parameters + + auto params = adaptor.getParams(); + auto staticParams = op.getStaticParams(); + auto paramMask = op.getParamsMask(); + + // Extract parameters: phi (theta) and beta + SmallVector paramValues; + size_t dynamicIdx = 0; + size_t staticIdx = 0; + + for (size_t i = 0; i < 2; ++i) { + if (paramMask && (*paramMask)[i]) { + // Static parameter + auto floatAttr = rewriter.getF64FloatAttr((*staticParams)[staticIdx++]); + paramValues.push_back( + rewriter.create(op.getLoc(), floatAttr)); + } else { + // Dynamic parameter + paramValues.push_back(params[dynamicIdx++]); + } + } + + const Value phi = paramValues[0]; // First parameter + const Value beta = paramValues[1]; // Second parameter + + // Create constants for pi + auto pi = rewriter.create(op.getLoc(), + rewriter.getF64FloatAttr(M_PI)); + + // Compute beta - pi + auto betaMinusPi = rewriter.create(op.getLoc(), beta, pi); + + // Compute pi - beta + auto piMinusBeta = rewriter.create(op.getLoc(), pi, beta); + + // Apply Rz(pi - beta) on qubit 1 (second qubit) + auto rz1 = rewriter.create( + op.getLoc(), + /*gate=*/"RZ", + /*in_qubits=*/ValueRange{extracted.inQubits[1]}, + /*in_ctrl_qubits=*/extracted.ctrlInfo.ctrlQubits, + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{piMinusBeta}, + /*adjoint=*/false); + + // Apply IsingXY(phi) on both qubits + auto isingxy = rewriter.create( + op.getLoc(), + /*gate=*/"IsingXY", + /*in_qubits=*/ValueRange{extracted.inQubits[0], rz1.getOutQubits()[0]}, + /*in_ctrl_qubits=*/rz1.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{phi}, + /*adjoint=*/false); + + // Apply Rz(beta - pi) on qubit 1 (second qubit) + auto rz2 = rewriter.create( + op.getLoc(), + /*gate=*/"RZ", + /*in_qubits=*/ValueRange{isingxy.getOutQubits()[1]}, + /*in_ctrl_qubits=*/isingxy.getOutCtrlQubits(), + /*in_ctrl_values=*/extracted.ctrlInfo.ctrlValues, + /*params=*/ValueRange{betaMinusPi}, + /*adjoint=*/false); + + // Final results: (q0_final, q1_final, controls...) + SmallVector finalResults; + finalResults.push_back(isingxy.getOutQubits()[0]); // q0 from IsingXY + finalResults.push_back(rz2.getOutQubits()[0]); // q1 after final Rz + finalResults.append(rz2.getOutCtrlQubits().begin(), + rz2.getOutCtrlQubits().end()); + + rewriter.replaceOp(op, finalResults); + return success(); + } +}; + +struct MQTOptToCatalystQuantum final + : impl::MQTOptToCatalystQuantumBase { + using MQTOptToCatalystQuantumBase::MQTOptToCatalystQuantumBase; + + void runOnOperation() override { + MLIRContext* context = &getContext(); + auto* module = getOperation(); + + ConversionTarget target(*context); + target.addLegalDialect(); + target.addLegalDialect(); + target.addLegalDialect(); + target.addIllegalDialect(); + + // Mark memref operations on qubits as illegal to trigger conversion + target.addDynamicallyLegalOp([](memref::AllocOp op) { + auto memrefType = dyn_cast(op.getType()); + if (!memrefType) { + return true; + } + auto elementType = memrefType.getElementType(); + return !isa(elementType); + }); + + target.addDynamicallyLegalOp([](memref::DeallocOp op) { + auto memrefType = dyn_cast(op.getMemref().getType()); + if (!memrefType) { + return true; + } + auto elementType = memrefType.getElementType(); + return !isa(elementType); + }); + + target.addDynamicallyLegalOp([](memref::LoadOp op) { + auto memrefType = dyn_cast(op.getMemRef().getType()); + if (!memrefType) { + return true; + } + auto elementType = memrefType.getElementType(); + return !isa(elementType); + }); + + target.addDynamicallyLegalOp([](memref::StoreOp op) { + auto memrefType = dyn_cast(op.getMemRef().getType()); + if (!memrefType) { + return true; + } + auto elementType = memrefType.getElementType(); + return !isa(elementType); + }); + + RewritePatternSet patterns(context); + MQTOptToCatalystQuantumTypeConverter typeConverter(context); + + patterns.add(typeConverter, + context); + + patterns.add>(typeConverter, + context); + patterns.add>(typeConverter, + context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, + context); + + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, context); + patterns.add>(typeConverter, + context); + patterns.add>(typeConverter, + context); + patterns.add>(typeConverter, + context); + + // Type conversion boilerplate to handle function signatures and control + // flow See: https://www.jeremykun.com/2023/10/23/mlir-dialect-conversion + + // Convert func.func signatures to use the converted types + populateFunctionOpInterfaceTypeConversionPattern( + patterns, typeConverter); + + // Mark func.func as legal only if signature and body types are converted + target.addDynamicallyLegalOp([&](func::FuncOp op) { + return typeConverter.isSignatureLegal(op.getFunctionType()) && + typeConverter.isLegal(&op.getBody()); + }); + + // Convert return ops to match the new function result types + populateReturnOpTypeConversionPattern(patterns, typeConverter); + + // Mark func.return as legal only if operand types match converted types + target.addDynamicallyLegalOp( + [&](const func::ReturnOp op) { return typeConverter.isLegal(op); }); + + // Convert call sites to use the converted argument and result types + populateCallOpTypeConversionPattern(patterns, typeConverter); + + // Mark func.call as legal only if operand and result types are converted + target.addDynamicallyLegalOp( + [&](const func::CallOp op) { return typeConverter.isLegal(op); }); + + // Convert control-flow ops (cf.br, cf.cond_br, etc.) + populateBranchOpInterfaceTypeConversionPattern(patterns, typeConverter); + + // Mark unknown ops as legal if they don't require type conversion + target.markUnknownOpDynamicallyLegal([&](Operation* op) { + return isNotBranchOpInterfaceOrReturnLikeOp(op) || + isLegalForBranchOpInterfaceTypeConversionPattern(op, + typeConverter) || + isLegalForReturnOpTypeConversionPattern(op, typeConverter); + }); + + if (failed(applyPartialConversion(module, target, std::move(patterns)))) { + signalPassFailure(); + } + } +}; + +} // namespace mqt::ir::conversions diff --git a/plugins/catalyst/lib/mqt-plugin.cpp b/plugins/catalyst/lib/mqt-plugin.cpp new file mode 100644 index 0000000000..6f7ad4542d --- /dev/null +++ b/plugins/catalyst/lib/mqt-plugin.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 - 2025 Chair for Design Automation, TUM + * Copyright (c) 2025 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +//===- mqt-plugin.cpp ------------------------------------*- C++ -*-===// +// +// This file is licensed under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "mlir/Conversion/CatalystQuantumToMQTOpt/CatalystQuantumToMQTOpt.h" +#include "mlir/Conversion/MQTOptToCatalystQuantum/MQTOptToCatalystQuantum.h" +#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h" +#include "mlir/Dialect/MQTOpt/Transforms/Passes.h" + +#include +#include +#include +#include +#include + +using namespace mlir; + +/// Dialect plugin registration mechanism. +/// Observe that it also allows registering passes. +/// Necessary symbol to register the dialect plugin. +extern "C" LLVM_ATTRIBUTE_WEAK DialectPluginLibraryInfo +mlirGetDialectPluginInfo() { + return {MLIR_PLUGIN_API_VERSION, "MQTOpt", LLVM_VERSION_STRING, + [](DialectRegistry* registry) { + registry->insert<::mqt::ir::opt::MQTOptDialect>(); + }}; +} + +/// The pass plugin registration mechanism. +/// Necessary symbol to register the pass plugin. +extern "C" LLVM_ATTRIBUTE_WEAK PassPluginLibraryInfo mlirGetPassPluginInfo() { + return {MLIR_PLUGIN_API_VERSION, "MQTOptPasses", LLVM_VERSION_STRING, []() { + mqt::ir::opt::registerMQTOptPasses(); + mqt::ir::conversions::registerCatalystQuantumToMQTOptPasses(); + mqt::ir::conversions::registerMQTOptToCatalystQuantumPasses(); + }}; +} diff --git a/plugins/catalyst/pyproject.toml b/plugins/catalyst/pyproject.toml new file mode 100644 index 0000000000..0563c5eb83 --- /dev/null +++ b/plugins/catalyst/pyproject.toml @@ -0,0 +1,251 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +[build-system] +requires = [ + "scikit-build-core>=0.11.5", + "pennylane-catalyst>0.12.0", + # The following dependencies are needed whenever one wants to build with a pre-release version of Catalyst + # "pennylane>=0.42.0.dev68", + # "pennylane-lightning>=0.42.0.dev45", + # "pennylane-lightning-kokkos>=0.42.0.dev45", +] +build-backend = "scikit_build_core.build" + +[project] +name = "mqt-core-catalyst-plugin" +version = "0.1.0" +description = "The MQT Core Catalyst MLIR Plugin" +readme = "README.md" +authors = [ + { name = "Patrick Hopf", email = "patrick.hopf@tum.de" }, + { name = "Yannick Stade", email = "yannick.stade@tum.de" }, + { name = "Lukas Burgholzer", email = "burgholzer@me.com" } +] +keywords = ["MQT", "quantum-computing", "design-automation", "catalyst", "MLIR", "plugin"] +license = "MIT" + +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Science/Research", + "Natural Language :: English", + "Operating System :: MacOS", + "Operating System :: POSIX :: Linux", + "Programming Language :: C++", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", + "Typing :: Typed", +] +# PennyLane Catalyst requires at least Python 3.11 +requires-python = ">=3.11" +dependencies = [ + "pennylane-catalyst>0.12.0", + # The following dependencies are needed whenever one wants to build with a pre-release version of Catalyst + # "pennylane>=0.42.0.dev68", + # "pennylane-lightning>=0.42.0.dev45", + # "pennylane-lightning-kokkos>=0.42.0.dev45", +] + +[dependency-groups] +build = [ + "scikit-build-core>=0.11.5", + {include-group = "catalyst"}, +] +test = [ + "lit>=18.1.8", + "pytest>=8.3.5", + "pytest-cov>=6.1.1", +] +catalyst = [ + "pennylane-catalyst>0.12.0", + # The following dependencies are needed whenever one wants to build with a pre-release version of Catalyst + # "pennylane>=0.42.0.dev68", + # "pennylane-lightning>=0.42.0.dev45", + # "pennylane-lightning-kokkos>=0.42.0.dev45", +] +dev = [ + {include-group = "build"}, + {include-group = "test"}, +] + +[project.entry-points."catalyst.passes_resolution"] +"mqt.passes" = "mqt.core.plugins.catalyst" + +[project.urls] +Homepage = "https://github.com/munich-quantum-toolkit/core" +Changelog = "https://github.com/munich-quantum-toolkit/core/blob/main/CHANGELOG.md" +Documentation = "https://mqt.readthedocs.io/projects/core" +Issues = "https://github.com/munich-quantum-toolkit/core/issues" +Discussions = "https://github.com/munich-quantum-toolkit/core/discussions" +PyPI = "https://pypi.org/project/mqt-core/" + + +[tool.scikit-build] +# Protect the configuration against future changes in scikit-build-core +minimum-version = "build-system.requires" + +# Set the wheel install directory +wheel.install-dir = "mqt/core/plugins/catalyst" + +# Explicitly set the package directory +wheel.packages = ["python/mqt"] + +# Set required Ninja version +ninja.version = ">=1.10" + +# Setuptools-style build caching in a local directory +build-dir = "build/{wheel_tag}/{build_type}" + +# All the targets to build +build.targets = [ + "mqt-core-catalyst-plugin" +] + +# All components to install +install.components = [ + "mqt-core-catalyst_Plugin", +] + +# We are not building a CPython extension, so the produced wheel will work with any Python version +wheel.py-api = "py3" + +sdist.exclude = [ + "**/test", +] + + +[tool.check-sdist] +git-only = [ + "test/*", +] + + +[tool.pytest.ini_options] +minversion = "7.2" +addopts = ["-ra", "--strict-markers", "--strict-config"] +xfail_strict = true +filterwarnings = [ + 'error', + 'ignore:\s.*Pyarrow.*:DeprecationWarning:', + 'ignore:.*datetime\.datetime\.utcfromtimestamp.*:DeprecationWarning:', + 'ignore:.*jax\.interpreters\.mlir.*:DeprecationWarning:', + 'ignore:.*The jaxlib\\.hlo_helpers submodule is deprecated.*:DeprecationWarning', + 'ignore:.*:DeprecationWarning:gast.*', +] +log_cli_level = "INFO" +testpaths = ["test/python"] + +[tool.coverage] +run.source = ["mqt.core.plugins.catalyst"] +run.omit = [] +report.exclude_also = [ + '\.\.\.', + 'if TYPE_CHECKING:', + 'raise AssertionError', + 'raise NotImplementedError', + 'def __dir__()', # Ignore __dir__ method that exists mainly for better IDE support + '@overload' # Overloads are only for static typing +] + +[tool.mypy] +files = ["python", "test"] +mypy_path = ["$MYPY_CONFIG_FILE_DIR/python"] +python_version = "3.11" +warn_unused_configs = true +enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"] +strict = true +disallow_untyped_defs = false +explicit_package_bases = true +warn_unreachable = true +isort.required-imports = ["from __future__ import annotations"] + +[tool.typos] +default.extend-ignore-re = [ + "(?Rm)^.*(#|//)\\s*spellchecker:disable-line$", # ignore line + "(?s)(#|//)\\s*spellchecker:off.*?\\n\\s*(#|//)\\s*spellchecker:on" # ignore block +] + +[tool.typos.default.extend-words] +wille = "wille" +anc = "anc" +mch = "mch" +ket = "ket" + +[tool.repo-review] +ignore = ["GH200"] + +[tool.cibuildwheel] +build = "cp3*" +skip = "*-musllinux_* *-win_*" +archs = "auto64" +test-groups = ["test"] +test-command = "pytest {project}/test/python" +build-frontend = "build[uv]" + +[tool.cibuildwheel.linux] +environment = { DEPLOY = "ON" } + +[tool.cibuildwheel.macos] +# Set the minimum macOS deployment target to match the Catalyst requirements +environment = { MACOSX_DEPLOYMENT_TARGET = "13.0" } + + +[tool.uv] +# Only resolve on Linux (x86_64 and arm64) and macOS (arm64) +environments = [ + "sys_platform == 'darwin' and platform_machine == 'arm64'", + "sys_platform == 'linux'", +] + +# The following configuration is needed whenever one wants to build with a pre-release version of Catalyst +#prerelease = "allow" +# +#[[tool.uv.index]] +#name = "test.pypi.org" +#url = "https://test.pypi.org/simple" +#explicit = true +# +# +#[tool.uv.sources] +#pennylane-catalyst = { index = "test.pypi.org" } +#pennylane-lightning = { index = "test.pypi.org" } +#pennylane-lightning-kokkos = { index = "test.pypi.org" } +#pennylane = { index = "test.pypi.org" } + +[tool.ruff] +line-length = 120 +namespace-packages = ["mqt"] +preview = true +unsafe-fixes = true +src = ["python"] + + +[tool.ruff.lint] +select = ["ALL"] +ignore = [ + "C90", # <...> too complex + "COM812", # Conflicts with formatter + "CPY001", # Missing copyright notice at top of file + "ISC001", # Conflicts with formatter + "PLR09", # Too many <...> + "PLR2004", # Magic value used in comparison + "PLC0415", # Import should be at top of file + "S101", # Use of assert detected + "S404", # `subprocess` module is possibly insecure + "TID252" # Prefer absolute imports over relative imports from parent modules +] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.per-file-ignores] +"test/python/**" = ["T20", "INP001"] diff --git a/plugins/catalyst/python/mqt/core/plugins/catalyst/__init__.py b/plugins/catalyst/python/mqt/core/plugins/catalyst/__init__.py new file mode 100644 index 0000000000..c9921606c3 --- /dev/null +++ b/plugins/catalyst/python/mqt/core/plugins/catalyst/__init__.py @@ -0,0 +1,78 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +"""MQT Catalyst Plugin.""" + +from __future__ import annotations + +import platform +from importlib import resources +from pathlib import Path + +from mqt.core.plugins.catalyst.device import configure_device_for_mqt, get_device + + +def get_catalyst_plugin_abs_path() -> Path: + """Locate the mqt-catalyst-plugin shared library. + + Returns: + The absolute path to the plugin shared library. + + Raises: + FileNotFoundError: If the plugin library is not found. + """ + ext = {"Darwin": ".dylib", "Linux": ".so", "Windows": ".dll"}.get(platform.system(), ".so") + + # 0. Allow override via env variable + from os import getenv + + override = getenv("MQT_CATALYST_PLUGIN_PATH") + if override: + override_path = Path(override) + if override_path.exists(): + return override_path.resolve() + msg = f"Environment override MQT_CATALYST_PLUGIN_PATH is set but not valid: {override_path}" + raise FileNotFoundError(msg) + + # First check in the package resources + for file in resources.files("mqt.core.plugins.catalyst").iterdir(): + if "mqt-core-catalyst-plugin" in file.name: + return Path(str(file)).resolve() + + # Then check in site-packages + import site + + for site_dir in site.getsitepackages(): + site_path = Path(site_dir) / "mqt/core/plugins/catalyst" + if site_path.exists(): + for file in site_path.iterdir(): + if "mqt-core-catalyst-plugin" in file.name: + return file.resolve() + + msg = f"Could not locate catalyst plugin library with extension {ext}" + raise FileNotFoundError(msg) + + +def name2pass(name: str) -> tuple[Path, str]: + """Convert a pass name to its plugin path and pass name (required by Catalyst). + + Args: + name: The name of the pass, e.g., "mqt-core-round-trip". + + Returns: + A tuple containing the absolute path to the plugin and the pass name. + """ + return get_catalyst_plugin_abs_path(), name + + +__all__ = [ + "configure_device_for_mqt", + "get_catalyst_plugin_abs_path", + "get_device", + "name2pass", +] diff --git a/plugins/catalyst/python/mqt/core/plugins/catalyst/device.py b/plugins/catalyst/python/mqt/core/plugins/catalyst/device.py new file mode 100644 index 0000000000..7c405d3aa2 --- /dev/null +++ b/plugins/catalyst/python/mqt/core/plugins/catalyst/device.py @@ -0,0 +1,112 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +"""Device utilities for MQT Catalyst Plugin. + +This module provides utilities to configure PennyLane devices for use with the MQT plugin, +preventing Catalyst from decomposing gates into quantum.unitary operations with matrix parameters. +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + import pennylane as qml + + +def configure_device_for_mqt(device: qml.devices.Device) -> qml.devices.Device: + """Configure a PennyLane device to work optimally with the MQT plugin. + + This function modifies device capabilities to prevent Catalyst from decomposing + controlled gates (like qml.ctrl(PauliX)) into quantum.unitary operations with + explicit matrix parameters. Instead, gates remain as quantum.custom operations + (like "CNOT") that can be losslessly converted by the MQT plugin. + + The modification uses several techniques: + 1. Removes QubitUnitary from device.operations (prevents matrix decomposition path) + 2. Marks gates as controllable=True (prevents qml.ctrl() from triggering decomposition) + 3. Marks gates as invertible=True (prevents qml.adjoint() from triggering decomposition) + 4. Clears _to_matrix_ops (avoids Catalyst validation requiring QubitUnitary support) + 5. Uses qjit_capabilities hook (injects modified capabilities before QJITDevice init) + + Args: + device: A PennyLane device instance to configure. + + Returns: + The same device instance with modified capabilities. + + Raises: + ValueError: If the device does not have a config_filepath attribute set. + + Example: + >>> import pennylane as qml + >>> from mqt.core.plugins.catalyst.device import configure_device_for_mqt + >>> dev = qml.device("lightning.qubit", wires=2) + >>> dev = configure_device_for_mqt(dev) + >>> @qml.qnode(dev) + ... def circuit(): + ... qml.ctrl(qml.PauliX(wires=0), control=1) # Will become CNOT, not matrix + ... return qml.state() + """ + from pennylane.devices.capabilities import DeviceCapabilities + + # Load the original capabilities from the device's config file + if hasattr(device, "config_filepath") and device.config_filepath is not None: + toml_file = device.config_filepath + else: + msg = "Device does not have a config_filepath attribute set." + raise ValueError(msg) + + caps = DeviceCapabilities.from_toml_file(toml_file, "qjit") + + # Remove QubitUnitary from operations to prevent matrix decomposition + if "QubitUnitary" in caps.operations: + del caps.operations["QubitUnitary"] + + # Clear _to_matrix_ops to avoid Catalyst validation at qjit_device.py:322 + # which requires QubitUnitary support if _to_matrix_ops is set + if hasattr(device, "_to_matrix_ops"): + device._to_matrix_ops = set() # noqa: SLF001 + + # Set the qjit_capabilities hook so QJITDevice uses our modified capabilities + # This bypasses the normal TOML loading in _load_device_capabilities + device.qjit_capabilities = caps + + return device + + +def get_device(device_name: str, **kwargs: Any) -> qml.devices.Device: # noqa: ANN401 + """Create and configure a PennyLane device for use with the MQT plugin. + + This is a convenience function that creates a device and automatically configures + it to work optimally with the MQT plugin, preventing unnecessary decomposition to + unitary matrices. + + Args: + device_name: The name of the PennyLane device (e.g., "lightning.qubit"). + **kwargs: Additional keyword arguments passed to qml.device(). + + Returns: + A configured PennyLane device ready for use with MQT conversion passes. + + Example: + >>> from mqt.core.plugins.catalyst.device import get_device + >>> dev = get_device("lightning.qubit", wires=2) + >>> @qml.qnode(dev) + ... def circuit(): + ... qml.ctrl(qml.PauliX(wires=0), control=1) + ... return qml.state() + """ + import pennylane as qml + + device = qml.device(device_name, **kwargs) + return configure_device_for_mqt(device) + + +__all__ = ["configure_device_for_mqt", "get_device"] diff --git a/plugins/catalyst/python/mqt/core/plugins/catalyst/py.typed b/plugins/catalyst/python/mqt/core/plugins/catalyst/py.typed new file mode 100644 index 0000000000..5f3ea3d919 --- /dev/null +++ b/plugins/catalyst/python/mqt/core/plugins/catalyst/py.typed @@ -0,0 +1,2 @@ +# Instruct type checkers to look for inline type annotations in this package. +# See PEP 561. diff --git a/plugins/catalyst/test/CMakeLists.txt b/plugins/catalyst/test/CMakeLists.txt new file mode 100644 index 0000000000..043973f3d3 --- /dev/null +++ b/plugins/catalyst/test/CMakeLists.txt @@ -0,0 +1,25 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py + MAIN_CONFIG + ${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.py.in + ${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py) + +set(CATALYST_OPT_TEST_DEPENDS FileCheck mqt-core-catalyst-plugin) + +add_lit_testsuite( + check-mqt-core-catalyst-plugin "Running the mqt-core-catalyst-plugin regression tests" + ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${CATALYST_OPT_TEST_DEPENDS}) +set_target_properties(check-mqt-core-catalyst-plugin PROPERTIES FOLDER "Tests" EXCLUDE_FROM_ALL ON) + +add_lit_testsuites(CATALYST_OPT ${CMAKE_CURRENT_SOURCE_DIR} DEPENDS ${CATALYST_OPT_TEST_DEPENDS}) diff --git a/plugins/catalyst/test/Conversion/mqtopt_clifford.mlir b/plugins/catalyst/test/Conversion/mqtopt_clifford.mlir new file mode 100644 index 0000000000..f8f935859b --- /dev/null +++ b/plugins/catalyst/test/Conversion/mqtopt_clifford.mlir @@ -0,0 +1,135 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +// RUN: catalyst --tool=opt \ +// RUN: --load-pass-plugin=%mqt_plugin_path% \ +// RUN: --load-dialect-plugin=%mqt_plugin_path% \ +// RUN: --catalyst-pipeline="builtin.module(mqtopt-to-catalystquantum)" \ +// RUN: %s | FileCheck %s + + +// ============================================================================ +// Clifford + T and controlled variants +// Groups: Allocation & extraction / Uncontrolled / Controlled / Reinsertion +// ============================================================================ +module { + // CHECK-LABEL: func.func @testMQTOptToCatalystQuantumCliffordT + func.func @testMQTOptToCatalystQuantumCliffordT() { + // --- Allocation & extraction --------------------------------------------------------------- + // CHECK: %[[C0:.*]] = arith.constant 0 : index + // CHECK: %[[C1:.*]] = arith.constant 1 : index + // CHECK: %[[C2:.*]] = arith.constant 2 : index + // CHECK: %[[C3_I64:.*]] = arith.constant 3 : i64 + // CHECK: %[[QREG:.*]] = quantum.alloc(%[[C3_I64]]) : !quantum.reg + // CHECK: %[[IDX0:.*]] = arith.index_cast %[[C0]] : index to i64 + // CHECK: %[[Q0:.*]] = quantum.extract %[[QREG]][%[[IDX0]]] : !quantum.reg -> !quantum.bit + // CHECK: %[[IDX1:.*]] = arith.index_cast %[[C1]] : index to i64 + // CHECK: %[[Q1:.*]] = quantum.extract %[[QREG]][%[[IDX1]]] : !quantum.reg -> !quantum.bit + // CHECK: %[[IDX2:.*]] = arith.index_cast %[[C2]] : index to i64 + // CHECK: %[[Q2:.*]] = quantum.extract %[[QREG]][%[[IDX2]]] : !quantum.reg -> !quantum.bit + + // --- Uncontrolled Clifford+T gates --------------------------------------------------------- + // CHECK: %[[I:.*]] = quantum.custom "Identity"() %[[Q0]] : !quantum.bit + // CHECK: %[[H:.*]] = quantum.custom "Hadamard"() %[[I]] : !quantum.bit + + // V gate gets decomposed into a sequence of single-qubit rotations + // CHECK: %[[CST:.*]] = arith.constant {{.*}} : f64 + // CHECK: %[[RZ1:.*]] = quantum.custom "RZ"(%[[CST]]) %[[H]] : !quantum.bit + // CHECK: %[[RY1:.*]] = quantum.custom "RY"(%[[CST]]) %[[RZ1]] : !quantum.bit + // CHECK: %[[RZ2:.*]] = quantum.custom "RZ"(%[[CST]]) %[[RY1]] adj : !quantum.bit + + + // CHECK: %[[NEG_CST:.*]] = arith.constant -{{.*}} : f64 + // CHECK: %[[RZ3:.*]] = quantum.custom "RZ"(%[[NEG_CST]]) %[[RZ2]] adj : !quantum.bit + // CHECK: %[[RY2:.*]] = quantum.custom "RY"(%[[NEG_CST]]) %[[RZ3]] : !quantum.bit + // CHECK: %[[RZ4:.*]] = quantum.custom "RZ"(%[[NEG_CST]]) %[[RY2]] : !quantum.bit + + // CHECK: %[[S:.*]] = quantum.custom "S"() %[[RZ4]] : !quantum.bit + // CHECK: %[[SDG:.*]] = quantum.custom "S"() %[[S]] adj : !quantum.bit + // CHECK: %[[T:.*]] = quantum.custom "T"() %[[SDG]] : !quantum.bit + // CHECK: %[[TDG:.*]] = quantum.custom "T"() %[[T]] adj : !quantum.bit + + // --- Peres gate decomposition ------------------------------------------------------------------- + // CHECK: %[[PERES_CNOT:.*]]:2 = quantum.custom "CNOT"() %[[TDG]], %[[Q1]] : !quantum.bit, !quantum.bit + // CHECK: %[[PERES_X:.*]] = quantum.custom "PauliX"() %[[PERES_CNOT]]#0 : !quantum.bit + + // --- Peresdg gate decomposition ------------------------------------------------------------------- + // CHECK: %[[PERESDG_X:.*]] = quantum.custom "PauliX"() %[[PERES_X]] : !quantum.bit + // CHECK: %[[PERESDG_CNOT:.*]]:2 = quantum.custom "CNOT"() %[[PERESDG_X]], %[[PERES_CNOT]]#1 : !quantum.bit, !quantum.bit + + // --- Controlled Hadamard ------------------------------------------------------------------- + // CHECK: %[[CH_T:.*]], %[[CH_C:.*]] = quantum.custom "Hadamard"() %[[PERESDG_CNOT]]#0 ctrls(%[[PERESDG_CNOT]]#1) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // --- Controlled V gate decomposition (controlled RZ-RY-RZ sequence) ------------------------------------------------------------------- + // CHECK: %[[CST:.*]] = arith.constant {{.*}} : f64 + // CHECK: %[[CRZ1_T:.*]], %[[CRZ1_C:.*]] = quantum.custom "RZ"(%[[CST]]) %[[CH_T]] ctrls(%[[CH_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CRY1_T:.*]], %[[CRY1_C:.*]] = quantum.custom "RY"(%[[CST]]) %[[CRZ1_T]] ctrls(%[[CRZ1_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CRZ2_T:.*]], %[[CRZ2_C:.*]] = quantum.custom "RZ"(%[[CST]]) %[[CRY1_T]] adj ctrls(%[[CRY1_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // --- Controlled Vdg gate decomposition ------------------------------------------------------------------- + // CHECK: %[[NEG_CST:.*]] = arith.constant -{{.*}} : f64 + // CHECK: %[[CRZ3_T:.*]], %[[CRZ3_C:.*]] = quantum.custom "RZ"(%[[NEG_CST]]) %[[CRZ2_T]] adj ctrls(%[[CRZ2_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CRY2_T:.*]], %[[CRY2_C:.*]] = quantum.custom "RY"(%[[NEG_CST]]) %[[CRZ3_T]] ctrls(%[[CRZ3_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CRZ4_T:.*]], %[[CRZ4_C:.*]] = quantum.custom "RZ"(%[[NEG_CST]]) %[[CRY2_T]] ctrls(%[[CRY2_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // --- Controlled S and Sdg ------------------------------------------------------------------- + // CHECK: %[[CS_T:.*]], %[[CS_C:.*]] = quantum.custom "S"() %[[CRZ4_T]] ctrls(%[[CRZ4_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CSD_T:.*]], %[[CSD_C:.*]] = quantum.custom "S"() %[[CS_T]] adj ctrls(%[[CS_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // --- Controlled Peres gate ------------------------------------------------------------------- + // CHECK: %[[CPERES_CNOT:.*]]:2, %[[CPERES_CTRL:.*]] = quantum.custom "CNOT"() %[[CSD_T]], %[[CSD_C]] ctrls(%[[Q2]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[CPERES_X:.*]], %[[CPERES_X_CTRL:.*]] = quantum.custom "PauliX"() %[[CPERES_CNOT]]#0 ctrls(%[[CPERES_CTRL]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // --- Controlled Peresdg gate ------------------------------------------------------------------- + // CHECK: %[[CPERESDG_X:.*]], %[[CPERESDG_X_CTRL:.*]] = quantum.custom "PauliX"() %[[CPERES_X]] ctrls(%[[CPERES_X_CTRL]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CPERESDG_CNOT:.*]]:2, %[[CPERESDG_CTRL:.*]] = quantum.custom "CNOT"() %[[CPERESDG_X]], %[[CPERES_CNOT]]#1 ctrls(%[[CPERESDG_X_CTRL]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + + // --- Reinsertion --------------------------------------------------------------------------- + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[CPERESDG_CNOT]]#0 : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[CPERESDG_CNOT]]#1 : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[CPERESDG_CTRL]] : !quantum.reg, !quantum.bit + // CHECK: quantum.dealloc %[[QREG]] : !quantum.reg + + // Prepare qubits + %i0 = arith.constant 0 : index + %i1 = arith.constant 1 : index + %i2 = arith.constant 2 : index + %r0_0 = memref.alloc() : memref<3x!mqtopt.Qubit> + %q0_0 = memref.load %r0_0[%i0] : memref<3x!mqtopt.Qubit> + %q1_0 = memref.load %r0_0[%i1] : memref<3x!mqtopt.Qubit> + %q2_0 = memref.load %r0_0[%i2] : memref<3x!mqtopt.Qubit> + + // I/H/V/Vdg/S/Sdg/T/Tdg/Peres/Peresdg (non-controlled) + %q0_1 = mqtopt.i() %q0_0 : !mqtopt.Qubit + %q0_2 = mqtopt.h() %q0_1 : !mqtopt.Qubit + %q0_3 = mqtopt.v() %q0_2 : !mqtopt.Qubit + %q0_4 = mqtopt.vdg() %q0_3 : !mqtopt.Qubit + %q0_5 = mqtopt.s() %q0_4 : !mqtopt.Qubit + %q0_6 = mqtopt.sdg() %q0_5 : !mqtopt.Qubit + %q0_7 = mqtopt.t() %q0_6 : !mqtopt.Qubit + %q0_8 = mqtopt.tdg() %q0_7 : !mqtopt.Qubit + %q0_9, %q1_1 = mqtopt.peres() %q0_8, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit + %q0_10, %q1_2 = mqtopt.peresdg() %q0_9, %q1_1 : !mqtopt.Qubit, !mqtopt.Qubit + + // Controlled H/V/Vdg/S/Sdg/T/Tdg + %q0_11, %q1_3 = mqtopt.h() %q0_10 ctrl %q1_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_12, %q1_4 = mqtopt.v() %q0_11 ctrl %q1_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_13, %q1_5 = mqtopt.vdg() %q0_12 ctrl %q1_4 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_14, %q1_6 = mqtopt.s() %q0_13 ctrl %q1_5 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_15, %q1_7 = mqtopt.sdg() %q0_14 ctrl %q1_6 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_16, %q1_8, %q2_1 = mqtopt.peres() %q0_15, %q1_7 ctrl %q2_0 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_17, %q1_9, %q2_2 = mqtopt.peresdg() %q0_16, %q1_8 ctrl %q2_1 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Release qubits + memref.store %q0_17, %r0_0[%i0] : memref<3x!mqtopt.Qubit> + memref.store %q1_9, %r0_0[%i1] : memref<3x!mqtopt.Qubit> + memref.store %q2_2, %r0_0[%i2] : memref<3x!mqtopt.Qubit> + memref.dealloc %r0_0 : memref<3x!mqtopt.Qubit> + return + } +} diff --git a/plugins/catalyst/test/Conversion/mqtopt_entangling.mlir b/plugins/catalyst/test/Conversion/mqtopt_entangling.mlir new file mode 100644 index 0000000000..35dcbf5114 --- /dev/null +++ b/plugins/catalyst/test/Conversion/mqtopt_entangling.mlir @@ -0,0 +1,88 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +// RUN: catalyst --tool=opt \ +// RUN: --load-pass-plugin=%mqt_plugin_path% \ +// RUN: --load-dialect-plugin=%mqt_plugin_path% \ +// RUN: --catalyst-pipeline="builtin.module(mqtopt-to-catalystquantum)" \ +// RUN: %s | FileCheck %s + + +// ============================================================================ +// Entangling gates (SWAP, ISWAP, ECR) and controlled variants +// Groups: Allocation & extraction / Uncontrolled / Controlled / Reinsertion +// ============================================================================ +module { + // CHECK-LABEL: func.func @testMQTOptToCatalystQuantumEntanglingGates + func.func @testMQTOptToCatalystQuantumEntanglingGates() { + // --- Allocation & extraction --------------------------------------------------------------- + // CHECK: %[[C0:.*]] = arith.constant 0 : index + // CHECK: %[[C1:.*]] = arith.constant 1 : index + // CHECK: %[[C2:.*]] = arith.constant 2 : index + // CHECK: %[[C3_I64:.*]] = arith.constant 3 : i64 + // CHECK: %[[QREG:.*]] = quantum.alloc(%[[C3_I64]]) : !quantum.reg + // CHECK: %[[IDX0:.*]] = arith.index_cast %[[C0]] : index to i64 + // CHECK: %[[Q0:.*]] = quantum.extract %[[QREG]][%[[IDX0]]] : !quantum.reg -> !quantum.bit + // CHECK: %[[IDX1:.*]] = arith.index_cast %[[C1]] : index to i64 + // CHECK: %[[Q1:.*]] = quantum.extract %[[QREG]][%[[IDX1]]] : !quantum.reg -> !quantum.bit + // CHECK: %[[IDX2:.*]] = arith.index_cast %[[C2]] : index to i64 + // CHECK: %[[Q2:.*]] = quantum.extract %[[QREG]][%[[IDX2]]] : !quantum.reg -> !quantum.bit + + // --- Uncontrolled ------------------------------------------------------------------------- + // CHECK: %[[SW0:.*]]:2 = quantum.custom "SWAP"() %[[Q0]], %[[Q1]] : !quantum.bit, !quantum.bit + // CHECK: %[[IS0:.*]]:2 = quantum.custom "ISWAP"() %[[SW0]]#0, %[[SW0]]#1 : !quantum.bit, !quantum.bit + // CHECK: %[[ISD0:.*]]:2 = quantum.custom "ISWAP"() %[[IS0]]#0, %[[IS0]]#1 adj : !quantum.bit, !quantum.bit + // CHECK: %[[ECR0:.*]]:2 = quantum.custom "ECR"() %[[ISD0]]#0, %[[ISD0]]#1 : !quantum.bit, !quantum.bit + // CHECK: %[[DCX0:.*]]:2 = quantum.custom "CNOT"() %[[ECR0]]#0, %[[ECR0]]#1 : !quantum.bit, !quantum.bit + // CHECK: %[[DCX1:.*]]:2 = quantum.custom "CNOT"() %[[DCX0]]#1, %[[DCX0]]#0 : !quantum.bit, !quantum.bit + + // --- Controlled ---------------------------------------------------------------------------- + // CHECK: %[[CSW_T:.*]]:2, %[[CSW_C:.*]] = quantum.custom "CSWAP"() %[[DCX1]]#0, %[[DCX1]]#1 ctrls(%[[Q2]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[CISW_T:.*]]:2, %[[CISW_C:.*]] = quantum.custom "ISWAP"() %[[CSW_T]]#0, %[[CSW_T]]#1 ctrls(%[[CSW_C]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[CISWD_T:.*]]:2, %[[CISWD_C:.*]] = quantum.custom "ISWAP"() %[[CISW_T]]#0, %[[CISW_T]]#1 adj ctrls(%[[CISW_C]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[CECR_T:.*]]:2, %[[CECR_C:.*]] = quantum.custom "ECR"() %[[CISWD_T]]#0, %[[CISWD_T]]#1 ctrls(%[[CISWD_C]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[CDCX1_T:.*]]:2, %[[CDCX1_C:.*]] = quantum.custom "CNOT"() %[[CECR_T]]#0, %[[CECR_T]]#1 ctrls(%[[CECR_C]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[CDCX2_T:.*]]:2, %[[CDCX2_C:.*]] = quantum.custom "CNOT"() %[[CDCX1_T]]#1, %[[CDCX1_T]]#0 ctrls(%[[CDCX1_C]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + + // --- Reinsertion --------------------------------------------------------------------------- + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[CDCX2_T]]#0 : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[CDCX2_T]]#1 : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[CDCX2_C]] : !quantum.reg, !quantum.bit + // CHECK: quantum.dealloc %[[QREG]] : !quantum.reg + + // Prepare qubits + %i0 = arith.constant 0 : index + %i1 = arith.constant 1 : index + %i2 = arith.constant 2 : index + %r0_0 = memref.alloc() : memref<3x!mqtopt.Qubit> + %q0_0 = memref.load %r0_0[%i0] : memref<3x!mqtopt.Qubit> + %q1_0 = memref.load %r0_0[%i1] : memref<3x!mqtopt.Qubit> + %q2_0 = memref.load %r0_0[%i2] : memref<3x!mqtopt.Qubit> + + // Uncontrolled + %q0_1, %q1_1 = mqtopt.swap() %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit + %q0_2, %q1_2 = mqtopt.iswap() %q0_1, %q1_1 : !mqtopt.Qubit, !mqtopt.Qubit + %q0_3, %q1_3 = mqtopt.iswapdg() %q0_2, %q1_2 : !mqtopt.Qubit, !mqtopt.Qubit + %q0_4, %q1_4 = mqtopt.ecr() %q0_3, %q1_3 : !mqtopt.Qubit, !mqtopt.Qubit + %q0_5, %q1_5 = mqtopt.dcx() %q0_4, %q1_4 : !mqtopt.Qubit, !mqtopt.Qubit + + // Controlled + %q0_6, %q1_6, %q2_1 = mqtopt.swap() %q0_5, %q1_5 ctrl %q2_0 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_7, %q1_7, %q2_2 = mqtopt.iswap() %q0_6, %q1_6 ctrl %q2_1 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_8, %q1_8, %q2_3 = mqtopt.iswapdg() %q0_7, %q1_7 ctrl %q2_2 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_9, %q1_9, %q2_4 = mqtopt.ecr() %q0_8, %q1_8 ctrl %q2_3 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_10, %q1_10, %q2_5 = mqtopt.dcx() %q0_9, %q1_9 ctrl %q2_4 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Release qubits + memref.store %q0_10, %r0_0[%i0] : memref<3x!mqtopt.Qubit> + memref.store %q1_10, %r0_0[%i1] : memref<3x!mqtopt.Qubit> + memref.store %q2_5, %r0_0[%i2] : memref<3x!mqtopt.Qubit> + memref.dealloc %r0_0 : memref<3x!mqtopt.Qubit> + return + } +} diff --git a/plugins/catalyst/test/Conversion/mqtopt_ising.mlir b/plugins/catalyst/test/Conversion/mqtopt_ising.mlir new file mode 100644 index 0000000000..7d6128eee2 --- /dev/null +++ b/plugins/catalyst/test/Conversion/mqtopt_ising.mlir @@ -0,0 +1,113 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +// RUN: catalyst --tool=opt \ +// RUN: --load-pass-plugin=%mqt_plugin_path% \ +// RUN: --load-dialect-plugin=%mqt_plugin_path% \ +// RUN: --catalyst-pipeline="builtin.module(mqtopt-to-catalystquantum)" \ +// RUN: %s | FileCheck %s + + +// ============================================================================ +// Ising-type gates and controlled variants +// Groups: Allocation & extraction / Uncontrolled / Controlled / Reinsertion +// ============================================================================ +module { + // CHECK-LABEL: func.func @testMQTOptToCatalystQuantumIsingGates + func.func @testMQTOptToCatalystQuantumIsingGates() { + // --- Allocation & extraction --------------------------------------------------------------- + // CHECK: %[[C0:.*]] = arith.constant 0 : index + // CHECK: %[[C1:.*]] = arith.constant 1 : index + // CHECK: %[[C2:.*]] = arith.constant 2 : index + // CHECK: %[[C3_I64:.*]] = arith.constant 3 : i64 + // CHECK: %[[QREG:.*]] = quantum.alloc(%[[C3_I64]]) : !quantum.reg + // CHECK: %[[IDX0:.*]] = arith.index_cast %[[C0]] : index to i64 + // CHECK: %[[Q0:.*]] = quantum.extract %[[QREG]][%[[IDX0]]] : !quantum.reg -> !quantum.bit + // CHECK: %[[IDX1:.*]] = arith.index_cast %[[C1]] : index to i64 + // CHECK: %[[Q1:.*]] = quantum.extract %[[QREG]][%[[IDX1]]] : !quantum.reg -> !quantum.bit + // CHECK: %[[IDX2:.*]] = arith.index_cast %[[C2]] : index to i64 + // CHECK: %[[Q2:.*]] = quantum.extract %[[QREG]][%[[IDX2]]] : !quantum.reg -> !quantum.bit + + // --- Uncontrolled ------------------------------------------------------------------- + // CHECK: %[[RZ0:.*]] = quantum.custom "RZ"(%{{.*}}) %[[Q1]] : !quantum.bit + // CHECK: %[[XY_P:.*]]:2 = quantum.custom "IsingXY"(%cst) %[[Q0]], %[[RZ0]] : !quantum.bit, !quantum.bit + // CHECK: %[[RZ1:.*]] = quantum.custom "RZ"(%{{.*}}) %[[XY_P]]#1 : !quantum.bit + + // CHECK: %[[X1:.*]] = quantum.custom "X"() %[[XY_P]]#0 : !quantum.bit + // CHECK: %[[RZ2:.*]] = quantum.custom "RZ"(%{{.*}}) %[[RZ1]] : !quantum.bit + // CHECK: %[[XY_M:.*]]:2 = quantum.custom "IsingXY"(%cst) %[[X1]], %[[RZ2]] : !quantum.bit, !quantum.bit + // CHECK: %[[RZ3:.*]] = quantum.custom "RZ"(%{{.*}}) %[[XY_M]]#1 : !quantum.bit + // CHECK: %[[X2:.*]] = quantum.custom "X"() %[[XY_M]]#0 : !quantum.bit + + // CHECK: %[[XX_P:.*]]:2 = quantum.custom "IsingXX"(%cst) %[[X2]], %[[RZ3]] : !quantum.bit, !quantum.bit + // CHECK: %[[YY_P:.*]]:2 = quantum.custom "IsingYY"(%cst) %[[XX_P]]#0, %[[XX_P]]#1 : !quantum.bit, !quantum.bit + // CHECK: %[[ZZ_P1:.*]]:2 = quantum.custom "IsingZZ"(%cst) %[[YY_P]]#0, %[[YY_P]]#1 : !quantum.bit, !quantum.bit + + // CHECK: %[[H1:.*]] = quantum.custom "Hadamard"() %[[ZZ_P1]]#1 : !quantum.bit + // CHECK: %[[ZZ_P2:.*]]:2 = quantum.custom "IsingZZ"(%cst) %[[ZZ_P1]]#0, %[[H1]] : !quantum.bit, !quantum.bit + // CHECK: %[[H2:.*]] = quantum.custom "Hadamard"() %[[ZZ_P2]]#1 : !quantum.bit + + // --- Controlled --------------------------------------------------------------------- + // CHECK: %[[RZ2:.*]], %[[CTRL1A:.*]] = quantum.custom "RZ"(%{{.*}}) %[[H2]] ctrls(%[[Q2]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[XY_C:.*]]:2, %[[CTRL1B:.*]] = quantum.custom "IsingXY"(%cst) %[[ZZ_P2]]#0, %[[RZ2]] ctrls(%[[CTRL1A]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[RZ3:.*]], %[[CTRL1:.*]] = quantum.custom "RZ"(%{{.*}}) %[[XY_C]]#1 ctrls(%[[CTRL1B]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // CHECK: %[[X1C:.*]], %[[CTRL2A:.*]] = quantum.custom "X"() %[[XY_C]]#0 ctrls(%[[CTRL1]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[RZ4:.*]], %[[CTRL2B:.*]] = quantum.custom "RZ"(%{{.*}}) %[[RZ3]] ctrls(%[[CTRL2A]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[XY_CM:.*]]:2, %[[CTRL2C:.*]] = quantum.custom "IsingXY"(%cst) %[[X1C]], %[[RZ4]] ctrls(%[[CTRL2B]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[RZ5:.*]], %[[CTRL2D:.*]] = quantum.custom "RZ"(%{{.*}}) %[[XY_CM]]#1 ctrls(%[[CTRL2C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[X2C:.*]], %[[CTRL2:.*]] = quantum.custom "X"() %[[XY_CM]]#0 ctrls(%[[CTRL2D]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // CHECK: %[[XX_C:.*]]:2, %[[CTRL3:.*]] = quantum.custom "IsingXX"(%cst) %[[X2C]], %[[RZ5]] ctrls(%[[CTRL2]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[YY_C:.*]]:2, %[[CTRL4:.*]] = quantum.custom "IsingYY"(%cst) %[[XX_C]]#0, %[[XX_C]]#1 ctrls(%[[CTRL3]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[ZZ_C1:.*]]:2, %[[CTRL5:.*]] = quantum.custom "IsingZZ"(%cst) %[[YY_C]]#0, %[[YY_C]]#1 ctrls(%[[CTRL4]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + + // CHECK: %[[H1:.*]], %[[CTRL6:.*]] = quantum.custom "Hadamard"() %[[ZZ_C1]]#1 ctrls(%[[CTRL5]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CZZ_P2:.*]]:2, %[[CTRL7:.*]] = quantum.custom "IsingZZ"(%cst) %[[ZZ_C1]]#0, %[[H1]] ctrls(%[[CTRL6]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[H2:.*]], %[[CTRL8:.*]] = quantum.custom "Hadamard"() %[[CZZ_P2]]#1 ctrls(%[[CTRL7]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // --- Reinsertion --------------------------------------------------------------------------- + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[CZZ_P2]]#0 : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[H2]] : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[CTRL8]] : !quantum.reg, !quantum.bit + // CHECK: quantum.dealloc %[[QREG]] : !quantum.reg + + // Prepare qubits + %i0 = arith.constant 0 : index + %i1 = arith.constant 1 : index + %i2 = arith.constant 2 : index + %r0_0 = memref.alloc() : memref<3x!mqtopt.Qubit> + %q0_0 = memref.load %r0_0[%i0] : memref<3x!mqtopt.Qubit> + %q1_0 = memref.load %r0_0[%i1] : memref<3x!mqtopt.Qubit> + %q2_0 = memref.load %r0_0[%i2] : memref<3x!mqtopt.Qubit> + + // Uncontrolled + %cst = arith.constant 3.000000e-01 : f64 + %q0_1, %q1_1 = mqtopt.xx_plus_yy(%cst, %cst) %q0_0, %q1_0 : !mqtopt.Qubit, !mqtopt.Qubit + %q0_2, %q1_2 = mqtopt.xx_minus_yy(%cst, %cst) %q0_1, %q1_1 : !mqtopt.Qubit, !mqtopt.Qubit + %q0_3, %q1_3 = mqtopt.rxx(%cst) %q0_2, %q1_2 : !mqtopt.Qubit, !mqtopt.Qubit + %q0_4, %q1_4 = mqtopt.ryy(%cst) %q0_3, %q1_3 : !mqtopt.Qubit, !mqtopt.Qubit + %q0_5, %q1_5 = mqtopt.rzz(%cst) %q0_4, %q1_4 : !mqtopt.Qubit, !mqtopt.Qubit + %q0_6, %q1_6 = mqtopt.rzx(%cst) %q0_5, %q1_5 : !mqtopt.Qubit, !mqtopt.Qubit + + // Controlled + %q0_7, %q1_7, %q2_1 = mqtopt.xx_plus_yy(%cst, %cst) %q0_6, %q1_6 ctrl %q2_0 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_8, %q1_8, %q2_2 = mqtopt.xx_minus_yy(%cst, %cst) %q0_7, %q1_7 ctrl %q2_1 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_9, %q1_9, %q2_3 = mqtopt.rxx(%cst) %q0_8, %q1_8 ctrl %q2_2 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_10, %q1_10, %q2_4 = mqtopt.ryy(%cst) %q0_9, %q1_9 ctrl %q2_3 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_11, %q1_11, %q2_5 = mqtopt.rzz(%cst) %q0_10, %q1_10 ctrl %q2_4 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_12, %q1_12, %q2_6 = mqtopt.rzx(%cst) %q0_11, %q1_11 ctrl %q2_5 : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Release qubits + memref.store %q0_12, %r0_0[%i0] : memref<3x!mqtopt.Qubit> + memref.store %q1_12, %r0_0[%i1] : memref<3x!mqtopt.Qubit> + memref.store %q2_6, %r0_0[%i2] : memref<3x!mqtopt.Qubit> + memref.dealloc %r0_0 : memref<3x!mqtopt.Qubit> + return + } +} diff --git a/plugins/catalyst/test/Conversion/mqtopt_param.mlir b/plugins/catalyst/test/Conversion/mqtopt_param.mlir new file mode 100644 index 0000000000..ab464fec5e --- /dev/null +++ b/plugins/catalyst/test/Conversion/mqtopt_param.mlir @@ -0,0 +1,78 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +// RUN: catalyst --tool=opt \ +// RUN: --load-pass-plugin=%mqt_plugin_path% \ +// RUN: --load-dialect-plugin=%mqt_plugin_path% \ +// RUN: --catalyst-pipeline="builtin.module(mqtopt-to-catalystquantum)" \ +// RUN: %s | FileCheck %s + + +// ============================================================================ +// Parameterized gates RX/RY/RZ, PhaseShift and controlled variants +// Groups: Allocation & extraction / Uncontrolled / Controlled / Reinsertion +// ============================================================================ +module { + // CHECK-LABEL: func.func @testMQTOptToCatalystQuantumParameterizedGates + func.func @testMQTOptToCatalystQuantumParameterizedGates() { + // --- Allocation & extraction --------------------------------------------------------------- + // CHECK: %[[C0:.*]] = arith.constant 0 : index + // CHECK: %[[C1:.*]] = arith.constant 1 : index + // CHECK: %[[C2_I64:.*]] = arith.constant 2 : i64 + // CHECK: %[[QREG:.*]] = quantum.alloc(%[[C2_I64]]) : !quantum.reg + // CHECK: %[[IDX0:.*]] = arith.index_cast %[[C0]] : index to i64 + // CHECK: %[[Q0:.*]] = quantum.extract %[[QREG]][%[[IDX0]]] : !quantum.reg -> !quantum.bit + // CHECK: %[[IDX1:.*]] = arith.index_cast %[[C1]] : index to i64 + // CHECK: %[[Q1:.*]] = quantum.extract %[[QREG]][%[[IDX1]]] : !quantum.reg -> !quantum.bit + + // --- Uncontrolled ------------------------------------------------------------------------- + // CHECK: %[[RX:.*]] = quantum.custom "RX"(%cst) %[[Q0]] : !quantum.bit + // CHECK: %[[RY:.*]] = quantum.custom "RY"(%cst) %[[RX]] : !quantum.bit + // CHECK: %[[RZ:.*]] = quantum.custom "RZ"(%cst) %[[RY]] : !quantum.bit + // CHECK: %[[PS:.*]] = quantum.custom "PhaseShift"(%cst) %[[RZ]] : !quantum.bit + // CHECK: quantum.gphase(%cst) : + + // --- Controlled ---------------------------------------------------------------------------- + // CHECK: %[[CRX_T:.*]], %[[CRX_C:.*]] = quantum.custom "CRX"(%cst) %[[PS]] ctrls(%[[Q1]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CRY_T:.*]], %[[CRY_C:.*]] = quantum.custom "CRY"(%cst) %[[CRX_T]] ctrls(%[[CRX_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CRZ_T:.*]], %[[CRZ_C:.*]] = quantum.custom "CRZ"(%cst) %[[CRY_T]] ctrls(%[[CRY_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CPS_T:.*]], %[[CPS_C:.*]] = quantum.custom "ControlledPhaseShift"(%cst) %[[CRZ_T]] ctrls(%[[CRZ_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // --- Reinsertion --------------------------------------------------------------------------- + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[CPS_T]] : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[CPS_C]] : !quantum.reg, !quantum.bit + // CHECK: quantum.dealloc %[[QREG]] : !quantum.reg + + // Prepare qubits + %cst = arith.constant 3.000000e-01 : f64 + %i0 = arith.constant 0 : index + %i1 = arith.constant 1 : index + %r0_0 = memref.alloc() : memref<2x!mqtopt.Qubit> + %q0_0 = memref.load %r0_0[%i0] : memref<2x!mqtopt.Qubit> + %q1_0 = memref.load %r0_0[%i1] : memref<2x!mqtopt.Qubit> + + // Non-controlled rotations + %q0_1 = mqtopt.rx(%cst) %q0_0 : !mqtopt.Qubit + %q0_2 = mqtopt.ry(%cst) %q0_1 : !mqtopt.Qubit + %q0_3 = mqtopt.rz(%cst) %q0_2 : !mqtopt.Qubit + %q0_4 = mqtopt.p(%cst) %q0_3 : !mqtopt.Qubit + mqtopt.gphase(%cst) : () + + // Controlled rotations + %q0_5, %q1_1 = mqtopt.rx(%cst) %q0_4 ctrl %q1_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_6, %q1_2 = mqtopt.ry(%cst) %q0_5 ctrl %q1_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_7, %q1_3 = mqtopt.rz(%cst) %q0_6 ctrl %q1_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_8, %q1_4 = mqtopt.p(%cst) %q0_7 ctrl %q1_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Release qubits + memref.store %q0_8, %r0_0[%i0] : memref<2x!mqtopt.Qubit> + memref.store %q1_4, %r0_0[%i1] : memref<2x!mqtopt.Qubit> + memref.dealloc %r0_0 : memref<2x!mqtopt.Qubit> + return + } +} diff --git a/plugins/catalyst/test/Conversion/mqtopt_pauli.mlir b/plugins/catalyst/test/Conversion/mqtopt_pauli.mlir new file mode 100644 index 0000000000..05401fd085 --- /dev/null +++ b/plugins/catalyst/test/Conversion/mqtopt_pauli.mlir @@ -0,0 +1,82 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +// RUN: catalyst --tool=opt \ +// RUN: --load-pass-plugin=%mqt_plugin_path% \ +// RUN: --load-dialect-plugin=%mqt_plugin_path% \ +// RUN: --catalyst-pipeline="builtin.module(mqtopt-to-catalystquantum)" \ +// RUN: %s | FileCheck %s + + +// ============================================================================ +// Pauli family (X, Y, Z) and controlled variants +// Groups: Allocation & extraction / Uncontrolled / Controlled / Reinsertion +// ============================================================================ +module { + // CHECK-LABEL: func.func @testMQTOptToCatalystQuantumPauliGates + func.func @testMQTOptToCatalystQuantumPauliGates() { + // --- Allocation & extraction --------------------------------------------------------------- + // CHECK: %[[C0:.*]] = arith.constant 0 : index + // CHECK: %[[C1:.*]] = arith.constant 1 : index + // CHECK: %[[C2:.*]] = arith.constant 2 : index + // CHECK: %[[C3_I64:.*]] = arith.constant 3 : i64 + // CHECK: %[[QREG:.*]] = quantum.alloc(%[[C3_I64]]) : !quantum.reg + // CHECK: %[[IDX0:.*]] = arith.index_cast %[[C0]] : index to i64 + // CHECK: %[[Q0:.*]] = quantum.extract %[[QREG]][%[[IDX0]]] : !quantum.reg -> !quantum.bit + // CHECK: %[[IDX1:.*]] = arith.index_cast %[[C1]] : index to i64 + // CHECK: %[[Q1:.*]] = quantum.extract %[[QREG]][%[[IDX1]]] : !quantum.reg -> !quantum.bit + // CHECK: %[[IDX2:.*]] = arith.index_cast %[[C2]] : index to i64 + // CHECK: %[[Q2:.*]] = quantum.extract %[[QREG]][%[[IDX2]]] : !quantum.reg -> !quantum.bit + + // --- Uncontrolled Pauli gates -------------------------------------------------------------- + // CHECK: %[[X:.*]] = quantum.custom "PauliX"() %[[Q0]] : !quantum.bit + // CHECK: %[[Y:.*]] = quantum.custom "PauliY"() %[[X]] : !quantum.bit + // CHECK: %[[Z:.*]] = quantum.custom "PauliZ"() %[[Y]] : !quantum.bit + // CHECK: %[[I:.*]] = quantum.custom "Identity"() %[[Z]] : !quantum.bit + + // CHECK: %[[CNOT_T:.*]], %[[CNOT_C:.*]] = quantum.custom "CNOT"() %[[I]] ctrls(%[[Q1]]) ctrlvals(%true) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CY_T:.*]], %[[CY_C:.*]] = quantum.custom "CY"() %[[CNOT_T]] ctrls(%[[CNOT_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CZ_T:.*]], %[[CZ_C:.*]] = quantum.custom "CZ"() %[[CY_T]] ctrls(%[[CY_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[I_T:.*]], %[[I_C:.*]] = quantum.custom "Identity"() %[[CZ_T]] ctrls(%[[CZ_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[TOF_T:.*]], %[[TOF_C:.*]]:2 = quantum.custom "Toffoli"() %[[I_T]] ctrls(%[[I_C]], %[[Q2]]) ctrlvals(%true{{.*}}, %true{{.*}}) : !quantum.bit ctrls !quantum.bit, !quantum.bit + + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[TOF_T]] : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[TOF_C]]#0 : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[TOF_C]]#1 : !quantum.reg, !quantum.bit + // CHECK: quantum.dealloc %[[QREG]] : !quantum.reg + + // Prepare qubits + %i0 = arith.constant 0 : index + %i1 = arith.constant 1 : index + %i2 = arith.constant 2 : index + %r0_0 = memref.alloc() : memref<3x!mqtopt.Qubit> + %q0_0 = memref.load %r0_0[%i0] : memref<3x!mqtopt.Qubit> + %q1_0 = memref.load %r0_0[%i1] : memref<3x!mqtopt.Qubit> + %q2_0 = memref.load %r0_0[%i2] : memref<3x!mqtopt.Qubit> + + // Non-controlled Pauli gates + %q0_1 = mqtopt.x() %q0_0 : !mqtopt.Qubit + %q0_2 = mqtopt.y() %q0_1 : !mqtopt.Qubit + %q0_3 = mqtopt.z() %q0_2 : !mqtopt.Qubit + %q0_4 = mqtopt.i() %q0_3 : !mqtopt.Qubit + + // Controlled Pauli gates + %q0_5, %q1_1 = mqtopt.x() %q0_4 ctrl %q1_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_6, %q1_2 = mqtopt.y() %q0_5 ctrl %q1_1 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_7, %q1_3 = mqtopt.z() %q0_6 ctrl %q1_2 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_8, %q1_4 = mqtopt.i() %q0_7 ctrl %q1_3 : !mqtopt.Qubit ctrl !mqtopt.Qubit + %q0_9, %q1_5, %q2_1 = mqtopt.x() %q0_8 ctrl %q1_4, %q2_0 : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit + + // Release qubits + memref.store %q0_9, %r0_0[%i0] : memref<3x!mqtopt.Qubit> + memref.store %q1_5, %r0_0[%i1] : memref<3x!mqtopt.Qubit> + memref.store %q2_1, %r0_0[%i2] : memref<3x!mqtopt.Qubit> + memref.dealloc %r0_0 : memref<3x!mqtopt.Qubit> + return + } +} diff --git a/plugins/catalyst/test/Conversion/quantum_clifford.mlir b/plugins/catalyst/test/Conversion/quantum_clifford.mlir new file mode 100644 index 0000000000..6e3426568d --- /dev/null +++ b/plugins/catalyst/test/Conversion/quantum_clifford.mlir @@ -0,0 +1,85 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +// RUN: catalyst --tool=opt \ +// RUN: --load-pass-plugin=%mqt_plugin_path% \ +// RUN: --load-dialect-plugin=%mqt_plugin_path% \ +// RUN: --catalyst-pipeline="builtin.module(catalystquantum-to-mqtopt)" \ +// RUN: %s | FileCheck %s + + +// ============================================================================ +// Clifford + T and controlled variants +// Groups: Allocation & extraction / Uncontrolled / Controlled / Reinsertion +// ============================================================================ +module { + // CHECK-LABEL: func.func @testCatalystQuantumToMQTOptCliffordT + func.func @testCatalystQuantumToMQTOptCliffordT() { + // --- Allocation & extraction --------------------------------------------------------------- + // CHECK: %[[ALLOC:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> + // CHECK: %[[C0:.*]] = arith.constant 0 : index + // CHECK: %[[Q0:.*]] = memref.load %[[ALLOC]][%[[C0]]] : memref<2x!mqtopt.Qubit> + // CHECK: %[[C1:.*]] = arith.constant 1 : index + // CHECK: %[[Q1:.*]] = memref.load %[[ALLOC]][%[[C1]]] : memref<2x!mqtopt.Qubit> + + // --- Uncontrolled Clifford+T gates --------------------------------------------------------- + // CHECK: %[[H:.*]] = mqtopt.h(static [] mask []) %[[Q0]] : !mqtopt.Qubit + // CHECK: %[[V:.*]] = mqtopt.sx(static [] mask []) %[[H]] : !mqtopt.Qubit + // CHECK: %[[VDG:.*]] = mqtopt.sx(static [] mask []) %[[V]] : !mqtopt.Qubit + // CHECK: %[[S:.*]] = mqtopt.s(static [] mask []) %[[VDG]] : !mqtopt.Qubit + // CHECK: %[[SDG:.*]] = mqtopt.s(static [] mask []) %[[S]] : !mqtopt.Qubit + // CHECK: %[[T:.*]] = mqtopt.t(static [] mask []) %[[SDG]] : !mqtopt.Qubit + // CHECK: %[[TDG:.*]] = mqtopt.t(static [] mask []) %[[T]] : !mqtopt.Qubit + + // --- Controlled Clifford+T gates ----------------------------------------------------------- + // CHECK: %[[CH_T:.*]], %[[CH_C:.*]] = mqtopt.h(static [] mask []) %[[TDG]] ctrl %[[Q1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CV_T:.*]], %[[CV_C:.*]] = mqtopt.sx(static [] mask []) %[[CH_T]] ctrl %[[CH_C]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CVDG_T:.*]], %[[CVDG_C:.*]] = mqtopt.sx(static [] mask []) %[[CV_T]] ctrl %[[CV_C]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CS_T:.*]], %[[CS_C:.*]] = mqtopt.s(static [] mask []) %[[CVDG_T]] ctrl %[[CVDG_C]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CSDG_T:.*]], %[[CSDG_C:.*]] = mqtopt.s(static [] mask []) %[[CS_T]] ctrl %[[CS_C]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CT_T:.*]], %[[CT_C:.*]] = mqtopt.t(static [] mask []) %[[CSDG_T]] ctrl %[[CSDG_C]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CTDG_T:.*]], %[[CTDG_C:.*]] = mqtopt.t(static [] mask []) %[[CT_T]] ctrl %[[CT_C]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + + // --- Reinsertion --------------------------------------------------------------------------- + // CHECK: %[[C0_FINAL:.*]] = arith.constant 0 : index + // CHECK: memref.store %[[CTDG_T]], %[[ALLOC]][%[[C0_FINAL]]] : memref<2x!mqtopt.Qubit> + // CHECK: %[[C1_FINAL:.*]] = arith.constant 1 : index + // CHECK: memref.store %[[CTDG_C]], %[[ALLOC]][%[[C1_FINAL]]] : memref<2x!mqtopt.Qubit> + // CHECK: memref.dealloc %[[ALLOC]] : memref<2x!mqtopt.Qubit> + + // Prepare qubits + %qreg = quantum.alloc( 2) : !quantum.reg + %q0 = quantum.extract %qreg[ 0] : !quantum.reg -> !quantum.bit + %q1 = quantum.extract %qreg[ 1] : !quantum.reg -> !quantum.bit + + // Non-controlled Clifford+T gates + %q0_h = quantum.custom "Hadamard"() %q0 : !quantum.bit + %q0_v = quantum.custom "SX"() %q0_h : !quantum.bit + %q0_vdg = quantum.custom "SX"() %q0_v {adjoint} : !quantum.bit + %q0_s = quantum.custom "S"() %q0_vdg : !quantum.bit + %q0_sdg = quantum.custom "S"() %q0_s {adjoint} : !quantum.bit + %q0_t = quantum.custom "T"() %q0_sdg : !quantum.bit + %q0_tdg = quantum.custom "T"() %q0_t {adjoint} : !quantum.bit + + // Controlled Clifford+T gates + %true = arith.constant true + %q0_ch, %q1_ch = quantum.custom "Hadamard"() %q0_tdg ctrls(%q1) ctrlvals(%true) : !quantum.bit ctrls !quantum.bit + %q0_cv, %q1_cv = quantum.custom "SX"() %q0_ch ctrls(%q1_ch) ctrlvals(%true) : !quantum.bit ctrls !quantum.bit + %q0_cvdg, %q1_cvdg = quantum.custom "SX"() %q0_cv {adjoint} ctrls(%q1_cv) ctrlvals(%true) : !quantum.bit ctrls !quantum.bit + %q0_cs, %q1_cs = quantum.custom "S"() %q0_cvdg ctrls(%q1_cvdg) ctrlvals(%true) : !quantum.bit ctrls !quantum.bit + %q0_csdg, %q1_csdg = quantum.custom "S"() %q0_cs {adjoint} ctrls(%q1_cs) ctrlvals(%true) : !quantum.bit ctrls !quantum.bit + %q0_ct, %q1_ct = quantum.custom "T"() %q0_csdg ctrls(%q1_csdg) ctrlvals(%true) : !quantum.bit ctrls !quantum.bit + %q0_ctdg, %q1_ctdg = quantum.custom "T"() %q0_ct {adjoint} ctrls(%q1_ct) ctrlvals(%true) : !quantum.bit ctrls !quantum.bit + + // Release qubits + %qreg1 = quantum.insert %qreg[ 0], %q0_ctdg : !quantum.reg, !quantum.bit + %qreg2 = quantum.insert %qreg1[ 1], %q1_ctdg : !quantum.reg, !quantum.bit + quantum.dealloc %qreg2 : !quantum.reg + return + } +} diff --git a/plugins/catalyst/test/Conversion/quantum_entangling.mlir b/plugins/catalyst/test/Conversion/quantum_entangling.mlir new file mode 100644 index 0000000000..7592385ab1 --- /dev/null +++ b/plugins/catalyst/test/Conversion/quantum_entangling.mlir @@ -0,0 +1,79 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +// RUN: catalyst --tool=opt \ +// RUN: --load-pass-plugin=%mqt_plugin_path% \ +// RUN: --load-dialect-plugin=%mqt_plugin_path% \ +// RUN: --catalyst-pipeline="builtin.module(catalystquantum-to-mqtopt)" \ +// RUN: %s | FileCheck %s + + +// ============================================================================ +// Entangling gates (SWAP, ISWAP, ECR) and controlled variants +// Groups: Allocation & extraction / Uncontrolled / Controlled / Reinsertion +// ============================================================================ +module { + // CHECK-LABEL: func.func @testCatalystQuantumToMQTOptEntanglingGates + func.func @testCatalystQuantumToMQTOptEntanglingGates() { + // --- Allocation & extraction --------------------------------------------------------------- + // CHECK: %[[ALLOC:.*]] = memref.alloc() : memref<3x!mqtopt.Qubit> + // CHECK: %[[C0:.*]] = arith.constant 0 : index + // CHECK: %[[Q0:.*]] = memref.load %[[ALLOC]][%[[C0]]] : memref<3x!mqtopt.Qubit> + // CHECK: %[[C1:.*]] = arith.constant 1 : index + // CHECK: %[[Q1:.*]] = memref.load %[[ALLOC]][%[[C1]]] : memref<3x!mqtopt.Qubit> + // CHECK: %[[C2:.*]] = arith.constant 2 : index + // CHECK: %[[Q2:.*]] = memref.load %[[ALLOC]][%[[C2]]] : memref<3x!mqtopt.Qubit> + + // --- Uncontrolled entangling gates --------------------------------------------------------- + // CHECK: %[[SW:.*]]:2 = mqtopt.swap(static [] mask []) %[[Q0]], %[[Q1]] : !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[IS:.*]]:2 = mqtopt.iswap(static [] mask []) %[[SW]]#0, %[[SW]]#1 : !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[ISD:.*]]:2 = mqtopt.iswapdg(static [] mask []) %[[IS]]#0, %[[IS]]#1 : !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[ECR:.*]]:2 = mqtopt.ecr(static [] mask []) %[[Q0]], %[[Q1]] : !mqtopt.Qubit, !mqtopt.Qubit + + // --- Controlled entangling gates ----------------------------------------------------------- + // CHECK: %[[CSW_T:.*]]:2, %[[CSW_C:.*]] = mqtopt.swap(static [] mask []) %[[ECR]]#0, %[[ECR]]#1 ctrl %[[Q2]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CISW_T:.*]]:2, %[[CISW_C:.*]] = mqtopt.iswap(static [] mask []) %[[CSW_T]]#0, %[[CSW_T]]#1 ctrl %[[CSW_C]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CISWD_T:.*]]:2, %[[CISWD_C:.*]] = mqtopt.iswapdg(static [] mask []) %[[CISW_T]]#0, %[[CISW_T]]#1 ctrl %[[CISW_C]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CECR_T:.*]]:2, %[[CECR_C:.*]] = mqtopt.ecr(static [] mask []) %[[CISWD_T]]#0, %[[CISWD_T]]#1 ctrl %[[CISWD_C]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + + // --- Reinsertion --------------------------------------------------------------------------- + // CHECK: %[[C0_FINAL:.*]] = arith.constant 0 : index + // CHECK: memref.store %[[CECR_T]]#0, %[[ALLOC]][%[[C0_FINAL]]] : memref<3x!mqtopt.Qubit> + // CHECK: %[[C1_FINAL:.*]] = arith.constant 1 : index + // CHECK: memref.store %[[CECR_T]]#1, %[[ALLOC]][%[[C1_FINAL]]] : memref<3x!mqtopt.Qubit> + // CHECK: %[[C2_FINAL:.*]] = arith.constant 2 : index + // CHECK: memref.store %[[CECR_C]], %[[ALLOC]][%[[C2_FINAL]]] : memref<3x!mqtopt.Qubit> + // CHECK: memref.dealloc %[[ALLOC]] : memref<3x!mqtopt.Qubit> + + // Prepare qubits + %qreg = quantum.alloc( 3) : !quantum.reg + %q0 = quantum.extract %qreg[ 0] : !quantum.reg -> !quantum.bit + %q1 = quantum.extract %qreg[ 1] : !quantum.reg -> !quantum.bit + %q2 = quantum.extract %qreg[ 2] : !quantum.reg -> !quantum.bit + + // Uncontrolled permutation gates + %q0_sw, %q1_sw = quantum.custom "SWAP"() %q0, %q1 : !quantum.bit, !quantum.bit + %q0_is, %q1_is = quantum.custom "ISWAP"() %q0_sw, %q1_sw : !quantum.bit, !quantum.bit + %q0_isd, %q1_isd = quantum.custom "ISWAP"() %q0_is, %q1_is adj : !quantum.bit, !quantum.bit + %q0_ecr, %q1_ecr = quantum.custom "ECR"() %q0, %q1 : !quantum.bit, !quantum.bit + + // Controlled permutation gates + %true = arith.constant true + %q0_csw, %q1_csw, %q2_csw = quantum.custom "SWAP"() %q0_ecr, %q1_ecr ctrls(%q2) ctrlvals(%true) : !quantum.bit, !quantum.bit ctrls !quantum.bit + %q0_cis, %q1_cis, %q2_cis = quantum.custom "ISWAP"() %q0_csw, %q1_csw ctrls(%q2_csw) ctrlvals(%true) : !quantum.bit, !quantum.bit ctrls !quantum.bit + %q0_cisd, %q1_cisd, %q2_cisd = quantum.custom "ISWAP"() %q0_cis, %q1_cis adj ctrls(%q2_cis) ctrlvals(%true) : !quantum.bit, !quantum.bit ctrls !quantum.bit + %q0_cecr, %q1_cecr, %q2_cecr = quantum.custom "ECR"() %q0_cisd, %q1_cisd ctrls(%q2_cisd) ctrlvals(%true) : !quantum.bit, !quantum.bit ctrls !quantum.bit + + // Release qubits + %qreg1 = quantum.insert %qreg[ 0], %q0_cecr : !quantum.reg, !quantum.bit + %qreg2 = quantum.insert %qreg1[ 1], %q1_cecr : !quantum.reg, !quantum.bit + %qreg3 = quantum.insert %qreg2[ 2], %q2_cecr : !quantum.reg, !quantum.bit + quantum.dealloc %qreg3 : !quantum.reg + return + } +} diff --git a/plugins/catalyst/test/Conversion/quantum_ising.mlir b/plugins/catalyst/test/Conversion/quantum_ising.mlir new file mode 100644 index 0000000000..f1b01594e8 --- /dev/null +++ b/plugins/catalyst/test/Conversion/quantum_ising.mlir @@ -0,0 +1,84 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +// RUN: catalyst --tool=opt \ +// RUN: --load-pass-plugin=%mqt_plugin_path% \ +// RUN: --load-dialect-plugin=%mqt_plugin_path% \ +// RUN: --catalyst-pipeline="builtin.module(catalystquantum-to-mqtopt)" \ +// RUN: %s | FileCheck %s + + +// ============================================================================ +// Ising-type gates and controlled variants +// Groups: Allocation & extraction / Uncontrolled / Controlled / Reinsertion +// ============================================================================ +module { + // CHECK-LABEL: func.func @testCatalystQuantumToMQTOptIsingGates + func.func @testCatalystQuantumToMQTOptIsingGates() { + // --- Allocation & extraction --------------------------------------------------------------- + // CHECK: %cst = arith.constant 3.000000e-01 : f64 + // CHECK: %[[ALLOC:.*]] = memref.alloc() : memref<3x!mqtopt.Qubit> + // CHECK: %[[C0:.*]] = arith.constant 0 : index + // CHECK: %[[Q0:.*]] = memref.load %[[ALLOC]][%[[C0]]] : memref<3x!mqtopt.Qubit> + // CHECK: %[[C1:.*]] = arith.constant 1 : index + // CHECK: %[[Q1:.*]] = memref.load %[[ALLOC]][%[[C1]]] : memref<3x!mqtopt.Qubit> + // CHECK: %[[C2:.*]] = arith.constant 2 : index + // CHECK: %[[Q2:.*]] = memref.load %[[ALLOC]][%[[C2]]] : memref<3x!mqtopt.Qubit> + + // --- Uncontrolled --------------------------------------------------------------------------- + // CHECK: %[[PI:.*]] = arith.constant 3.1415926535897931 : f64 + // CHECK: %[[XY:.*]]:2 = mqtopt.xx_plus_yy(%cst, %[[PI:.*]] static [] mask [false, false]) %[[Q0]], %[[Q1]] : !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[XX:.*]]:2 = mqtopt.rxx(%cst static [] mask [false]) %[[XY]]#0, %[[XY]]#1 : !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[YY:.*]]:2 = mqtopt.ryy(%cst static [] mask [false]) %[[XX]]#0, %[[XX]]#1 : !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[ZZ:.*]]:2 = mqtopt.rzz(%cst static [] mask [false]) %[[YY]]#0, %[[YY]]#1 : !mqtopt.Qubit, !mqtopt.Qubit + + // --- Controlled ----------------------------------------------------------------------------- + // CHECK: %[[PI:.*]] = arith.constant 3.1415926535897931 : f64 + // CHECK: %[[CXY_T:.*]]:2, %[[CXY_C:.*]] = mqtopt.xx_plus_yy(%cst, %[[PI:.*]] static [] mask [false, false]) %[[ZZ]]#0, %[[ZZ]]#1 ctrl %[[Q2]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CXX_T:.*]]:2, %[[CXX_C:.*]] = mqtopt.rxx(%cst static [] mask [false]) %[[CXY_T]]#0, %[[CXY_T]]#1 ctrl %[[CXY_C]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CYY_T:.*]]:2, %[[CYY_C:.*]] = mqtopt.ryy(%cst static [] mask [false]) %[[CXX_T]]#0, %[[CXX_T]]#1 ctrl %[[CXX_C]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CZZ_T:.*]]:2, %[[CZZ_C:.*]] = mqtopt.rzz(%cst static [] mask [false]) %[[CYY_T]]#0, %[[CYY_T]]#1 ctrl %[[CYY_C]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + + // --- Reinsertion --------------------------------------------------------------------------- + // CHECK: %[[C0_FINAL:.*]] = arith.constant 0 : index + // CHECK: memref.store %[[CZZ_T]]#0, %[[ALLOC]][%[[C0_FINAL]]] : memref<3x!mqtopt.Qubit> + // CHECK: %[[C1_FINAL:.*]] = arith.constant 1 : index + // CHECK: memref.store %[[CZZ_T]]#1, %[[ALLOC]][%[[C1_FINAL]]] : memref<3x!mqtopt.Qubit> + // CHECK: %[[C2_FINAL:.*]] = arith.constant 2 : index + // CHECK: memref.store %[[CZZ_C]], %[[ALLOC]][%[[C2_FINAL]]] : memref<3x!mqtopt.Qubit> + // CHECK: memref.dealloc %[[ALLOC]] : memref<3x!mqtopt.Qubit> + + // Prepare qubits + %angle = arith.constant 3.000000e-01 : f64 + %qreg = quantum.alloc( 3) : !quantum.reg + %q0 = quantum.extract %qreg[ 0] : !quantum.reg -> !quantum.bit + %q1 = quantum.extract %qreg[ 1] : !quantum.reg -> !quantum.bit + %q2 = quantum.extract %qreg[ 2] : !quantum.reg -> !quantum.bit + + // Uncontrolled Ising gates + %q0_xy, %q1_xy = quantum.custom "IsingXY"(%angle) %q0, %q1 : !quantum.bit, !quantum.bit + %q0_xx, %q1_xx = quantum.custom "IsingXX"(%angle) %q0_xy, %q1_xy : !quantum.bit, !quantum.bit + %q0_yy, %q1_yy = quantum.custom "IsingYY"(%angle) %q0_xx, %q1_xx : !quantum.bit, !quantum.bit + %q0_zz, %q1_zz = quantum.custom "IsingZZ"(%angle) %q0_yy, %q1_yy : !quantum.bit, !quantum.bit + + // Controlled Ising gates + %true = arith.constant true + %q0_cxy, %q1_cxy, %q2_cxy = quantum.custom "IsingXY"(%angle) %q0_zz, %q1_zz ctrls(%q2) ctrlvals(%true) : !quantum.bit, !quantum.bit ctrls !quantum.bit + %q0_cxx, %q1_cxx, %q2_cxx = quantum.custom "IsingXX"(%angle) %q0_cxy, %q1_cxy ctrls(%q2_cxy) ctrlvals(%true) : !quantum.bit, !quantum.bit ctrls !quantum.bit + %q0_cyy, %q1_cyy, %q2_cyy = quantum.custom "IsingYY"(%angle) %q0_cxx, %q1_cxx ctrls(%q2_cxx) ctrlvals(%true) : !quantum.bit, !quantum.bit ctrls !quantum.bit + %q0_czz, %q1_czz, %q2_czz = quantum.custom "IsingZZ"(%angle) %q0_cyy, %q1_cyy ctrls(%q2_cyy) ctrlvals(%true) : !quantum.bit, !quantum.bit ctrls !quantum.bit + + + // Release qubits + %qreg1 = quantum.insert %qreg[ 0], %q0_czz : !quantum.reg, !quantum.bit + %qreg2 = quantum.insert %qreg1[ 1], %q1_czz : !quantum.reg, !quantum.bit + %qreg3 = quantum.insert %qreg2[ 2], %q2_czz : !quantum.reg, !quantum.bit + quantum.dealloc %qreg3 : !quantum.reg + return + } +} diff --git a/plugins/catalyst/test/Conversion/quantum_param.mlir b/plugins/catalyst/test/Conversion/quantum_param.mlir new file mode 100644 index 0000000000..92aec4d927 --- /dev/null +++ b/plugins/catalyst/test/Conversion/quantum_param.mlir @@ -0,0 +1,71 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +// RUN: catalyst --tool=opt \ +// RUN: --load-pass-plugin=%mqt_plugin_path% \ +// RUN: --load-dialect-plugin=%mqt_plugin_path% \ +// RUN: --catalyst-pipeline="builtin.module(catalystquantum-to-mqtopt)" \ +// RUN: %s | FileCheck %s + + +// ============================================================================ +// Parameterized gates RX/RY/RZ, PhaseShift and controlled variants +// Groups: Allocation & extraction / Uncontrolled / Controlled / Reinsertion +// ============================================================================ +module { + // CHECK-LABEL: func.func @testCatalystQuantumToMQTOptParameterizedGates + func.func @testCatalystQuantumToMQTOptParameterizedGates() { + // --- Allocation & extraction --------------------------------------------------------------- + // CHECK: %cst = arith.constant 3.000000e-01 : f64 + // CHECK: %[[ALLOC:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> + // CHECK: %[[C0:.*]] = arith.constant 0 : index + // CHECK: %[[Q0:.*]] = memref.load %[[ALLOC]][%[[C0]]] : memref<2x!mqtopt.Qubit> + // CHECK: %[[C1:.*]] = arith.constant 1 : index + // CHECK: %[[Q1:.*]] = memref.load %[[ALLOC]][%[[C1]]] : memref<2x!mqtopt.Qubit> + + // --- Uncontrolled ------------------------------------------------------------------------- + // CHECK: %[[RX:.*]] = mqtopt.rx(%cst static [] mask [false]) %[[Q0]] : !mqtopt.Qubit + // CHECK: %[[RY:.*]] = mqtopt.ry(%cst static [] mask [false]) %[[RX]] : !mqtopt.Qubit + // CHECK: %[[RZ:.*]] = mqtopt.rz(%cst static [] mask [false]) %[[RY]] : !mqtopt.Qubit + // CHECK: %[[PS:.*]] = mqtopt.p(%cst static [] mask [false]) %[[RZ]] : !mqtopt.Qubit + + // --- Controlled ---------------------------------------------------------------------------- + // CHECK: %[[CRX_T:.*]], %[[CRX_C:.*]] = mqtopt.rx(%cst static [] mask [false]) %[[PS]] ctrl %[[Q1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CRY_T:.*]], %[[CRY_C:.*]] = mqtopt.ry(%cst static [] mask [false]) %[[CRX_T]] ctrl %[[CRX_C]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + + // --- Reinsertion --------------------------------------------------------------------------- + // CHECK: %[[C0_FINAL:.*]] = arith.constant 0 : index + // CHECK: memref.store %[[CRY_T]], %[[ALLOC]][%[[C0_FINAL]]] : memref<2x!mqtopt.Qubit> + // CHECK: %[[C1_FINAL:.*]] = arith.constant 1 : index + // CHECK: memref.store %[[CRY_C]], %[[ALLOC]][%[[C1_FINAL]]] : memref<2x!mqtopt.Qubit> + // CHECK: memref.dealloc %[[ALLOC]] : memref<2x!mqtopt.Qubit> + + // Prepare qubits + %angle = arith.constant 3.000000e-01 : f64 + %qreg = quantum.alloc( 2) : !quantum.reg + %q0 = quantum.extract %qreg[ 0] : !quantum.reg -> !quantum.bit + %q1 = quantum.extract %qreg[ 1] : !quantum.reg -> !quantum.bit + + // Non-controlled parameterized gates + %q0_rx = quantum.custom "RX"(%angle) %q0 : !quantum.bit + %q0_ry = quantum.custom "RY"(%angle) %q0_rx : !quantum.bit + %q0_rz = quantum.custom "RZ"(%angle) %q0_ry : !quantum.bit + %q0_p = quantum.custom "PhaseShift"(%angle) %q0_rz : !quantum.bit + + // Controlled parameterized gates + %true = arith.constant true + %q1_out, %q0_out = quantum.custom "RX"(%angle) %q0_p ctrls(%q1) ctrlvals(%true) : !quantum.bit ctrls !quantum.bit + %q1_out2, %q0_out2 = quantum.custom "RY"(%angle) %q1_out ctrls(%q0_out) ctrlvals(%true) : !quantum.bit ctrls !quantum.bit + + // Release qubits + %qreg1 = quantum.insert %qreg[ 0], %q1_out2 : !quantum.reg, !quantum.bit + %qreg2 = quantum.insert %qreg1[ 1], %q0_out2 : !quantum.reg, !quantum.bit + quantum.dealloc %qreg2 : !quantum.reg + return + } +} diff --git a/plugins/catalyst/test/Conversion/quantum_pauli.mlir b/plugins/catalyst/test/Conversion/quantum_pauli.mlir new file mode 100644 index 0000000000..c13c2a9f23 --- /dev/null +++ b/plugins/catalyst/test/Conversion/quantum_pauli.mlir @@ -0,0 +1,112 @@ +// Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +// Copyright (c) 2025 Munich Quantum Software Company GmbH +// All rights reserved. +// +// SPDX-License-Identifier: MIT +// +// Licensed under the MIT License + +// RUN: catalyst --tool=opt \ +// RUN: --load-pass-plugin=%mqt_plugin_path% \ +// RUN: --load-dialect-plugin=%mqt_plugin_path% \ +// RUN: --catalyst-pipeline="builtin.module(catalystquantum-to-mqtopt)" \ +// RUN: %s | FileCheck %s + + +// ============================================================================ +// Pauli family (X, Y, Z) and controlled variants +// Groups: Allocation & extraction / Uncontrolled / Controlled / Reinsertion +// ============================================================================ +module { + // CHECK-LABEL: func.func @testCatalystQuantumToMQTOptPauliGates + func.func @testCatalystQuantumToMQTOptPauliGates() { + // --- Allocation & extraction --------------------------------------------------------------- + // CHECK: %[[ALLOC:.*]] = memref.alloc() : memref<4x!mqtopt.Qubit> + // CHECK: %[[C0:.*]] = arith.constant 0 : index + // CHECK: %[[Q0:.*]] = memref.load %[[ALLOC]][%[[C0]]] : memref<4x!mqtopt.Qubit> + // CHECK: %[[C1:.*]] = arith.constant 1 : index + // CHECK: %[[Q1:.*]] = memref.load %[[ALLOC]][%[[C1]]] : memref<4x!mqtopt.Qubit> + // CHECK: %[[C2:.*]] = arith.constant 2 : index + // CHECK: %[[Q2:.*]] = memref.load %[[ALLOC]][%[[C2]]] : memref<4x!mqtopt.Qubit> + // CHECK: %[[C3:.*]] = arith.constant 3 : index + // CHECK: %[[Q3:.*]] = memref.load %[[ALLOC]][%[[C3]]] : memref<4x!mqtopt.Qubit> + + // --- Uncontrolled Pauli gates -------------------------------------------------------------- + // CHECK: %[[X1:.*]] = mqtopt.x(static [] mask []) %[[Q0]] : !mqtopt.Qubit + // CHECK: %[[Y1:.*]] = mqtopt.y(static [] mask []) %[[X1]] : !mqtopt.Qubit + // CHECK: %[[Z1:.*]] = mqtopt.z(static [] mask []) %[[Y1]] : !mqtopt.Qubit + // CHECK: %[[I1:.*]] = mqtopt.i(static [] mask []) %[[Z1]] : !mqtopt.Qubit + + // --- Controlled Pauli gates ---------------------------------------------------------------- + // CHECK: %[[TRUE:.*]] = arith.constant true + // CHECK: %[[T1:.*]], %[[C1:.*]] = mqtopt.x(static [] mask []) %[[I1]] ctrl %[[Q1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[T2:.*]], %[[C2:.*]] = mqtopt.y(static [] mask []) %[[T1]] ctrl %[[C1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[T3:.*]], %[[C3:.*]] = mqtopt.z(static [] mask []) %[[T2]] ctrl %[[C2]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[T4:.*]], %[[C4:.*]] = mqtopt.i(static [] mask []) %[[T3]] ctrl %[[C3]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + + // --- Two-qubit controlled gates ------------------------------------------------------------ + // CHECK: %[[T5:.*]], %[[C5:.*]] = mqtopt.x(static [] mask []) %[[C4]] ctrl %[[T4]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[T6:.*]], %[[C6:.*]] = mqtopt.y(static [] mask []) %[[C5]] ctrl %[[T5]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[T7:.*]], %[[C7:.*]] = mqtopt.z(static [] mask []) %[[C6]] ctrl %[[T6]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[T8:.*]], %[[C8:.*]]:2 = mqtopt.x(static [] mask []) %[[Q2]] ctrl %[[T7]], %[[C7]] : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit + + // --- Controlled two-qubit controlled gates --------------------------------------------------------------------------- + // CHECK: %[[T9:.*]], %[[C9:.*]]:2 = mqtopt.x(static [] mask []) %[[C8]]#0 ctrl %[[C8]]#1, %[[T8]] : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[T10:.*]], %[[C10:.*]]:2 = mqtopt.y(static [] mask []) %[[C9]]#0 ctrl %[[C9]]#1, %[[T9]] : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[T11:.*]], %[[C11:.*]]:2 = mqtopt.z(static [] mask []) %[[C10]]#0 ctrl %[[C10]]#1, %[[T10]] : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[T12:.*]], %[[C12:.*]]:3 = mqtopt.x(static [] mask []) %[[C11]]#1 ctrl %[[Q3]], %[[T11]], %[[C11]]#0 : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit, !mqtopt.Qubit + + // Release qubits + // CHECK: %[[C3:.*]] = arith.constant 3 : index + // CHECK: memref.store %[[C12]]#2, %[[ALLOC]][%[[C3]]] : memref<4x!mqtopt.Qubit> + // CHECK: %[[C2:.*]] = arith.constant 2 : index + // CHECK: memref.store %[[C12]]#1, %[[ALLOC]][%[[C2]]] : memref<4x!mqtopt.Qubit> + // CHECK: %[[C1:.*]] = arith.constant 1 : index + // CHECK: memref.store %[[C12]]#0, %[[ALLOC]][%[[C1]]] : memref<4x!mqtopt.Qubit> + // CHECK: %[[C0:.*]] = arith.constant 0 : index + // CHECK: memref.store %[[T12]], %[[ALLOC]][%[[C0]]] : memref<4x!mqtopt.Qubit> + // CHECK: memref.dealloc %[[ALLOC]] : memref<4x!mqtopt.Qubit> + + // Prepare qubits + %qreg = quantum.alloc( 4) : !quantum.reg + %q0 = quantum.extract %qreg[ 0] : !quantum.reg -> !quantum.bit + %q1 = quantum.extract %qreg[ 1] : !quantum.reg -> !quantum.bit + %q2 = quantum.extract %qreg[ 2] : !quantum.reg -> !quantum.bit + %q3 = quantum.extract %qreg[ 3] : !quantum.reg -> !quantum.bit + + // Non-controlled Pauli gates + %q0_x = quantum.custom "PauliX"() %q0 : !quantum.bit + %q0_y = quantum.custom "PauliY"() %q0_x : !quantum.bit + %q0_z = quantum.custom "PauliZ"() %q0_y : !quantum.bit + %q0_i = quantum.custom "Identity"() %q0_z : !quantum.bit + + %true = arith.constant true + + // Controlled Pauli gates + %q0_ctrlx, %q1_ctrlx = quantum.custom "PauliX"() %q0_i ctrls(%q1) ctrlvals(%true) :!quantum.bit ctrls !quantum.bit + %q0_ctrly, %q1_ctrly = quantum.custom "PauliY"() %q0_ctrlx ctrls(%q1_ctrlx) ctrlvals(%true) :!quantum.bit ctrls !quantum.bit + %q0_ctrlz, %q1_ctrlz = quantum.custom "PauliZ"() %q0_ctrly ctrls(%q1_ctrly) ctrlvals(%true) :!quantum.bit ctrls !quantum.bit + %q0_ctrli, %q1_ctrli = quantum.custom "Identity"() %q0_ctrlz ctrls(%q1_ctrlz) ctrlvals(%true) :!quantum.bit ctrls !quantum.bit + + // C gates + %q0_cx, %q1_cx = quantum.custom "CNOT"() %q0_ctrli, %q1_ctrli : !quantum.bit, !quantum.bit + %q0_cy, %q1_cy = quantum.custom "CY"() %q0_cx, %q1_cx : !quantum.bit, !quantum.bit + %q0_cz, %q1_cz = quantum.custom "CZ"() %q0_cy, %q1_cy : !quantum.bit, !quantum.bit + %q0_ct, %q1_ct, %q2_ct = quantum.custom "Toffoli"() %q0_cz, %q1_cz, %q2 : !quantum.bit, !quantum.bit, !quantum.bit + + // Controlled-C gates + %q0_ccx, %q1_ccx, %q2_ccx = quantum.custom "CNOT"() %q0_ct, %q1_ct ctrls(%q2_ct) ctrlvals(%true) :!quantum.bit, !quantum.bit ctrls !quantum.bit + %q0_ccy, %q1_ccy, %q2_ccy = quantum.custom "CY"() %q0_ccx, %q1_ccx ctrls(%q2_ccx) ctrlvals(%true) :!quantum.bit, !quantum.bit ctrls !quantum.bit + %q0_ccz, %q1_ccz, %q2_ccz = quantum.custom "CZ"() %q0_ccy, %q1_ccy ctrls(%q2_ccy) ctrlvals(%true) :!quantum.bit, !quantum.bit ctrls !quantum.bit + %q0_cccx, %q1_cccx, %q2_cccx, %q3_cccx = quantum.custom "Toffoli"() %q0_ccz, %q1_ccz, %q2_ccz ctrls(%q3) ctrlvals(%true) :!quantum.bit, !quantum.bit, !quantum.bit ctrls !quantum.bit + + // Release qubits + %qreg1 = quantum.insert %qreg[ 3], %q3_cccx : !quantum.reg, !quantum.bit + %qreg2 = quantum.insert %qreg1[ 2], %q2_cccx : !quantum.reg, !quantum.bit + %qreg3 = quantum.insert %qreg2[ 1], %q1_cccx : !quantum.reg, !quantum.bit + %qreg4 = quantum.insert %qreg3[ 0], %q0_cccx : !quantum.reg, !quantum.bit + + quantum.dealloc %qreg4 : !quantum.reg + return + } +} diff --git a/plugins/catalyst/test/lit.cfg.py b/plugins/catalyst/test/lit.cfg.py new file mode 100644 index 0000000000..26b0d20d52 --- /dev/null +++ b/plugins/catalyst/test/lit.cfg.py @@ -0,0 +1,52 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +# ruff: noqa: INP001 + +"""LIT Configuration file for the MQT MLIR Catalyst Plugin test suite. + +This file configures the LLVM LIT testing infrastructure for MLIR dialect tests. +""" + +from __future__ import annotations + +import contextlib +from pathlib import Path + +import lit.formats +from lit.llvm import llvm_config + +from mqt.core.plugins.catalyst import get_catalyst_plugin_abs_path + +# Use `lit_config` to access `config` from lit.site.cfg.py +config = globals().get("config") +if config is None: + msg = "LIT config object is missing. Ensure lit.site.cfg.py is loaded first." + raise RuntimeError(msg) + +config.name = "MQT MLIR Catalyst Plugin test suite" +config.test_format = lit.formats.ShTest(execute_external=True) + +# Define the file extensions to treat as test files. +config.suffixes = [".mlir"] +config.excludes = ["lit.cfg.py"] + +# Define the root path of where to look for tests. +config.test_source_root = Path(__file__).parent + +# Define where to execute tests (and produce the output). +config.test_exec_root = getattr(config, "plugin_test_dir", ".lit") + +# Define PATH to include the tools needed for our tests. +with contextlib.suppress(AttributeError): + # From within a build target we have access to cmake variables configured in lit.site.cfg.py.in. + llvm_config.with_environment("PATH", config.llvm_tools_dir, append_path=True) # FileCheck + +# Dynamic plugin path substitution (prefers installed wheel; env override supported by the helper) +plugin_path = get_catalyst_plugin_abs_path() +config.substitutions.append(("%mqt_plugin_path%", str(plugin_path))) diff --git a/plugins/catalyst/test/lit.site.cfg.py.in b/plugins/catalyst/test/lit.site.cfg.py.in new file mode 100644 index 0000000000..d4fb6fdcdc --- /dev/null +++ b/plugins/catalyst/test/lit.site.cfg.py.in @@ -0,0 +1,21 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +@LIT_SITE_CFG_IN_HEADER@ + +config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" +config.build_dir = "@CMAKE_BINARY_DIR@" +config.source_dir = "@CMAKE_SOURCE_DIR@" + +# Provided by ExternalDependencies.cmake +config.catalyst_bin = "@CATALYST_BIN@" + +import lit.llvm +lit.llvm.initialize(lit_config, config) + +lit_config.load_config(config, "@CMAKE_SOURCE_DIR@/test/lit.cfg.py") diff --git a/plugins/catalyst/test/python/test_plugin.py b/plugins/catalyst/test/python/test_plugin.py new file mode 100644 index 0000000000..7ea8d2aa0a --- /dev/null +++ b/plugins/catalyst/test/python/test_plugin.py @@ -0,0 +1,867 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +"""Tests for MQT plugin execution with PennyLane and Catalyst. + +These tests check that the MQT plugin conversion passes execute successfully +for various gate categories, mirroring the MLIR conversion tests. They verify +that the full lossless roundtrip (CatalystQuantum → MQTOpt → CatalystQuantum) +works correctly. The tests use FileCheck (from LLVM) to verify the generated MLIR output. + +Environment Variables: + FILECHECK_PATH: Optional path to FileCheck binary if not in PATH +""" + +from __future__ import annotations + +import os +import subprocess +from pathlib import Path + +import pennylane as qml +from catalyst.passes import apply_pass + +from mqt.core.plugins.catalyst import get_device + + +def _run_filecheck(mlir_content: str, check_patterns: str, test_name: str = "test") -> None: + """Run FileCheck on MLIR content using CHECK patterns from a string. + + Args: + mlir_content: The MLIR output to verify + check_patterns: String containing FileCheck directives (lines starting with // CHECK) + test_name: Name of the test (for error messages) + + Raises: + RuntimeError: If FileCheck is not found + AssertionError: If FileCheck validation fails + """ + # Find FileCheck (usually in LLVM bin directory) + filecheck = None + possible_paths = [ + "FileCheck", # If in PATH + os.environ.get("FILECHECK_PATH"), # Custom env variable + "/opt/homebrew/opt/llvm/bin/FileCheck", # Common macOS location + ] + + for path in possible_paths: + if path: + try: + result = subprocess.run([path, "--version"], check=False, capture_output=True, timeout=5) # noqa: S603 + if result.returncode == 0: + filecheck = path + break + except (subprocess.SubprocessError, FileNotFoundError): + continue + + if not filecheck: + msg = ( + "FileCheck not found. Please ensure LLVM's FileCheck is in your PATH, " + "or set FILECHECK_PATH environment variable." + ) + raise RuntimeError(msg) + + # Write CHECK patterns to a temporary file + import tempfile + + with tempfile.NamedTemporaryFile(encoding="utf-8", mode="w", suffix=".mlir", delete=False) as check_file: + check_file.write(check_patterns) + check_file_path = check_file.name + + try: + # Run FileCheck: pipe MLIR content as stdin, use check_file for CHECK directives + result = subprocess.run( # noqa: S603 + [filecheck, check_file_path, "--allow-unused-prefixes"], + check=False, + input=mlir_content.encode(), + capture_output=True, + timeout=30, + ) + + if result.returncode != 0: + error_msg = result.stderr.decode() if result.stderr else "Unknown error" + msg = ( + f"FileCheck failed for {test_name}:\n{error_msg}\n\n" + f"MLIR Output (first 2000 chars):\n{mlir_content[:2000]}..." + ) + raise AssertionError(msg) + finally: + # Clean up temporary file + Path(check_file_path).unlink() + + +def test_clifford_gates_roundtrip() -> None: + """Test roundtrip conversion of Clifford+T gates. + + Mirrors: quantum_clifford.mlir + Gates: H, SX, SX†, S, S†, T, T†, and their controlled variants + + Raises: + FileNotFoundError: If intermediate MLIR files are not found + """ + + @apply_pass("mqt.mqtopt-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-mqtopt") + @qml.qnode(get_device("lightning.qubit", wires=2)) + def circuit() -> None: + # Non-controlled Clifford+T gates + qml.Hadamard(wires=0) + qml.SX(wires=0) + qml.adjoint(qml.SX(wires=0)) + qml.S(wires=0) + qml.adjoint(qml.S(wires=0)) + qml.T(wires=0) + qml.adjoint(qml.T(wires=0)) + + # Controlled Clifford+T gates + qml.CH(wires=[1, 0]) + qml.ctrl(qml.SX(wires=0), control=1) + # Why is `qml.ctrl(qml.adjoint(qml.SX(wires=0)), control=1)` not supported by Catalyst? + qml.ctrl(qml.S(wires=0), control=1) + qml.ctrl(qml.adjoint(qml.S(wires=0)), control=1) + qml.ctrl(qml.T(wires=0), control=1) + qml.ctrl(qml.adjoint(qml.T(wires=0)), control=1) + + custom_pipeline = [ + ("to-mqtopt", ["builtin.module(catalystquantum-to-mqtopt)"]), + ("to-catalystquantum", ["builtin.module(mqtopt-to-catalystquantum)"]), + ] + + @qml.qjit(target="mlir", pipelines=custom_pipeline, autograph=True, keep_intermediate=2) + def module() -> None: + return circuit() + + # Verify the roundtrip completes successfully + mlir_opt = module.mlir_opt + assert mlir_opt + + # Find where MLIR files are generated (relative to cwd where pytest is run) + # Catalyst generates MLIR files in the current working directory + mlir_dir = Path.cwd() + + # Read the intermediate MLIR files + mlir_to_mqtopt = mlir_dir / "3_to-mqtopt.mlir" + mlir_to_catalyst = mlir_dir / "4_MQTOptToCatalystQuantum.mlir" + + if not mlir_to_mqtopt.exists() or not mlir_to_catalyst.exists(): + available_files = list(mlir_dir.glob("*.mlir")) + msg = f"Expected MLIR files not found in {mlir_dir}.\nAvailable files: {[f.name for f in available_files]}" + raise FileNotFoundError(msg) + + with Path(mlir_to_mqtopt).open("r", encoding="utf-8") as f: + mlir_after_mqtopt = f.read() + with Path(mlir_to_catalyst).open("r", encoding="utf-8") as f: + mlir_after_roundtrip = f.read() + + # Verify CatalystQuantum → MQTOpt conversion + check_to_mqtopt = """ + // CHECK: func.func {{.*}}@circuit + // CHECK: %[[ALLOC:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> + // CHECK: %[[Q0:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<2x!mqtopt.Qubit> + + // Uncontrolled Clifford+T gates + // Hadamard + // CHECK: %[[H:.*]] = mqtopt.h(static [] mask []) %[[Q0]] : !mqtopt.Qubit + + // SX gate decomposes to RZ(π/2) -> RY(π/2) -> RZ(-π/2) + gphase + // CHECK: %[[RZ1:.*]] = mqtopt.rz({{.*}} static [] mask [false]) %[[H]] : !mqtopt.Qubit + // CHECK: %[[RY1:.*]] = mqtopt.ry({{.*}} static [] mask [false]) %[[RZ1]] : !mqtopt.Qubit + // CHECK: %[[RZ2:.*]] = mqtopt.rz({{.*}} static [] mask [false]) %[[RY1]] : !mqtopt.Qubit + // CHECK: mqtopt.gphase({{.*}} static [] mask [false]) + // CHECK: mqtopt.gphase({{.*}} static [] mask [false]) + + // SX† gate also decomposes to RZ -> RY -> RZ + // CHECK: %[[RZ3:.*]] = mqtopt.rz({{.*}} static [] mask [false]) %[[RZ2]] : !mqtopt.Qubit + // CHECK: %[[RY2:.*]] = mqtopt.ry({{.*}} static [] mask [false]) %[[RZ3]] : !mqtopt.Qubit + // CHECK: %[[RZ4:.*]] = mqtopt.rz({{.*}} static [] mask [false]) %[[RY2]] : !mqtopt.Qubit + + // S, S†, T, T† + // CHECK: %[[S:.*]] = mqtopt.s(static [] mask []) %[[RZ4]] : !mqtopt.Qubit + // CHECK: %[[SDG:.*]] = mqtopt.s(static [] mask []) %[[S]] : !mqtopt.Qubit + // CHECK: %[[T:.*]] = mqtopt.t(static [] mask []) %[[SDG]] : !mqtopt.Qubit + // CHECK: %[[TDG:.*]] = mqtopt.t(static [] mask []) %[[T]] : !mqtopt.Qubit + + // Load control qubit + // CHECK: %[[Q1:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<2x!mqtopt.Qubit> + + // Controlled Clifford+T gates + // Controlled Hadamard + // CHECK: %[[CH_T:.*]], %[[CH_C:.*]] = mqtopt.h(static [] mask []) %[[TDG]] ctrl %[[Q1]] + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Controlled SX decomposes to controlled RZ -> RY -> RZ + gphase + // CHECK: %[[CRZ1_T:.*]], %[[CRZ1_C:.*]] = mqtopt.rz({{.*}} static [] mask [false]) %[[CH_T]] ctrl %[[CH_C]] + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CRY1_T:.*]], %[[CRY1_C:.*]] = mqtopt.ry({{.*}} static [] mask [false]) %[[CRZ1_C]] ctrl %[[CRZ1_T]] + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CRZ2_T:.*]], %[[CRZ2_C:.*]] = mqtopt.rz({{.*}} static [] mask [false]) %[[CRY1_C]] ctrl %[[CRY1_T]] + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: mqtopt.gphase({{.*}} static [] mask [false]) ctrl + + // Controlled S + // CHECK: %[[CS_T:.*]], %[[CS_C:.*]] = mqtopt.s(static [] mask []) %[[CRZ2_C]] ctrl %[[CRZ2_T]] + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Controlled S† (represented as controlled phase gate) + // CHECK: %[[CSDG_T:.*]], %[[CSDG_C:.*]] = mqtopt.p({{.*}} static [] mask [false]) %[[CS_T]] ctrl %[[CS_C]] + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Controlled T + // CHECK: %[[CT_T:.*]], %[[CT_C:.*]] = mqtopt.t(static [] mask []) %[[CSDG_C]] ctrl %[[CSDG_T]] + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Controlled T† (represented as controlled phase gate) + // CHECK: %[[CTDG_T:.*]], %[[CTDG_C:.*]] = mqtopt.p({{.*}} static [] mask [false]) %[[CT_T]] ctrl %[[CT_C]] + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Reinsertion + // CHECK: memref.store {{.*}}, %[[ALLOC]][{{.*}}] : memref<2x!mqtopt.Qubit> + // CHECK: memref.store {{.*}}, %[[ALLOC]][{{.*}}] : memref<2x!mqtopt.Qubit> + // CHECK: memref.dealloc %[[ALLOC]] : memref<2x!mqtopt.Qubit> + """ + _run_filecheck(mlir_after_mqtopt, check_to_mqtopt, "Clifford: CatalystQuantum to MQTOpt") + + # Verify MQTOpt → CatalystQuantum conversion + check_to_catalyst = """ + // CHECK: func.func {{.*}}@circuit + // CHECK: %[[QREG:.*]] = quantum.alloc({{.*}}) : !quantum.reg + // CHECK: %[[Q0:.*]] = quantum.extract %[[QREG]][{{.*}}] : !quantum.reg -> !quantum.bit + + // Uncontrolled Clifford+T gates + // Hadamard + // CHECK: %[[H:.*]] = quantum.custom "Hadamard"() %[[Q0]] : !quantum.bit + + // SX decomposed to RZ -> RY -> RZ + // CHECK: %[[RZ1:.*]] = quantum.custom "RZ"({{.*}}) %[[H]] : !quantum.bit + // CHECK: %[[RY1:.*]] = quantum.custom "RY"({{.*}}) %[[RZ1]] : !quantum.bit + // CHECK: %[[RZ2:.*]] = quantum.custom "RZ"({{.*}}) %[[RY1]] : !quantum.bit + + // SX† decomposed to RZ -> RY -> RZ + // CHECK: %[[RZ3:.*]] = quantum.custom "RZ"({{.*}}) %[[RZ2]] : !quantum.bit + // CHECK: %[[RY2:.*]] = quantum.custom "RY"({{.*}}) %[[RZ3]] : !quantum.bit + // CHECK: %[[RZ4:.*]] = quantum.custom "RZ"({{.*}}) %[[RY2]] : !quantum.bit + + // S, S†, T, T† + // CHECK: %[[S:.*]] = quantum.custom "S"() %[[RZ4]] : !quantum.bit + // CHECK: %[[SDG:.*]] = quantum.custom "S"() %[[S]] : !quantum.bit + // CHECK: %[[T:.*]] = quantum.custom "T"() %[[SDG]] : !quantum.bit + // CHECK: %[[TDG:.*]] = quantum.custom "T"() %[[T]] : !quantum.bit + + // Extract control qubit + // CHECK: %[[Q1:.*]] = quantum.extract %[[QREG]][{{.*}}] : !quantum.reg -> !quantum.bit + + // Controlled Clifford+T gates + // Controlled Hadamard + // CHECK: %[[CH_T:.*]], %[[CH_C:.*]] = quantum.custom "Hadamard"() %[[TDG]] ctrls(%[[Q1]]) + // CHECK-SAME: ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // Controlled SX decomposed to CRZ -> CRY -> CRZ + // CHECK: %[[CRZ1_T:.*]], %[[CRZ1_C:.*]] = quantum.custom "CRZ"({{.*}}) %[[CH_T]] ctrls(%[[CH_C]]) + // CHECK-SAME: ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CRY_T:.*]], %[[CRY_C:.*]] = quantum.custom "CRY"({{.*}}) %[[CRZ1_C]] ctrls(%[[CRZ1_T]]) + // CHECK-SAME: ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CRZ2_T:.*]], %[[CRZ2_C:.*]] = quantum.custom "CRZ"({{.*}}) %[[CRY_C]] ctrls(%[[CRY_T]]) + // CHECK-SAME: ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // Controlled S + // CHECK: %[[CS_T:.*]], %[[CS_C:.*]] = quantum.custom "S"() %[[CRZ2_C]] ctrls(%[[CRZ2_T]]) + // CHECK-SAME: ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // Controlled S† (as ControlledPhaseShift) + // CHECK: %[[CSDG_T:.*]], %[[CSDG_C:.*]] = quantum.custom "ControlledPhaseShift"({{.*}}) %[[CS_T]] + // CHECK-SAME: ctrls(%[[CS_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // Controlled T + // CHECK: %[[CT_T:.*]], %[[CT_C:.*]] = quantum.custom "T"() %[[CSDG_C]] ctrls(%[[CSDG_T]]) + // CHECK-SAME: ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // Controlled T† (as ControlledPhaseShift) + // CHECK: %[[CTDG_T:.*]], %[[CTDG_C:.*]] = quantum.custom "ControlledPhaseShift"({{.*}}) %[[CT_T]] + // CHECK-SAME: ctrls(%[[CT_C]]) ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // Reinsertion + // CHECK: quantum.insert %[[QREG]][{{.*}}], {{.*}} : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], {{.*}} : !quantum.reg, !quantum.bit + // CHECK: quantum.dealloc %[[QREG]] : !quantum.reg + """ + _run_filecheck(mlir_after_roundtrip, check_to_catalyst, "Clifford: MQTOpt to CatalystQuantum") + + +def test_pauli_gates_roundtrip() -> None: + """Test roundtrip conversion of Pauli gates. + + Mirrors: quantum_pauli.mlir + Gates: X, Y, Z, I, and their controlled variants (CNOT, CY, CZ, Toffoli) + Structure: + 1. Uncontrolled Pauli gates (X, Y, Z, I) + 2. Controlled Pauli gates (using qml.ctrl on Pauli gates) + 3. Two-qubit controlled gates (CNOT, CY, CZ) + 4. Toffoli (CCX) + 5. Controlled two-qubit gates (controlled CNOT, CY, CZ, Toffoli) + + Raises: + FileNotFoundError: If intermediate MLIR files are not found + """ + + @apply_pass("mqt.mqtopt-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-mqtopt") + @qml.qnode(get_device("lightning.qubit", wires=4)) + def circuit() -> None: + # Uncontrolled Pauli gates + qml.PauliX(wires=0) + qml.PauliY(wires=0) + qml.PauliZ(wires=0) + qml.Identity(wires=0) + + # Controlled Pauli gates (single control) - use qml.ctrl on Pauli gates + qml.ctrl(qml.PauliX(wires=0), control=1) + qml.ctrl(qml.PauliY(wires=0), control=1) + qml.ctrl(qml.PauliZ(wires=0), control=1) + # Why is `qml.ctrl(qml.Identity(wires=0), control=1)` not supported by Catalyst? + + # Two-qubit controlled gates (explicit CNOT, CY, CZ gate names) + qml.CNOT(wires=[0, 1]) + qml.CY(wires=[0, 1]) + qml.CZ(wires=[0, 1]) + + # Toffoli (also CCX) + qml.Toffoli(wires=[0, 1, 2]) + + # Controlled multi-qubit gates (adding extra controls) + qml.ctrl(qml.CNOT(wires=[0, 1]), control=2) + qml.ctrl(qml.CY(wires=[0, 1]), control=2) + qml.ctrl(qml.CZ(wires=[0, 1]), control=2) + qml.ctrl(qml.Toffoli(wires=[0, 1, 2]), control=3) + + custom_pipeline = [ + ("to-mqtopt", ["builtin.module(catalystquantum-to-mqtopt)"]), + ("to-catalystquantum", ["builtin.module(mqtopt-to-catalystquantum)"]), + ] + + @qml.qjit(target="mlir", pipelines=custom_pipeline, autograph=True, keep_intermediate=2) + def module() -> None: + return circuit() + + # Verify the roundtrip completes successfully + mlir_opt = module.mlir_opt + assert mlir_opt + + # Find where MLIR files are generated (relative to cwd where pytest is run) + # Catalyst generates MLIR files in the current working directory + mlir_dir = Path.cwd() + + # Read the intermediate MLIR files + mlir_to_mqtopt = mlir_dir / "3_to-mqtopt.mlir" + mlir_to_catalyst = mlir_dir / "4_MQTOptToCatalystQuantum.mlir" + + if not mlir_to_mqtopt.exists() or not mlir_to_catalyst.exists(): + available_files = list(mlir_dir.glob("*.mlir")) + msg = f"Expected MLIR files not found in {mlir_dir}.\nAvailable files: {[f.name for f in available_files]}" + raise FileNotFoundError(msg) + + with Path(mlir_to_mqtopt).open("r", encoding="utf-8") as f: + mlir_after_mqtopt = f.read() + with Path(mlir_to_catalyst).open("r", encoding="utf-8") as f: + mlir_after_roundtrip = f.read() + + # Verify CatalystQuantum → MQTOpt conversion + check_to_mqtopt = """ + // CHECK: func.func {{.*}}@circuit + // CHECK: %[[ALLOC:.*]] = memref.alloc() : memref<4x!mqtopt.Qubit> + // CHECK: %[[Q0:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<4x!mqtopt.Qubit> + + // Uncontrolled Pauli gates + // CHECK: %[[X1:.*]] = mqtopt.x({{.*}}) %[[Q0]] : !mqtopt.Qubit + // CHECK: %[[Y1:.*]] = mqtopt.y({{.*}}) %[[X1]] : !mqtopt.Qubit + // CHECK: %[[Z1:.*]] = mqtopt.z({{.*}}) %[[Y1]] : !mqtopt.Qubit + // CHECK: %[[I1:.*]] = mqtopt.i({{.*}}) %[[Z1]] : !mqtopt.Qubit + + // Load Q1 for controlled Pauli gates + // CHECK: %[[Q1:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<4x!mqtopt.Qubit> + + // Controlled Pauli gates (single control) + // CHECK: %[[T1:.*]], %[[C1:.*]] = mqtopt.x({{.*}}) %[[I1]] ctrl %[[Q1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[T2:.*]], %[[C2:.*]] = mqtopt.y({{.*}}) %[[C1]] ctrl %[[T1]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[T3:.*]], %[[C3:.*]] = mqtopt.z({{.*}}) %[[C2]] ctrl %[[T2]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Two-qubit controlled gates (CNOT, CY, CZ as X, Y, Z with ctrl) + // CHECK: %[[T4:.*]], %[[C4:.*]] = mqtopt.x({{.*}}) %[[T3]] ctrl %[[C3]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[T5:.*]], %[[C5:.*]] = mqtopt.y({{.*}}) %[[C4]] ctrl %[[T4]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[T6:.*]], %[[C6:.*]] = mqtopt.z({{.*}}) %[[C5]] ctrl %[[T5]] : !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Load Q2 for Toffoli + // CHECK: %[[Q2:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<4x!mqtopt.Qubit> + + // Toffoli (X with 2 controls) + // CHECK: %[[T7:.*]], %[[C7:.*]]:2 = mqtopt.x({{.*}}) %[[Q2]] ctrl %[[T6]], %[[C6]] + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit + + // Controlled two-qubit gates (add extra control) - qubit ordering from actual MLIR + // CHECK: %[[T8:.*]], %[[C8:.*]]:2 = mqtopt.x({{.*}}) %[[C7]]#0 ctrl %[[C7]]#1, %[[T7]] + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[T9:.*]], %[[C9:.*]]:2 = mqtopt.y({{.*}}) %[[C8]]#1 ctrl %[[T8]], %[[C8]]#0 + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[T10:.*]], %[[C10:.*]]:2 = mqtopt.z({{.*}}) %[[T9]] ctrl %[[C9]]#0, %[[C9]]#1 + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit + + // Load Q3 for controlled Toffoli + // CHECK: %[[Q3:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<4x!mqtopt.Qubit> + + // Controlled Toffoli (X with 3 controls) + // CHECK: %[[T11:.*]], %[[C11:.*]]:3 = mqtopt.x({{.*}}) %[[C10]]#0 ctrl %[[Q3]], %[[C10]]#1, %[[T10]] + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit, !mqtopt.Qubit, !mqtopt.Qubit + + // Reinsertion + // CHECK: memref.store {{.*}}, %[[ALLOC]][{{.*}}] : memref<4x!mqtopt.Qubit> + // CHECK: memref.store {{.*}}, %[[ALLOC]][{{.*}}] : memref<4x!mqtopt.Qubit> + // CHECK: memref.dealloc %[[ALLOC]] : memref<4x!mqtopt.Qubit> + """ + _run_filecheck(mlir_after_mqtopt, check_to_mqtopt, "Pauli: CatalystQuantum to MQTOpt") + + # Verify MQTOpt → CatalystQuantum conversion + check_to_catalyst = """ + // CHECK: func.func {{.*}}@circuit + // CHECK: %[[QREG:.*]] = quantum.alloc({{.*}}) : !quantum.reg + + // Qubits extracted as needed + // CHECK: %[[Q0:.*]] = quantum.extract %[[QREG]][{{.*}}] : !quantum.reg -> !quantum.bit + + // Uncontrolled Pauli gates + // CHECK: %[[X:.*]] = quantum.custom "PauliX"() %[[Q0]] : !quantum.bit + // CHECK: %[[Y:.*]] = quantum.custom "PauliY"() %[[X]] : !quantum.bit + // CHECK: %[[Z:.*]] = quantum.custom "PauliZ"() %[[Y]] : !quantum.bit + // CHECK: %[[I:.*]] = quantum.custom "Identity"() %[[Z]] : !quantum.bit + + // Extract Q1 for controlled Pauli gates + // CHECK: %[[Q1:.*]] = quantum.extract %[[QREG]][{{.*}}] : !quantum.reg -> !quantum.bit + + // Controlled Pauli gates using CNOT/CY/CZ + // CHECK: quantum.custom "CNOT"() %[[I]] ctrls(%[[Q1]]) ctrlvals( + // CHECK: quantum.custom "CY"() {{.*}} ctrls({{.*}}) ctrlvals( + // CHECK: quantum.custom "CZ"() {{.*}} ctrls({{.*}}) ctrlvals( + + // Two-qubit controlled gates (CNOT, CY, CZ) + // CHECK: quantum.custom "CNOT"() {{.*}} ctrls({{.*}}) ctrlvals( + // CHECK: quantum.custom "CY"() {{.*}} ctrls({{.*}}) ctrlvals( + // CHECK: quantum.custom "CZ"() {{.*}} ctrls({{.*}}) ctrlvals( + + // Extract Q2 for Toffoli + // CHECK: %[[Q2:.*]] = quantum.extract %[[QREG]][{{.*}}] : !quantum.reg -> !quantum.bit + + // Toffoli + // CHECK: quantum.custom "Toffoli"() {{.*}} ctrls({{.*}}, {{.*}}) ctrlvals( + + // Reinsertion + // CHECK: quantum.insert %[[QREG]][{{.*}}], {{.*}} : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], {{.*}} : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], {{.*}} : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], {{.*}} : !quantum.reg, !quantum.bit + // CHECK: quantum.dealloc %[[QREG]] : !quantum.reg + """ + _run_filecheck(mlir_after_roundtrip, check_to_catalyst, "Pauli: MQTOpt to CatalystQuantum") + + +def test_parameterized_gates_roundtrip() -> None: + """Test roundtrip conversion of parameterized rotation gates. + + Mirrors: quantum_param.mlir + Gates: RX, RY, RZ, PhaseShift, and their controlled variants (CRX, CRY) + Note: MLIR test does NOT include CRZ, only CRX and CRY + + Raises: + FileNotFoundError: If intermediate MLIR files are not found + """ + + @apply_pass("mqt.mqtopt-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-mqtopt") + @qml.qnode(get_device("lightning.qubit", wires=2)) + def circuit() -> None: + angle = 0.3 + + # Non-controlled parameterized gates + qml.RX(angle, wires=0) + qml.RY(angle, wires=0) + qml.RZ(angle, wires=0) + qml.PhaseShift(angle, wires=0) + + # Controlled parameterized gates + qml.CRX(angle, wires=[1, 0]) + qml.CRY(angle, wires=[1, 0]) + + custom_pipeline = [ + ("to-mqtopt", ["builtin.module(catalystquantum-to-mqtopt)"]), + ("to-catalystquantum", ["builtin.module(mqtopt-to-catalystquantum)"]), + ] + + @qml.qjit(target="mlir", pipelines=custom_pipeline, autograph=True, keep_intermediate=2) + def module() -> None: + return circuit() + + # Verify the roundtrip completes successfully + mlir_opt = module.mlir_opt + assert mlir_opt + + # Find where MLIR files are generated (relative to cwd where pytest is run) + # Catalyst generates MLIR files in the current working directory + mlir_dir = Path.cwd() + + # Read the intermediate MLIR files + mlir_to_mqtopt = mlir_dir / "3_to-mqtopt.mlir" + mlir_to_catalyst = mlir_dir / "4_MQTOptToCatalystQuantum.mlir" + + if not mlir_to_mqtopt.exists() or not mlir_to_catalyst.exists(): + available_files = list(mlir_dir.glob("*.mlir")) + msg = f"Expected MLIR files not found in {mlir_dir}.\nAvailable files: {[f.name for f in available_files]}" + raise FileNotFoundError(msg) + + with Path(mlir_to_mqtopt).open("r", encoding="utf-8") as f: + mlir_after_mqtopt = f.read() + with Path(mlir_to_catalyst).open("r", encoding="utf-8") as f: + mlir_after_roundtrip = f.read() + + # Verify CatalystQuantum → MQTOpt conversion + check_to_mqtopt = """ + // CHECK: func.func {{.*}}@circuit + // CHECK: %[[ALLOC:.*]] = memref.alloc() : memref<2x!mqtopt.Qubit> + // CHECK: %[[Q0:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<2x!mqtopt.Qubit> + + // Uncontrolled parameterized gates + // CHECK: %[[RX:.*]] = mqtopt.rx({{.*}}) %[[Q0]] : !mqtopt.Qubit + // CHECK: %[[RY:.*]] = mqtopt.ry({{.*}}) %[[RX]] : !mqtopt.Qubit + // CHECK: %[[RZ:.*]] = mqtopt.rz({{.*}}) %[[RY]] : !mqtopt.Qubit + // CHECK: %[[PS:.*]] = mqtopt.p({{.*}}) %[[RZ]] : !mqtopt.Qubit + + // Load Q1 for controlled parameterized gates + // CHECK: %[[Q1:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<2x!mqtopt.Qubit> + + // Controlled parameterized gates (qubits swap roles after each operation like Pauli gates) + // CHECK: %[[CRX_T:.*]], %[[CRX_C:.*]] = mqtopt.rx({{.*}}) %[[PS]] ctrl %[[Q1]] + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CRY_T:.*]], %[[CRY_C:.*]] = mqtopt.ry({{.*}}) %[[CRX_C]] ctrl %[[CRX_T]] + // CHECK-SAME: : !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Reinsertion + // CHECK: memref.store {{.*}}, %[[ALLOC]][{{.*}}] : memref<2x!mqtopt.Qubit> + // CHECK: memref.store {{.*}}, %[[ALLOC]][{{.*}}] : memref<2x!mqtopt.Qubit> + // CHECK: memref.dealloc %[[ALLOC]] : memref<2x!mqtopt.Qubit> + """ + _run_filecheck(mlir_after_mqtopt, check_to_mqtopt, "Param: CatalystQuantum to MQTOpt") + + # Verify MQTOpt → CatalystQuantum conversion + check_to_catalyst = """ + // CHECK: func.func {{.*}}@circuit + // CHECK: %[[QREG:.*]] = quantum.alloc({{.*}}) : !quantum.reg + + // Q0 is extracted first for uncontrolled gates + // CHECK: %[[Q0:.*]] = quantum.extract %[[QREG]][{{.*}}] : !quantum.reg -> !quantum.bit + + // Uncontrolled parameterized gates + // CHECK: %[[RX:.*]] = quantum.custom "RX"({{.*}}) %[[Q0]] : !quantum.bit + // CHECK: %[[RY:.*]] = quantum.custom "RY"({{.*}}) %[[RX]] : !quantum.bit + // CHECK: %[[RZ:.*]] = quantum.custom "RZ"({{.*}}) %[[RY]] : !quantum.bit + // CHECK: %[[PS:.*]] = quantum.custom "PhaseShift"({{.*}}) %[[RZ]] : !quantum.bit + + // Q1 is extracted lazily right before controlled gates + // CHECK: %[[Q1:.*]] = quantum.extract %[[QREG]][{{.*}}] : !quantum.reg -> !quantum.bit + + // Controlled parameterized gates (qubits swap after each operation) + // CRX: target=%[[PS]], control=%[[Q1]] + // CHECK: %[[CRX_T:.*]], %[[CRX_C:.*]] = quantum.custom "CRX"({{.*}}) %[[PS]] ctrls(%[[Q1]]) + // CHECK-SAME: ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CRY: target=%[[CRX_C]] (previous control), control=%[[CRX_T]] (previous target) + // CHECK: quantum.custom "CRY"({{.*}}) %[[CRX_C]] ctrls(%[[CRX_T]]) ctrlvals(%true{{.*}}) + // CHECK-SAME: : !quantum.bit ctrls !quantum.bit + + // Reinsertion + // CHECK: quantum.insert %[[QREG]][{{.*}}], {{.*}} : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], {{.*}} : !quantum.reg, !quantum.bit + // CHECK: quantum.dealloc %[[QREG]] : !quantum.reg + """ + _run_filecheck(mlir_after_roundtrip, check_to_catalyst, "Param: MQTOpt to CatalystQuantum") + + +def test_entangling_gates_roundtrip() -> None: + """Test roundtrip conversion of entangling/permutation gates. + + Mirrors: quantum_entangling.mlir + Gates: SWAP, ISWAP, ISWAP†, ECR, and their controlled variants + + Raises: + FileNotFoundError: If intermediate MLIR files are not found + """ + + @apply_pass("mqt.mqtopt-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-mqtopt") + @qml.qnode(get_device("lightning.qubit", wires=3)) + def circuit() -> None: + # Uncontrolled permutation gates + qml.SWAP(wires=[0, 1]) + qml.ISWAP(wires=[0, 1]) + qml.adjoint(qml.ISWAP(wires=[0, 1])) + qml.ECR(wires=[0, 1]) + + # Controlled permutation gates + qml.CSWAP(wires=[2, 0, 1]) + qml.ctrl(qml.ISWAP(wires=[0, 1]), control=2) + qml.ctrl(qml.adjoint(qml.ISWAP(wires=[0, 1])), control=2) + qml.ctrl(qml.ECR(wires=[0, 1]), control=2) + + custom_pipeline = [ + ("to-mqtopt", ["builtin.module(catalystquantum-to-mqtopt)"]), + ("to-catalystquantum", ["builtin.module(mqtopt-to-catalystquantum)"]), + ] + + @qml.qjit(target="mlir", pipelines=custom_pipeline, autograph=True, keep_intermediate=2) + def module() -> None: + return circuit() + + # Verify the roundtrip completes successfully + mlir_opt = module.mlir_opt + assert mlir_opt + + # Find where MLIR files are generated (relative to cwd where pytest is run) + # Catalyst generates MLIR files in the current working directory + mlir_dir = Path.cwd() + + # Read the intermediate MLIR files + mlir_to_mqtopt = mlir_dir / "3_to-mqtopt.mlir" + mlir_to_catalyst = mlir_dir / "4_MQTOptToCatalystQuantum.mlir" + + if not mlir_to_mqtopt.exists() or not mlir_to_catalyst.exists(): + available_files = list(mlir_dir.glob("*.mlir")) + msg = f"Expected MLIR files not found in {mlir_dir}.\nAvailable files: {[f.name for f in available_files]}" + raise FileNotFoundError(msg) + + with Path(mlir_to_mqtopt).open("r", encoding="utf-8") as f: + mlir_after_mqtopt = f.read() + with Path(mlir_to_catalyst).open("r", encoding="utf-8") as f: + mlir_after_roundtrip = f.read() + + # Verify CatalystQuantum → MQTOpt conversion + check_to_mqtopt = """ + // CHECK: func.func {{.*}}@circuit + // CHECK: %[[ALLOC:.*]] = memref.alloc() : memref<3x!mqtopt.Qubit> + + // Qubits loaded as needed + // CHECK: %[[Q0:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<3x!mqtopt.Qubit> + // CHECK: %[[Q1:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<3x!mqtopt.Qubit> + + // Uncontrolled entangling gates (ISWAP/ECR get heavily decomposed, but SWAP should be visible) + // CHECK: mqtopt.swap({{.*}}) %[[Q0]], %[[Q1]] : !mqtopt.Qubit, !mqtopt.Qubit + + // After decompositions, Q2 is loaded for controlled gates + // CHECK: %[[Q2:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<3x!mqtopt.Qubit> + + // Controlled swap gate + // CHECK: mqtopt.swap({{.*}}) {{.*}} ctrl %[[Q2]] : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Reinsertion + // CHECK: memref.store {{.*}}, %[[ALLOC]][{{.*}}] : memref<3x!mqtopt.Qubit> + // CHECK: memref.store {{.*}}, %[[ALLOC]][{{.*}}] : memref<3x!mqtopt.Qubit> + // CHECK: memref.store {{.*}}, %[[ALLOC]][{{.*}}] : memref<3x!mqtopt.Qubit> + // CHECK: memref.dealloc %[[ALLOC]] : memref<3x!mqtopt.Qubit> + """ + _run_filecheck(mlir_after_mqtopt, check_to_mqtopt, "Entangling: CatalystQuantum to MQTOpt") + + # Verify MQTOpt → CatalystQuantum conversion (simplified - ISWAP/ECR are heavily decomposed) + check_to_catalyst = """ + // CHECK: func.func {{.*}}@circuit + // CHECK: %[[QREG:.*]] = quantum.alloc({{.*}}) : !quantum.reg + + // Qubits extracted as needed + // CHECK: %[[Q0:.*]] = quantum.extract %[[QREG]][{{.*}}] : !quantum.reg -> !quantum.bit + // CHECK: %[[Q1:.*]] = quantum.extract %[[QREG]][{{.*}}] : !quantum.reg -> !quantum.bit + + // SWAP is visible, but ISWAP/ECR/adjISWAP are heavily decomposed into primitives (H, S, CNOT, RZ, RY chains) + // CHECK: quantum.custom "SWAP"() %[[Q0]], %[[Q1]] : !quantum.bit, !quantum.bit + + // After all decompositions, Q2 extracted for controlled gates + // CHECK: %[[Q2:.*]] = quantum.extract %[[QREG]][{{.*}}] : !quantum.reg -> !quantum.bit + + // Controlled swap + // CHECK: quantum.custom "CSWAP"() {{.*}} ctrls(%[[Q2]]) ctrlvals( + + // Reinsertion + // CHECK: quantum.insert %[[QREG]][{{.*}}], {{.*}} : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], {{.*}} : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], {{.*}} : !quantum.reg, !quantum.bit + // CHECK: quantum.dealloc %[[QREG]] : !quantum.reg + """ + _run_filecheck(mlir_after_roundtrip, check_to_catalyst, "Entangling: MQTOpt to CatalystQuantum") + + +def test_ising_gates_roundtrip() -> None: + """Test roundtrip conversion of Ising-type gates. + + Mirrors: quantum_ising.mlir + Gates: IsingXY, IsingXX, IsingYY, IsingZZ, and their controlled variants + Note: IsingXY takes 2 parameters in MLIR (phi and beta) + + Raises: + FileNotFoundError: If intermediate MLIR files are not found + """ + + @apply_pass("mqt.mqtopt-to-catalystquantum") + @apply_pass("mqt.catalystquantum-to-mqtopt") + @qml.qnode(get_device("lightning.qubit", wires=3)) + def circuit() -> None: + angle = 0.3 + + # Uncontrolled Ising gates + qml.IsingXY(angle, wires=[0, 1]) + qml.IsingXX(angle, wires=[0, 1]) + qml.IsingYY(angle, wires=[0, 1]) + qml.IsingZZ(angle, wires=[0, 1]) + + # Controlled Ising gates + qml.ctrl(qml.IsingXY(angle, wires=[0, 1]), control=2) + qml.ctrl(qml.IsingXX(angle, wires=[0, 1]), control=2) + qml.ctrl(qml.IsingYY(angle, wires=[0, 1]), control=2) + qml.ctrl(qml.IsingZZ(angle, wires=[0, 1]), control=2) + + custom_pipeline = [ + ("to-mqtopt", ["builtin.module(catalystquantum-to-mqtopt)"]), + ("to-catalystquantum", ["builtin.module(mqtopt-to-catalystquantum)"]), + ] + + @qml.qjit(target="mlir", pipelines=custom_pipeline, autograph=True, keep_intermediate=2) + def module() -> None: + return circuit() + + # Verify the roundtrip completes successfully + mlir_opt = module.mlir_opt + assert mlir_opt + + # Find where MLIR files are generated (relative to cwd where pytest is run) + # Catalyst generates MLIR files in the current working directory + # This works regardless of where pytest is run from (locally or CI) + test_file_dir = Path(__file__).parent + mlir_dir = Path.cwd() + test_file_dir.parent / "Conversion" + + # Read the intermediate MLIR files + mlir_to_mqtopt = mlir_dir / "3_to-mqtopt.mlir" + mlir_to_catalyst = mlir_dir / "4_MQTOptToCatalystQuantum.mlir" + + if not mlir_to_mqtopt.exists() or not mlir_to_catalyst.exists(): + # Fallback: list what files actually exist for debugging + available_files = list(mlir_dir.glob("*.mlir")) + msg = f"Expected MLIR files not found in {mlir_dir}.\nAvailable files: {[f.name for f in available_files]}" + raise FileNotFoundError(msg) + + with Path(mlir_to_mqtopt).open("r", encoding="utf-8") as f: + mlir_after_mqtopt = f.read() + with Path(mlir_to_catalyst).open("r", encoding="utf-8") as f: + mlir_after_roundtrip = f.read() + + # Verify CatalystQuantum → MQTOpt conversion with FileCheck + check_to_mqtopt = """ + // CHECK: func.func {{.*}}@circuit + // CHECK: %[[ALLOC:.*]] = memref.alloc() : memref<3x!mqtopt.Qubit> + // CHECK: %[[Q0:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<3x!mqtopt.Qubit> + // CHECK: %[[Q1:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<3x!mqtopt.Qubit> + + // Uncontrolled Ising gates + // CHECK: %[[XY_OUT:.*]]:2 = mqtopt.xx_plus_yy({{.*}}) %[[Q0]], %[[Q1]] : !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[XX_OUT:.*]]:2 = mqtopt.rxx({{.*}}) %[[XY_OUT]]#0, %[[XY_OUT]]#1 : !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[YY_OUT:.*]]:2 = mqtopt.ryy({{.*}}) %[[XX_OUT]]#0, %[[XX_OUT]]#1 : !mqtopt.Qubit, !mqtopt.Qubit + // CHECK: %[[ZZ_OUT:.*]]:2 = mqtopt.rzz({{.*}}) %[[YY_OUT]]#0, %[[YY_OUT]]#1 : !mqtopt.Qubit, !mqtopt.Qubit + + // Controlled Ising gates (control qubit loaded here) + // CHECK: %[[Q2:.*]] = memref.load %[[ALLOC]][{{.*}}] : memref<3x!mqtopt.Qubit> + // CHECK: %[[CXY_OUT:.*]]:2, %[[CTRL1:.*]] = mqtopt.xx_plus_yy({{.*}}) %[[ZZ_OUT]]#0, %[[ZZ_OUT]]#1 ctrl %[[Q2]] + // CHECK-SAME: : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CXX_OUT:.*]]:2, %[[CTRL2:.*]] = mqtopt.rxx({{.*}}) %[[CXY_OUT]]#0, %[[CXY_OUT]]#1 ctrl %[[CTRL1]] + // CHECK-SAME: : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CYY_OUT:.*]]:2, %[[CTRL3:.*]] = mqtopt.ryy({{.*}}) %[[CXX_OUT]]#0, %[[CXX_OUT]]#1 ctrl %[[CTRL2]] + // CHECK-SAME: : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + // CHECK: %[[CZZ_OUT:.*]]:2, %[[CTRL4:.*]] = mqtopt.rzz({{.*}}) %[[CYY_OUT]]#0, %[[CYY_OUT]]#1 ctrl %[[CTRL3]] + // CHECK-SAME: : !mqtopt.Qubit, !mqtopt.Qubit ctrl !mqtopt.Qubit + + // Reinsertion + // CHECK: memref.store %[[CZZ_OUT]]#0, %[[ALLOC]][{{.*}}] : memref<3x!mqtopt.Qubit> + // CHECK: memref.store %[[CZZ_OUT]]#1, %[[ALLOC]][{{.*}}] : memref<3x!mqtopt.Qubit> + // CHECK: memref.store %[[CTRL4]], %[[ALLOC]][{{.*}}] : memref<3x!mqtopt.Qubit> + // CHECK: memref.dealloc %[[ALLOC]] : memref<3x!mqtopt.Qubit> + """ + _run_filecheck(mlir_after_mqtopt, check_to_mqtopt, "Ising: CatalystQuantum to MQTOpt") + + # Verify MQTOpt → CatalystQuantum conversion with FileCheck + # Based on mqtopt_ising.mlir reference test + check_to_catalyst = """ + // CHECK: func.func {{.*}}@circuit + // CHECK: %[[QREG:.*]] = quantum.alloc({{.*}}) : !quantum.reg + // CHECK: %[[Q0:.*]] = quantum.extract %[[QREG]][{{.*}}] : !quantum.reg -> !quantum.bit + // CHECK: %[[Q1:.*]] = quantum.extract %[[QREG]][{{.*}}] : !quantum.reg -> !quantum.bit + + // Uncontrolled Ising gates + // IsingXY is decomposed: RZ -> IsingXY -> RZ + // CHECK: %[[RZ0:.*]] = quantum.custom "RZ"({{.*}}) %[[Q1]] : !quantum.bit + // CHECK: %[[XY:.*]]:2 = quantum.custom "IsingXY"({{.*}}) %[[Q0]], %[[RZ0]] : !quantum.bit, !quantum.bit + // CHECK: %[[RZ1:.*]] = quantum.custom "RZ"({{.*}}) %[[XY]]#1 : !quantum.bit + + // IsingXX, IsingYY, IsingZZ gates + // CHECK: %[[XX:.*]]:2 = quantum.custom "IsingXX"({{.*}}) %[[XY]]#0, %[[RZ1]] : !quantum.bit, !quantum.bit + // CHECK: %[[YY:.*]]:2 = quantum.custom "IsingYY"({{.*}}) %[[XX]]#0, %[[XX]]#1 : !quantum.bit, !quantum.bit + // CHECK: %[[ZZ:.*]]:2 = quantum.custom "IsingZZ"({{.*}}) %[[YY]]#0, %[[YY]]#1 : !quantum.bit, !quantum.bit + + // Controlled Ising gates (with ctrls) + // Extract control qubit + // CHECK: %[[Q2:.*]] = quantum.extract %[[QREG]][{{.*}}] : !quantum.reg -> !quantum.bit + + // Controlled IsingXY: RZ(ctrl) -> IsingXY(ctrl) -> RZ(ctrl) + // CHECK: %[[CRZ0:.*]], %[[CTRL1:.*]] = quantum.custom "RZ"({{.*}}) %[[ZZ]]#1 ctrls(%[[Q2]]) + // CHECK-SAME: ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + // CHECK: %[[CXY:.*]]:2, %[[CTRL2:.*]] = quantum.custom "IsingXY"({{.*}}) %[[ZZ]]#0, %[[CRZ0]] + // CHECK-SAME: ctrls(%[[CTRL1]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[CRZ1:.*]], %[[CTRL3:.*]] = quantum.custom "RZ"({{.*}}) %[[CXY]]#1 ctrls(%[[CTRL2]]) + // CHECK-SAME: ctrlvals(%true{{.*}}) : !quantum.bit ctrls !quantum.bit + + // Controlled IsingXX, IsingYY, IsingZZ + // CHECK: %[[CXX:.*]]:2, %[[CTRL4:.*]] = quantum.custom "IsingXX"({{.*}}) %[[CXY]]#0, %[[CRZ1]] + // CHECK-SAME: ctrls(%[[CTRL3]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[CYY:.*]]:2, %[[CTRL5:.*]] = quantum.custom "IsingYY"({{.*}}) %[[CXX]]#0, %[[CXX]]#1 + // CHECK-SAME: ctrls(%[[CTRL4]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + // CHECK: %[[CZZ:.*]]:2, %[[CTRL6:.*]] = quantum.custom "IsingZZ"({{.*}}) %[[CYY]]#0, %[[CYY]]#1 + // CHECK-SAME: ctrls(%[[CTRL5]]) ctrlvals(%true{{.*}}) : !quantum.bit, !quantum.bit ctrls !quantum.bit + + // Reinsertion + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[CZZ]]#0 : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[CZZ]]#1 : !quantum.reg, !quantum.bit + // CHECK: quantum.insert %[[QREG]][{{.*}}], %[[CTRL6]] : !quantum.reg, !quantum.bit + // CHECK: quantum.dealloc %[[QREG]] : !quantum.reg + """ + _run_filecheck(mlir_after_roundtrip, check_to_catalyst, "Ising: MQTOpt to CatalystQuantum") + + # Remove all intermediate files created during the test + for mlir_file in mlir_dir.glob("*.mlir"): + mlir_file.unlink() + + +def test_mqtopt_roundtrip() -> None: + """Execute the full roundtrip including MQT Core IR. + + Executes the conversion passes to and from MQTOpt dialect AND + the roundtrip through MQT Core IR. + """ + + @apply_pass("mqt.mqtopt-to-catalystquantum") + @apply_pass("mqt.mqt-core-round-trip") + @apply_pass("mqt.catalystquantum-to-mqtopt") + @qml.qnode(get_device("lightning.qubit", wires=2)) + def circuit() -> None: + qml.Hadamard(wires=[0]) + qml.CNOT(wires=[0, 1]) + + @qml.qjit(target="mlir", autograph=True) + def module() -> None: + return circuit() + + # This will execute the pass and return the final MLIR + mlir_opt = module.mlir_opt + assert mlir_opt diff --git a/plugins/catalyst/test/python/test_plugin_setup.py b/plugins/catalyst/test/python/test_plugin_setup.py new file mode 100644 index 0000000000..85803948ef --- /dev/null +++ b/plugins/catalyst/test/python/test_plugin_setup.py @@ -0,0 +1,94 @@ +# Copyright (c) 2023 - 2025 Chair for Design Automation, TUM +# Copyright (c) 2025 Munich Quantum Software Company GmbH +# All rights reserved. +# +# SPDX-License-Identifier: MIT +# +# Licensed under the MIT License + +"""Tests for MQT plugin setup with PennyLane and Catalyst. + +These tests only check that the MQT plugin is correctly installed and +can be used in various ways with PennyLane (they do NOT execute any pass). +""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pennylane as qml +from catalyst import pipeline +from catalyst.passes import apply_pass, apply_pass_plugin + +from mqt.core.plugins.catalyst import get_catalyst_plugin_abs_path + +if TYPE_CHECKING: + from pennylane.measurements.state import StateMP + + +def test_mqt_plugin() -> None: + """Generate MLIR for the MQT plugin. + + Does not execute the pass. + """ + plugin_path = str(get_catalyst_plugin_abs_path()) + + @apply_pass("mqt-core-round-trip") + @qml.qnode(qml.device("null.qubit", wires=0)) + def qnode() -> StateMP: + return qml.state() + + @qml.qjit(pass_plugins={plugin_path}, dialect_plugins={plugin_path}, target="mlir") + def module() -> StateMP: + return qnode() + + assert "mqt-core-round-trip" in module.mlir + + +def test_mqt_plugin_no_preregistration() -> None: + """Generate MLIR for the MQT plugin. + + No need to register the plugin ahead of time in the qjit decorator. + """ + plugin_path = str(get_catalyst_plugin_abs_path()) + + @apply_pass_plugin(plugin_path, "mqt-core-round-trip") + @qml.qnode(qml.device("null.qubit", wires=0)) + def qnode() -> StateMP: + return qml.state() + + @qml.qjit(target="mlir") + def module() -> StateMP: + return qnode() + + assert "mqt-core-round-trip" in module.mlir + + +def test_mqt_entry_point() -> None: + """Generate MLIR for the MQT plugin via entry-point.""" + + @apply_pass("mqt.mqt-core-round-trip") + @qml.qnode(qml.device("null.qubit", wires=0)) + def qnode() -> StateMP: + return qml.state() + + @qml.qjit(target="mlir") + def module() -> StateMP: + return qnode() + + assert "mqt-core-round-trip" in module.mlir + + +def test_mqt_dictionary() -> None: + """Generate MLIR for the MQT plugin via entry-point.""" + + @pipeline({"mqt.mqt-core-round-trip": {}}) + @qml.qnode(qml.device("null.qubit", wires=0)) + def qnode() -> StateMP: + return qml.state() + + @qml.qjit(target="mlir") + def module() -> StateMP: + return qnode() + + assert "mqt-core-round-trip" in module.mlir diff --git a/plugins/catalyst/uv.lock b/plugins/catalyst/uv.lock new file mode 100644 index 0000000000..0919bbc3bf --- /dev/null +++ b/plugins/catalyst/uv.lock @@ -0,0 +1,998 @@ +version = 1 +revision = 2 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'linux'", + "python_full_version < '3.11' and sys_platform == 'linux'", +] +supported-markers = [ + "platform_machine == 'arm64' and sys_platform == 'darwin'", + "sys_platform == 'linux'", +] + +[[package]] +name = "appdirs" +version = "1.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470, upload-time = "2020-05-11T07:59:51.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566, upload-time = "2020-05-11T07:59:49.499Z" }, +] + +[[package]] +name = "astunparse" +version = "1.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "wheel", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/af/4182184d3c338792894f34a62672919db7ca008c89abee9b564dd34d8029/astunparse-1.6.3.tar.gz", hash = "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872", size = 18290, upload-time = "2019-12-22T18:12:13.129Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/03/13dde6512ad7b4557eb792fbcf0c653af6076b81e5941d36ec61f7ce6028/astunparse-1.6.3-py2.py3-none-any.whl", hash = "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8", size = 12732, upload-time = "2019-12-22T18:12:11.297Z" }, +] + +[[package]] +name = "autograd" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/67/1c/3c24ec03c8ba4decc742b1df5a10c52f98c84ca8797757f313e7bdcdf276/autograd-1.8.0.tar.gz", hash = "sha256:107374ded5b09fc8643ac925348c0369e7b0e73bbed9565ffd61b8fd04425683", size = 2562146, upload-time = "2025-05-05T12:49:02.502Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/ea/e16f0c423f7d83cf8b79cae9452040fb7b2e020c7439a167ee7c317de448/autograd-1.8.0-py3-none-any.whl", hash = "sha256:4ab9084294f814cf56c280adbe19612546a35574d67c574b04933c7d2ecb7d78", size = 51478, upload-time = "2025-05-05T12:49:00.585Z" }, +] + +[[package]] +name = "autoray" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/d7/133dd5a2837e0416375b7f059f0cf3b26d22a330405ca0bd8bda43c00278/autoray-0.7.2.tar.gz", hash = "sha256:7f8f375af12deea65736b65305b86eff2e11ebaf8c58d3e599752681772ff0d7", size = 1210729, upload-time = "2025-07-09T00:26:37.235Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/ca/00c61fcd5bb7ce1dfe3e9313febf2ba11fa17cf619676960f548a7c32586/autoray-0.7.2-py3-none-any.whl", hash = "sha256:f692f0f21a6f187ce877f3cc810f035d601ccd713beac02974811650bb60f580", size = 930824, upload-time = "2025-07-09T00:26:35.182Z" }, +] + +[[package]] +name = "cachetools" +version = "6.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/61/e4fad8155db4a04bfb4734c7c8ff0882f078f24294d42798b3568eb63bff/cachetools-6.2.0.tar.gz", hash = "sha256:38b328c0889450f05f5e120f56ab68c8abaf424e1275522b138ffc93253f7e32", size = 30988, upload-time = "2025-08-25T18:57:30.924Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/56/3124f61d37a7a4e7cc96afc5492c78ba0cb551151e530b54669ddd1436ef/cachetools-6.2.0-py3-none-any.whl", hash = "sha256:1c76a8960c0041fcc21097e357f882197c79da0dbff766e7317890a65d7d8ba6", size = 11276, upload-time = "2025-08-25T18:57:29.684Z" }, +] + +[[package]] +name = "certifi" +version = "2025.8.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/67/960ebe6bf230a96cda2e0abcf73af550ec4f090005363542f0765df162e0/certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407", size = 162386, upload-time = "2025-08-03T03:07:47.08Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/48/1549795ba7742c948d2ad169c1c8cdbae65bc450d6cd753d124b17c8cd32/certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5", size = 161216, upload-time = "2025-08-03T03:07:45.777Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/2d/5fd176ceb9b2fc619e63405525573493ca23441330fcdaee6bef9460e924/charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14", size = 122371, upload-time = "2025-08-09T07:57:28.46Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/98/f3b8013223728a99b908c9344da3aa04ee6e3fa235f19409033eda92fb78/charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72", size = 207695, upload-time = "2025-08-09T07:55:36.452Z" }, + { url = "https://files.pythonhosted.org/packages/21/40/5188be1e3118c82dcb7c2a5ba101b783822cfb413a0268ed3be0468532de/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe", size = 147153, upload-time = "2025-08-09T07:55:38.467Z" }, + { url = "https://files.pythonhosted.org/packages/37/60/5d0d74bc1e1380f0b72c327948d9c2aca14b46a9efd87604e724260f384c/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601", size = 160428, upload-time = "2025-08-09T07:55:40.072Z" }, + { url = "https://files.pythonhosted.org/packages/85/9a/d891f63722d9158688de58d050c59dc3da560ea7f04f4c53e769de5140f5/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c", size = 157627, upload-time = "2025-08-09T07:55:41.706Z" }, + { url = "https://files.pythonhosted.org/packages/65/1a/7425c952944a6521a9cfa7e675343f83fd82085b8af2b1373a2409c683dc/charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2", size = 152388, upload-time = "2025-08-09T07:55:43.262Z" }, + { url = "https://files.pythonhosted.org/packages/f0/c9/a2c9c2a355a8594ce2446085e2ec97fd44d323c684ff32042e2a6b718e1d/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0", size = 150077, upload-time = "2025-08-09T07:55:44.903Z" }, + { url = "https://files.pythonhosted.org/packages/3b/38/20a1f44e4851aa1c9105d6e7110c9d020e093dfa5836d712a5f074a12bf7/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0", size = 161631, upload-time = "2025-08-09T07:55:46.346Z" }, + { url = "https://files.pythonhosted.org/packages/a4/fa/384d2c0f57edad03d7bec3ebefb462090d8905b4ff5a2d2525f3bb711fac/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0", size = 159210, upload-time = "2025-08-09T07:55:47.539Z" }, + { url = "https://files.pythonhosted.org/packages/33/9e/eca49d35867ca2db336b6ca27617deed4653b97ebf45dfc21311ce473c37/charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a", size = 153739, upload-time = "2025-08-09T07:55:48.744Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b5/991245018615474a60965a7c9cd2b4efbaabd16d582a5547c47ee1c7730b/charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b", size = 204483, upload-time = "2025-08-09T07:55:53.12Z" }, + { url = "https://files.pythonhosted.org/packages/c7/2a/ae245c41c06299ec18262825c1569c5d3298fc920e4ddf56ab011b417efd/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64", size = 145520, upload-time = "2025-08-09T07:55:54.712Z" }, + { url = "https://files.pythonhosted.org/packages/3a/a4/b3b6c76e7a635748c4421d2b92c7b8f90a432f98bda5082049af37ffc8e3/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91", size = 158876, upload-time = "2025-08-09T07:55:56.024Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e6/63bb0e10f90a8243c5def74b5b105b3bbbfb3e7bb753915fe333fb0c11ea/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f", size = 156083, upload-time = "2025-08-09T07:55:57.582Z" }, + { url = "https://files.pythonhosted.org/packages/87/df/b7737ff046c974b183ea9aa111b74185ac8c3a326c6262d413bd5a1b8c69/charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07", size = 150295, upload-time = "2025-08-09T07:55:59.147Z" }, + { url = "https://files.pythonhosted.org/packages/61/f1/190d9977e0084d3f1dc169acd060d479bbbc71b90bf3e7bf7b9927dec3eb/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30", size = 148379, upload-time = "2025-08-09T07:56:00.364Z" }, + { url = "https://files.pythonhosted.org/packages/4c/92/27dbe365d34c68cfe0ca76f1edd70e8705d82b378cb54ebbaeabc2e3029d/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14", size = 160018, upload-time = "2025-08-09T07:56:01.678Z" }, + { url = "https://files.pythonhosted.org/packages/99/04/baae2a1ea1893a01635d475b9261c889a18fd48393634b6270827869fa34/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c", size = 157430, upload-time = "2025-08-09T07:56:02.87Z" }, + { url = "https://files.pythonhosted.org/packages/2f/36/77da9c6a328c54d17b960c89eccacfab8271fdaaa228305330915b88afa9/charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae", size = 151600, upload-time = "2025-08-09T07:56:04.089Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5e/14c94999e418d9b87682734589404a25854d5f5d0408df68bc15b6ff54bb/charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1", size = 205655, upload-time = "2025-08-09T07:56:08.475Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a8/c6ec5d389672521f644505a257f50544c074cf5fc292d5390331cd6fc9c3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884", size = 146223, upload-time = "2025-08-09T07:56:09.708Z" }, + { url = "https://files.pythonhosted.org/packages/fc/eb/a2ffb08547f4e1e5415fb69eb7db25932c52a52bed371429648db4d84fb1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018", size = 159366, upload-time = "2025-08-09T07:56:11.326Z" }, + { url = "https://files.pythonhosted.org/packages/82/10/0fd19f20c624b278dddaf83b8464dcddc2456cb4b02bb902a6da126b87a1/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392", size = 157104, upload-time = "2025-08-09T07:56:13.014Z" }, + { url = "https://files.pythonhosted.org/packages/16/ab/0233c3231af734f5dfcf0844aa9582d5a1466c985bbed6cedab85af9bfe3/charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f", size = 151830, upload-time = "2025-08-09T07:56:14.428Z" }, + { url = "https://files.pythonhosted.org/packages/ae/02/e29e22b4e02839a0e4a06557b1999d0a47db3567e82989b5bb21f3fbbd9f/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154", size = 148854, upload-time = "2025-08-09T07:56:16.051Z" }, + { url = "https://files.pythonhosted.org/packages/05/6b/e2539a0a4be302b481e8cafb5af8792da8093b486885a1ae4d15d452bcec/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491", size = 160670, upload-time = "2025-08-09T07:56:17.314Z" }, + { url = "https://files.pythonhosted.org/packages/31/e7/883ee5676a2ef217a40ce0bffcc3d0dfbf9e64cbcfbdf822c52981c3304b/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93", size = 158501, upload-time = "2025-08-09T07:56:18.641Z" }, + { url = "https://files.pythonhosted.org/packages/c1/35/6525b21aa0db614cf8b5792d232021dca3df7f90a1944db934efa5d20bb1/charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f", size = 153173, upload-time = "2025-08-09T07:56:20.289Z" }, + { url = "https://files.pythonhosted.org/packages/65/ca/2135ac97709b400c7654b4b764daf5c5567c2da45a30cdd20f9eefe2d658/charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe", size = 205326, upload-time = "2025-08-09T07:56:24.721Z" }, + { url = "https://files.pythonhosted.org/packages/71/11/98a04c3c97dd34e49c7d247083af03645ca3730809a5509443f3c37f7c99/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8", size = 146008, upload-time = "2025-08-09T07:56:26.004Z" }, + { url = "https://files.pythonhosted.org/packages/60/f5/4659a4cb3c4ec146bec80c32d8bb16033752574c20b1252ee842a95d1a1e/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9", size = 159196, upload-time = "2025-08-09T07:56:27.25Z" }, + { url = "https://files.pythonhosted.org/packages/86/9e/f552f7a00611f168b9a5865a1414179b2c6de8235a4fa40189f6f79a1753/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31", size = 156819, upload-time = "2025-08-09T07:56:28.515Z" }, + { url = "https://files.pythonhosted.org/packages/7e/95/42aa2156235cbc8fa61208aded06ef46111c4d3f0de233107b3f38631803/charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f", size = 151350, upload-time = "2025-08-09T07:56:29.716Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a9/3865b02c56f300a6f94fc631ef54f0a8a29da74fb45a773dfd3dcd380af7/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927", size = 148644, upload-time = "2025-08-09T07:56:30.984Z" }, + { url = "https://files.pythonhosted.org/packages/77/d9/cbcf1a2a5c7d7856f11e7ac2d782aec12bdfea60d104e60e0aa1c97849dc/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9", size = 160468, upload-time = "2025-08-09T07:56:32.252Z" }, + { url = "https://files.pythonhosted.org/packages/f6/42/6f45efee8697b89fda4d50580f292b8f7f9306cb2971d4b53f8914e4d890/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5", size = 158187, upload-time = "2025-08-09T07:56:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/70/99/f1c3bdcfaa9c45b3ce96f70b14f070411366fa19549c1d4832c935d8e2c3/charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc", size = 152699, upload-time = "2025-08-09T07:56:34.739Z" }, + { url = "https://files.pythonhosted.org/packages/8e/91/b5a06ad970ddc7a0e513112d40113e834638f4ca1120eb727a249fb2715e/charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15", size = 204342, upload-time = "2025-08-09T07:56:38.687Z" }, + { url = "https://files.pythonhosted.org/packages/ce/ec/1edc30a377f0a02689342f214455c3f6c2fbedd896a1d2f856c002fc3062/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db", size = 145995, upload-time = "2025-08-09T07:56:40.048Z" }, + { url = "https://files.pythonhosted.org/packages/17/e5/5e67ab85e6d22b04641acb5399c8684f4d37caf7558a53859f0283a650e9/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d", size = 158640, upload-time = "2025-08-09T07:56:41.311Z" }, + { url = "https://files.pythonhosted.org/packages/f1/e5/38421987f6c697ee3722981289d554957c4be652f963d71c5e46a262e135/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096", size = 156636, upload-time = "2025-08-09T07:56:43.195Z" }, + { url = "https://files.pythonhosted.org/packages/a0/e4/5a075de8daa3ec0745a9a3b54467e0c2967daaaf2cec04c845f73493e9a1/charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa", size = 150939, upload-time = "2025-08-09T07:56:44.819Z" }, + { url = "https://files.pythonhosted.org/packages/02/f7/3611b32318b30974131db62b4043f335861d4d9b49adc6d57c1149cc49d4/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049", size = 148580, upload-time = "2025-08-09T07:56:46.684Z" }, + { url = "https://files.pythonhosted.org/packages/7e/61/19b36f4bd67f2793ab6a99b979b4e4f3d8fc754cbdffb805335df4337126/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0", size = 159870, upload-time = "2025-08-09T07:56:47.941Z" }, + { url = "https://files.pythonhosted.org/packages/06/57/84722eefdd338c04cf3030ada66889298eaedf3e7a30a624201e0cbe424a/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92", size = 157797, upload-time = "2025-08-09T07:56:49.756Z" }, + { url = "https://files.pythonhosted.org/packages/72/2a/aff5dd112b2f14bcc3462c312dce5445806bfc8ab3a7328555da95330e4b/charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16", size = 152224, upload-time = "2025-08-09T07:56:51.369Z" }, + { url = "https://files.pythonhosted.org/packages/8a/1f/f041989e93b001bc4e44bb1669ccdcf54d3f00e628229a85b08d330615c5/charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a", size = 53175, upload-time = "2025-08-09T07:57:26.864Z" }, +] + +[[package]] +name = "coverage" +version = "7.10.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/26/d22c300112504f5f9a9fd2297ce33c35f3d353e4aeb987c8419453b2a7c2/coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239", size = 827704, upload-time = "2025-09-21T20:03:56.815Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/6c/3a3f7a46888e69d18abe3ccc6fe4cb16cccb1e6a2f99698931dafca489e6/coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a", size = 217987, upload-time = "2025-09-21T20:00:57.218Z" }, + { url = "https://files.pythonhosted.org/packages/03/94/952d30f180b1a916c11a56f5c22d3535e943aa22430e9e3322447e520e1c/coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5", size = 218388, upload-time = "2025-09-21T20:01:00.081Z" }, + { url = "https://files.pythonhosted.org/packages/50/2b/9e0cf8ded1e114bcd8b2fd42792b57f1c4e9e4ea1824cde2af93a67305be/coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17", size = 245148, upload-time = "2025-09-21T20:01:01.768Z" }, + { url = "https://files.pythonhosted.org/packages/19/20/d0384ac06a6f908783d9b6aa6135e41b093971499ec488e47279f5b846e6/coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b", size = 246958, upload-time = "2025-09-21T20:01:03.355Z" }, + { url = "https://files.pythonhosted.org/packages/60/83/5c283cff3d41285f8eab897651585db908a909c572bdc014bcfaf8a8b6ae/coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87", size = 248819, upload-time = "2025-09-21T20:01:04.968Z" }, + { url = "https://files.pythonhosted.org/packages/60/22/02eb98fdc5ff79f423e990d877693e5310ae1eab6cb20ae0b0b9ac45b23b/coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e", size = 245754, upload-time = "2025-09-21T20:01:06.321Z" }, + { url = "https://files.pythonhosted.org/packages/b4/bc/25c83bcf3ad141b32cd7dc45485ef3c01a776ca3aa8ef0a93e77e8b5bc43/coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e", size = 246860, upload-time = "2025-09-21T20:01:07.605Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b7/95574702888b58c0928a6e982038c596f9c34d52c5e5107f1eef729399b5/coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df", size = 244877, upload-time = "2025-09-21T20:01:08.829Z" }, + { url = "https://files.pythonhosted.org/packages/47/b6/40095c185f235e085df0e0b158f6bd68cc6e1d80ba6c7721dc81d97ec318/coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0", size = 245108, upload-time = "2025-09-21T20:01:10.527Z" }, + { url = "https://files.pythonhosted.org/packages/c8/50/4aea0556da7a4b93ec9168420d170b55e2eb50ae21b25062513d020c6861/coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13", size = 245752, upload-time = "2025-09-21T20:01:11.857Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5d/c1a17867b0456f2e9ce2d8d4708a4c3a089947d0bec9c66cdf60c9e7739f/coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59", size = 218102, upload-time = "2025-09-21T20:01:16.089Z" }, + { url = "https://files.pythonhosted.org/packages/54/f0/514dcf4b4e3698b9a9077f084429681bf3aad2b4a72578f89d7f643eb506/coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a", size = 218505, upload-time = "2025-09-21T20:01:17.788Z" }, + { url = "https://files.pythonhosted.org/packages/20/f6/9626b81d17e2a4b25c63ac1b425ff307ecdeef03d67c9a147673ae40dc36/coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699", size = 248898, upload-time = "2025-09-21T20:01:19.488Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ef/bd8e719c2f7417ba03239052e099b76ea1130ac0cbb183ee1fcaa58aaff3/coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d", size = 250831, upload-time = "2025-09-21T20:01:20.817Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b6/bf054de41ec948b151ae2b79a55c107f5760979538f5fb80c195f2517718/coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e", size = 252937, upload-time = "2025-09-21T20:01:22.171Z" }, + { url = "https://files.pythonhosted.org/packages/0f/e5/3860756aa6f9318227443c6ce4ed7bf9e70bb7f1447a0353f45ac5c7974b/coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23", size = 249021, upload-time = "2025-09-21T20:01:23.907Z" }, + { url = "https://files.pythonhosted.org/packages/26/0f/bd08bd042854f7fd07b45808927ebcce99a7ed0f2f412d11629883517ac2/coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab", size = 250626, upload-time = "2025-09-21T20:01:25.721Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a7/4777b14de4abcc2e80c6b1d430f5d51eb18ed1d75fca56cbce5f2db9b36e/coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82", size = 248682, upload-time = "2025-09-21T20:01:27.105Z" }, + { url = "https://files.pythonhosted.org/packages/34/72/17d082b00b53cd45679bad682fac058b87f011fd8b9fe31d77f5f8d3a4e4/coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2", size = 248402, upload-time = "2025-09-21T20:01:28.629Z" }, + { url = "https://files.pythonhosted.org/packages/81/7a/92367572eb5bdd6a84bfa278cc7e97db192f9f45b28c94a9ca1a921c3577/coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61", size = 249320, upload-time = "2025-09-21T20:01:30.004Z" }, + { url = "https://files.pythonhosted.org/packages/13/e4/eb12450f71b542a53972d19117ea5a5cea1cab3ac9e31b0b5d498df1bd5a/coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417", size = 218290, upload-time = "2025-09-21T20:01:36.455Z" }, + { url = "https://files.pythonhosted.org/packages/37/66/593f9be12fc19fb36711f19a5371af79a718537204d16ea1d36f16bd78d2/coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973", size = 218515, upload-time = "2025-09-21T20:01:37.982Z" }, + { url = "https://files.pythonhosted.org/packages/66/80/4c49f7ae09cafdacc73fbc30949ffe77359635c168f4e9ff33c9ebb07838/coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c", size = 250020, upload-time = "2025-09-21T20:01:39.617Z" }, + { url = "https://files.pythonhosted.org/packages/a6/90/a64aaacab3b37a17aaedd83e8000142561a29eb262cede42d94a67f7556b/coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7", size = 252769, upload-time = "2025-09-21T20:01:41.341Z" }, + { url = "https://files.pythonhosted.org/packages/98/2e/2dda59afd6103b342e096f246ebc5f87a3363b5412609946c120f4e7750d/coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6", size = 253901, upload-time = "2025-09-21T20:01:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/dc/8d8119c9051d50f3119bb4a75f29f1e4a6ab9415cd1fa8bf22fcc3fb3b5f/coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59", size = 250413, upload-time = "2025-09-21T20:01:44.469Z" }, + { url = "https://files.pythonhosted.org/packages/98/b3/edaff9c5d79ee4d4b6d3fe046f2b1d799850425695b789d491a64225d493/coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b", size = 251820, upload-time = "2025-09-21T20:01:45.915Z" }, + { url = "https://files.pythonhosted.org/packages/11/25/9a0728564bb05863f7e513e5a594fe5ffef091b325437f5430e8cfb0d530/coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a", size = 249941, upload-time = "2025-09-21T20:01:47.296Z" }, + { url = "https://files.pythonhosted.org/packages/e0/fd/ca2650443bfbef5b0e74373aac4df67b08180d2f184b482c41499668e258/coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb", size = 249519, upload-time = "2025-09-21T20:01:48.73Z" }, + { url = "https://files.pythonhosted.org/packages/24/79/f692f125fb4299b6f963b0745124998ebb8e73ecdfce4ceceb06a8c6bec5/coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1", size = 251375, upload-time = "2025-09-21T20:01:50.529Z" }, + { url = "https://files.pythonhosted.org/packages/9a/94/b765c1abcb613d103b64fcf10395f54d69b0ef8be6a0dd9c524384892cc7/coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d", size = 218320, upload-time = "2025-09-21T20:01:56.629Z" }, + { url = "https://files.pythonhosted.org/packages/72/4f/732fff31c119bb73b35236dd333030f32c4bfe909f445b423e6c7594f9a2/coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b", size = 218575, upload-time = "2025-09-21T20:01:58.203Z" }, + { url = "https://files.pythonhosted.org/packages/87/02/ae7e0af4b674be47566707777db1aa375474f02a1d64b9323e5813a6cdd5/coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e", size = 249568, upload-time = "2025-09-21T20:01:59.748Z" }, + { url = "https://files.pythonhosted.org/packages/a2/77/8c6d22bf61921a59bce5471c2f1f7ac30cd4ac50aadde72b8c48d5727902/coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b", size = 252174, upload-time = "2025-09-21T20:02:01.192Z" }, + { url = "https://files.pythonhosted.org/packages/b1/20/b6ea4f69bbb52dac0aebd62157ba6a9dddbfe664f5af8122dac296c3ee15/coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49", size = 253447, upload-time = "2025-09-21T20:02:02.701Z" }, + { url = "https://files.pythonhosted.org/packages/f9/28/4831523ba483a7f90f7b259d2018fef02cb4d5b90bc7c1505d6e5a84883c/coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911", size = 249779, upload-time = "2025-09-21T20:02:04.185Z" }, + { url = "https://files.pythonhosted.org/packages/a7/9f/4331142bc98c10ca6436d2d620c3e165f31e6c58d43479985afce6f3191c/coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0", size = 251604, upload-time = "2025-09-21T20:02:06.034Z" }, + { url = "https://files.pythonhosted.org/packages/ce/60/bda83b96602036b77ecf34e6393a3836365481b69f7ed7079ab85048202b/coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f", size = 249497, upload-time = "2025-09-21T20:02:07.619Z" }, + { url = "https://files.pythonhosted.org/packages/5f/af/152633ff35b2af63977edd835d8e6430f0caef27d171edf2fc76c270ef31/coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c", size = 249350, upload-time = "2025-09-21T20:02:10.34Z" }, + { url = "https://files.pythonhosted.org/packages/9d/71/d92105d122bd21cebba877228990e1646d862e34a98bb3374d3fece5a794/coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f", size = 251111, upload-time = "2025-09-21T20:02:12.122Z" }, + { url = "https://files.pythonhosted.org/packages/bb/22/e04514bf2a735d8b0add31d2b4ab636fc02370730787c576bb995390d2d5/coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c", size = 219029, upload-time = "2025-09-21T20:02:18.936Z" }, + { url = "https://files.pythonhosted.org/packages/11/0b/91128e099035ece15da3445d9015e4b4153a6059403452d324cbb0a575fa/coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15", size = 219259, upload-time = "2025-09-21T20:02:20.44Z" }, + { url = "https://files.pythonhosted.org/packages/8b/51/66420081e72801536a091a0c8f8c1f88a5c4bf7b9b1bdc6222c7afe6dc9b/coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4", size = 260592, upload-time = "2025-09-21T20:02:22.313Z" }, + { url = "https://files.pythonhosted.org/packages/5d/22/9b8d458c2881b22df3db5bb3e7369e63d527d986decb6c11a591ba2364f7/coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0", size = 262768, upload-time = "2025-09-21T20:02:24.287Z" }, + { url = "https://files.pythonhosted.org/packages/f7/08/16bee2c433e60913c610ea200b276e8eeef084b0d200bdcff69920bd5828/coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0", size = 264995, upload-time = "2025-09-21T20:02:26.133Z" }, + { url = "https://files.pythonhosted.org/packages/20/9d/e53eb9771d154859b084b90201e5221bca7674ba449a17c101a5031d4054/coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65", size = 259546, upload-time = "2025-09-21T20:02:27.716Z" }, + { url = "https://files.pythonhosted.org/packages/ad/b0/69bc7050f8d4e56a89fb550a1577d5d0d1db2278106f6f626464067b3817/coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541", size = 262544, upload-time = "2025-09-21T20:02:29.216Z" }, + { url = "https://files.pythonhosted.org/packages/ef/4b/2514b060dbd1bc0aaf23b852c14bb5818f244c664cb16517feff6bb3a5ab/coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6", size = 260308, upload-time = "2025-09-21T20:02:31.226Z" }, + { url = "https://files.pythonhosted.org/packages/54/78/7ba2175007c246d75e496f64c06e94122bdb914790a1285d627a918bd271/coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999", size = 258920, upload-time = "2025-09-21T20:02:32.823Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b3/fac9f7abbc841409b9a410309d73bfa6cfb2e51c3fada738cb607ce174f8/coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2", size = 261434, upload-time = "2025-09-21T20:02:34.86Z" }, + { url = "https://files.pythonhosted.org/packages/23/9c/5844ab4ca6a4dd97a1850e030a15ec7d292b5c5cb93082979225126e35dd/coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520", size = 218302, upload-time = "2025-09-21T20:02:42.527Z" }, + { url = "https://files.pythonhosted.org/packages/f0/89/673f6514b0961d1f0e20ddc242e9342f6da21eaba3489901b565c0689f34/coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32", size = 218578, upload-time = "2025-09-21T20:02:44.468Z" }, + { url = "https://files.pythonhosted.org/packages/05/e8/261cae479e85232828fb17ad536765c88dd818c8470aca690b0ac6feeaa3/coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f", size = 249629, upload-time = "2025-09-21T20:02:46.503Z" }, + { url = "https://files.pythonhosted.org/packages/82/62/14ed6546d0207e6eda876434e3e8475a3e9adbe32110ce896c9e0c06bb9a/coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a", size = 252162, upload-time = "2025-09-21T20:02:48.689Z" }, + { url = "https://files.pythonhosted.org/packages/ff/49/07f00db9ac6478e4358165a08fb41b469a1b053212e8a00cb02f0d27a05f/coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360", size = 253517, upload-time = "2025-09-21T20:02:50.31Z" }, + { url = "https://files.pythonhosted.org/packages/a2/59/c5201c62dbf165dfbc91460f6dbbaa85a8b82cfa6131ac45d6c1bfb52deb/coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69", size = 249632, upload-time = "2025-09-21T20:02:51.971Z" }, + { url = "https://files.pythonhosted.org/packages/07/ae/5920097195291a51fb00b3a70b9bbd2edbfe3c84876a1762bd1ef1565ebc/coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14", size = 251520, upload-time = "2025-09-21T20:02:53.858Z" }, + { url = "https://files.pythonhosted.org/packages/b9/3c/a815dde77a2981f5743a60b63df31cb322c944843e57dbd579326625a413/coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe", size = 249455, upload-time = "2025-09-21T20:02:55.807Z" }, + { url = "https://files.pythonhosted.org/packages/aa/99/f5cdd8421ea656abefb6c0ce92556709db2265c41e8f9fc6c8ae0f7824c9/coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e", size = 249287, upload-time = "2025-09-21T20:02:57.784Z" }, + { url = "https://files.pythonhosted.org/packages/c3/7a/e9a2da6a1fc5d007dd51fca083a663ab930a8c4d149c087732a5dbaa0029/coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd", size = 250946, upload-time = "2025-09-21T20:02:59.431Z" }, + { url = "https://files.pythonhosted.org/packages/62/09/9a5608d319fa3eba7a2019addeacb8c746fb50872b57a724c9f79f146969/coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63", size = 219047, upload-time = "2025-09-21T20:03:06.795Z" }, + { url = "https://files.pythonhosted.org/packages/f5/6f/f58d46f33db9f2e3647b2d0764704548c184e6f5e014bef528b7f979ef84/coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2", size = 219266, upload-time = "2025-09-21T20:03:08.495Z" }, + { url = "https://files.pythonhosted.org/packages/74/5c/183ffc817ba68e0b443b8c934c8795553eb0c14573813415bd59941ee165/coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d", size = 260767, upload-time = "2025-09-21T20:03:10.172Z" }, + { url = "https://files.pythonhosted.org/packages/0f/48/71a8abe9c1ad7e97548835e3cc1adbf361e743e9d60310c5f75c9e7bf847/coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0", size = 262931, upload-time = "2025-09-21T20:03:11.861Z" }, + { url = "https://files.pythonhosted.org/packages/84/fd/193a8fb132acfc0a901f72020e54be5e48021e1575bb327d8ee1097a28fd/coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699", size = 265186, upload-time = "2025-09-21T20:03:13.539Z" }, + { url = "https://files.pythonhosted.org/packages/b1/8f/74ecc30607dd95ad50e3034221113ccb1c6d4e8085cc761134782995daae/coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9", size = 259470, upload-time = "2025-09-21T20:03:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/0f/55/79ff53a769f20d71b07023ea115c9167c0bb56f281320520cf64c5298a96/coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f", size = 262626, upload-time = "2025-09-21T20:03:17.673Z" }, + { url = "https://files.pythonhosted.org/packages/88/e2/dac66c140009b61ac3fc13af673a574b00c16efdf04f9b5c740703e953c0/coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1", size = 260386, upload-time = "2025-09-21T20:03:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/a2/f1/f48f645e3f33bb9ca8a496bc4a9671b52f2f353146233ebd7c1df6160440/coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0", size = 258852, upload-time = "2025-09-21T20:03:21.007Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3b/8442618972c51a7affeead957995cfa8323c0c9bcf8fa5a027421f720ff4/coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399", size = 261534, upload-time = "2025-09-21T20:03:23.12Z" }, + { url = "https://files.pythonhosted.org/packages/ec/16/114df1c291c22cac3b0c127a73e0af5c12ed7bbb6558d310429a0ae24023/coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260", size = 209952, upload-time = "2025-09-21T20:03:53.918Z" }, +] + +[package.optional-dependencies] +toml = [ + { name = "tomli", marker = "(python_full_version <= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version <= '3.11' and sys_platform == 'linux')" }, +] + +[[package]] +name = "diastatic-malt" +version = "2.15.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "astunparse", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "gast", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "termcolor", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/27/c2f011f2db21317066831ed026a95463d5ae62acce3f044f63d4ea6ab3a9/diastatic-malt-2.15.2.tar.gz", hash = "sha256:7eb90d8c30b7ff16b4e84c3a65de2ff7f5b7b9d0f5cdea23918e747ff7fb5320", size = 115044, upload-time = "2024-07-15T18:13:05.964Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/73/785c03860b1106f8f0ffbc69f2521bde1b58545114970cbbd3540d7f5434/diastatic_malt-2.15.2-py3-none-any.whl", hash = "sha256:85429257b356030f101c31b2c7d506c4829f21bd865aed796766f900d7908407", size = 167919, upload-time = "2024-07-15T18:12:57.707Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "(python_full_version < '3.12' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.12' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, +] + +[[package]] +name = "gast" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3c/14/c566f5ca00c115db7725263408ff952b8ae6d6a4e792ef9c84e77d9af7a1/gast-0.6.0.tar.gz", hash = "sha256:88fc5300d32c7ac6ca7b515310862f71e6fdf2c029bbec7c66c0f5dd47b6b1fb", size = 27708, upload-time = "2024-06-27T20:31:49.527Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/61/8001b38461d751cd1a0c3a6ae84346796a5758123f3ed97a1b121dfbf4f3/gast-0.6.0-py3-none-any.whl", hash = "sha256:52b182313f7330389f72b069ba00f174cfe2a06411099547288839c6cbafbd54", size = 21173, upload-time = "2024-07-09T13:15:15.615Z" }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, +] + +[[package]] +name = "jax" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jaxlib", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "ml-dtypes", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux')" }, + { name = "opt-einsum", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, + { name = "scipy", version = "1.16.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8e/ab/1229e0e027df6ab9e1d343f2900ab2fcca41bfde688df3b26600feb87e59/jax-0.6.0.tar.gz", hash = "sha256:abc690c530349ce470eeef92e09a7bd8a0460424b4980bc72feea45332a636bf", size = 2016538, upload-time = "2025-04-17T00:02:27.861Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/25/32c5e2c919da4faaea9ef5088437ab6e01738c49402e4ec8a6c7b49e30ef/jax-0.6.0-py3-none-any.whl", hash = "sha256:22b21827597c6d6b46e88543b4fc372fcddf1cc1247660452de020cc4bda1afc", size = 2338416, upload-time = "2025-04-17T00:00:18.542Z" }, +] + +[[package]] +name = "jaxlib" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ml-dtypes", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux')" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, + { name = "scipy", version = "1.16.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux')" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/8a/9ebd3f8b8d49a730c7142a1a294711c3251bd40d92251c0615bd3b862ed8/jaxlib-0.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:64a82f8eb40fdb7ba1d46ef907300d42e4f98cbda9602a2ed8e70db1a9ac4a60", size = 53464347, upload-time = "2025-04-17T00:00:56.668Z" }, + { url = "https://files.pythonhosted.org/packages/21/27/63a7fe1d2283026ee04981aa7b72ee6f9b982dc5e0794e15259b0c843979/jaxlib-0.6.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:541a418b98b28df5bd3a1e93c62b2d3f64d44b0c70b7b608f7fe2b4aa452b2af", size = 77470946, upload-time = "2025-04-17T00:01:01.4Z" }, + { url = "https://files.pythonhosted.org/packages/7c/16/30adb773e3db553ac9a8fa6688e3f65f7ccc39798088ba4cf77e9d9314c3/jaxlib-0.6.0-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:a4d4254c713388887a321379d3c5b1a20213a8dcdc903faf15139ba81e3ecd61", size = 87767242, upload-time = "2025-04-17T00:01:06.52Z" }, + { url = "https://files.pythonhosted.org/packages/38/13/f072d016428b3abccdce09fe9154f7ddb8008fbc74e977f122988b177b69/jaxlib-0.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef163cf07de00bc5690169e97fafaadc378f1c381f0287e8a473e78ab5bab1b5", size = 53466890, upload-time = "2025-04-17T00:01:14.981Z" }, + { url = "https://files.pythonhosted.org/packages/42/ad/e7e580d0c3d8da64d610934342ac71c984e24d561cb97390ee05f51208b0/jaxlib-0.6.0-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:c0ae959899802e1329cc8ec5a2b4d4be9a076b5beb2052eb49ba37514e623ebc", size = 77475513, upload-time = "2025-04-17T00:01:19.68Z" }, + { url = "https://files.pythonhosted.org/packages/f9/d3/da7e18974de849093047251052d64228ab895ec0a2b12390f5a2dc7172d5/jaxlib-0.6.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:bed45525e3bb5ec08630bfd207c09af9d62e9ff13f5f07c2ee2cfd8ed8411ba1", size = 87767967, upload-time = "2025-04-17T00:01:25.582Z" }, + { url = "https://files.pythonhosted.org/packages/7d/f4/ab3b96c6f2fb6a4c83bce6b49ccae760a8c53ddb788e8a334bf9124d2607/jaxlib-0.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7e3ce2ef0edc9b48b36e2704c36181f1ece7a12ac114df753db4286ea2c6e8b8", size = 53481042, upload-time = "2025-04-17T00:01:35.765Z" }, + { url = "https://files.pythonhosted.org/packages/90/af/d6ef7f3aba000446a8829510d4015d6757a8fb67638197de625ed71c9a19/jaxlib-0.6.0-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:2536fa93ec148d5016da8b2077ba66325b0d86aae2289a61c126877f042b3d1c", size = 77475532, upload-time = "2025-04-17T00:01:40.398Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c4/1a252f12e3ed0a3986cfb36bbdc38ff33e358f58ee7a0ddb5d6bb126cb31/jaxlib-0.6.0-cp312-cp312-manylinux2014_x86_64.whl", hash = "sha256:b6d85b8d1fd79248b04503517201e72fcbcd3980cf791d37e814709ea50a3c82", size = 87788826, upload-time = "2025-04-17T00:01:47.247Z" }, + { url = "https://files.pythonhosted.org/packages/14/bd/bf524a52586efb3fb3b9428b0950436e4ed4c11a7a7e81b41b33dd5c7fac/jaxlib-0.6.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c4e97934cbaf5172343aa5ae8ef0c58462ce26154dfda754202b3034160cac7b", size = 53481483, upload-time = "2025-04-17T00:01:57.176Z" }, + { url = "https://files.pythonhosted.org/packages/da/5c/9678ca9d1880da395e25d0241bec1661e62a23250df8d8c15dca16727f14/jaxlib-0.6.0-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:189729639762050c1780b050e98ff620480b1ea32bf167533e000a5cf4c5738e", size = 77473727, upload-time = "2025-04-17T00:02:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/a0/3b/57be3ad61b04267d182be1053f4900cafa5a67aa60c7b4d2b69bc3f4662a/jaxlib-0.6.0-cp313-cp313-manylinux2014_x86_64.whl", hash = "sha256:d0fb122dc7830ca2a5ca3c874a087363a00532b644509c219c3bfd1d54515e8d", size = 87788241, upload-time = "2025-04-17T00:02:08.733Z" }, + { url = "https://files.pythonhosted.org/packages/eb/12/e322d282ac6c157574313938b79673f674d8180c01e7911d22649da7990f/jaxlib-0.6.0-cp313-cp313t-manylinux2014_aarch64.whl", hash = "sha256:d7ab9eaa6e4db3dc6bfba8a061b660147bcd5a1b9d777fde3d729c794f274ab9", size = 77610069, upload-time = "2025-04-17T00:02:13.968Z" }, + { url = "https://files.pythonhosted.org/packages/5e/c5/15dfc92b98f3d14eb3c50934f1293dc141c0e9ed4e66cd43833fd72cd131/jaxlib-0.6.0-cp313-cp313t-manylinux2014_x86_64.whl", hash = "sha256:63106d4e38aec5e4285c8de85e8cddcbb40084c077d07ac03778d3a2bcfa3aae", size = 87894402, upload-time = "2025-04-17T00:02:19.096Z" }, +] + +[[package]] +name = "lit" +version = "18.1.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/47/b4/d7e210971494db7b9a9ac48ff37dfa59a8b14c773f9cf47e6bda58411c0d/lit-18.1.8.tar.gz", hash = "sha256:47c174a186941ae830f04ded76a3444600be67d5e5fb8282c3783fba671c4edb", size = 161127, upload-time = "2024-06-25T14:33:14.489Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/06/b36f150fa7c5bcc96a31a4d19a20fddbd1d965b6f02510b57a3bb8d4b930/lit-18.1.8-py3-none-any.whl", hash = "sha256:a873ff7acd76e746368da32eb7355625e2e55a2baaab884c9cc130f2ee0300f7", size = 96365, upload-time = "2024-06-25T14:33:12.101Z" }, +] + +[[package]] +name = "ml-dtypes" +version = "0.5.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/78/a7/aad060393123cfb383956dca68402aff3db1e1caffd5764887ed5153f41b/ml_dtypes-0.5.3.tar.gz", hash = "sha256:95ce33057ba4d05df50b1f3cfefab22e351868a843b3b15a46c65836283670c9", size = 692316, upload-time = "2025-07-29T18:39:19.454Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/bb/1f32124ab6d3a279ea39202fe098aea95b2d81ef0ce1d48612b6bf715e82/ml_dtypes-0.5.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0a1d68a7cb53e3f640b2b6a34d12c0542da3dd935e560fdf463c0c77f339fc20", size = 667409, upload-time = "2025-07-29T18:38:17.321Z" }, + { url = "https://files.pythonhosted.org/packages/1d/ac/e002d12ae19136e25bb41c7d14d7e1a1b08f3c0e99a44455ff6339796507/ml_dtypes-0.5.3-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cd5a6c711b5350f3cbc2ac28def81cd1c580075ccb7955e61e9d8f4bfd40d24", size = 4960702, upload-time = "2025-07-29T18:38:19.616Z" }, + { url = "https://files.pythonhosted.org/packages/dd/12/79e9954e6b3255a4b1becb191a922d6e2e94d03d16a06341ae9261963ae8/ml_dtypes-0.5.3-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bdcf26c2dbc926b8a35ec8cbfad7eff1a8bd8239e12478caca83a1fc2c400dc2", size = 4933471, upload-time = "2025-07-29T18:38:21.809Z" }, + { url = "https://files.pythonhosted.org/packages/af/f1/720cb1409b5d0c05cff9040c0e9fba73fa4c67897d33babf905d5d46a070/ml_dtypes-0.5.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4a177b882667c69422402df6ed5c3428ce07ac2c1f844d8a1314944651439458", size = 667412, upload-time = "2025-07-29T18:38:25.275Z" }, + { url = "https://files.pythonhosted.org/packages/6a/d5/05861ede5d299f6599f86e6bc1291714e2116d96df003cfe23cc54bcc568/ml_dtypes-0.5.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9849ce7267444c0a717c80c6900997de4f36e2815ce34ac560a3edb2d9a64cd2", size = 4964606, upload-time = "2025-07-29T18:38:27.045Z" }, + { url = "https://files.pythonhosted.org/packages/db/dc/72992b68de367741bfab8df3b3fe7c29f982b7279d341aa5bf3e7ef737ea/ml_dtypes-0.5.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c3f5ae0309d9f888fd825c2e9d0241102fadaca81d888f26f845bc8c13c1e4ee", size = 4938435, upload-time = "2025-07-29T18:38:29.193Z" }, + { url = "https://files.pythonhosted.org/packages/0d/eb/bc07c88a6ab002b4635e44585d80fa0b350603f11a2097c9d1bfacc03357/ml_dtypes-0.5.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:156418abeeda48ea4797db6776db3c5bdab9ac7be197c1233771e0880c304057", size = 663864, upload-time = "2025-07-29T18:38:33.777Z" }, + { url = "https://files.pythonhosted.org/packages/cf/89/11af9b0f21b99e6386b6581ab40fb38d03225f9de5f55cf52097047e2826/ml_dtypes-0.5.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1db60c154989af253f6c4a34e8a540c2c9dce4d770784d426945e09908fbb177", size = 4951313, upload-time = "2025-07-29T18:38:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/d8/a9/b98b86426c24900b0c754aad006dce2863df7ce0bb2bcc2c02f9cc7e8489/ml_dtypes-0.5.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1b255acada256d1fa8c35ed07b5f6d18bc21d1556f842fbc2d5718aea2cd9e55", size = 4928805, upload-time = "2025-07-29T18:38:38.29Z" }, + { url = "https://files.pythonhosted.org/packages/2d/87/1bcc98a66de7b2455dfb292f271452cac9edc4e870796e0d87033524d790/ml_dtypes-0.5.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5103856a225465371fe119f2fef737402b705b810bd95ad5f348e6e1a6ae21af", size = 663781, upload-time = "2025-07-29T18:38:42.984Z" }, + { url = "https://files.pythonhosted.org/packages/fd/2c/bd2a79ba7c759ee192b5601b675b180a3fd6ccf48ffa27fe1782d280f1a7/ml_dtypes-0.5.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cae435a68861660af81fa3c5af16b70ca11a17275c5b662d9c6f58294e0f113", size = 4956217, upload-time = "2025-07-29T18:38:44.65Z" }, + { url = "https://files.pythonhosted.org/packages/14/f3/091ba84e5395d7fe5b30c081a44dec881cd84b408db1763ee50768b2ab63/ml_dtypes-0.5.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6936283b56d74fbec431ca57ce58a90a908fdbd14d4e2d22eea6d72bb208a7b7", size = 4933109, upload-time = "2025-07-29T18:38:46.405Z" }, + { url = "https://files.pythonhosted.org/packages/12/91/e6c7a0d67a152b9330445f9f0cf8ae6eee9b83f990b8c57fe74631e42a90/ml_dtypes-0.5.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:93c36a08a6d158db44f2eb9ce3258e53f24a9a4a695325a689494f0fdbc71770", size = 689321, upload-time = "2025-07-29T18:38:52.03Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6c/b7b94b84a104a5be1883305b87d4c6bd6ae781504474b4cca067cb2340ec/ml_dtypes-0.5.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e44a3761f64bc009d71ddb6d6c71008ba21b53ab6ee588dadab65e2fa79eafc", size = 5274495, upload-time = "2025-07-29T18:38:53.797Z" }, + { url = "https://files.pythonhosted.org/packages/5b/38/6266604dffb43378055394ea110570cf261a49876fc48f548dfe876f34cc/ml_dtypes-0.5.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bdf40d2aaabd3913dec11840f0d0ebb1b93134f99af6a0a4fd88ffe924928ab4", size = 5285422, upload-time = "2025-07-29T18:38:56.603Z" }, + { url = "https://files.pythonhosted.org/packages/7c/88/8612ff177d043a474b9408f0382605d881eeb4125ba89d4d4b3286573a83/ml_dtypes-0.5.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:aec640bd94c4c85c0d11e2733bd13cbb10438fb004852996ec0efbc6cacdaf70", size = 661182, upload-time = "2025-07-29T18:38:58.414Z" }, + { url = "https://files.pythonhosted.org/packages/6f/2b/0569a5e88b29240d373e835107c94ae9256fb2191d3156b43b2601859eff/ml_dtypes-0.5.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bda32ce212baa724e03c68771e5c69f39e584ea426bfe1a701cb01508ffc7035", size = 4956187, upload-time = "2025-07-29T18:39:00.611Z" }, + { url = "https://files.pythonhosted.org/packages/51/66/273c2a06ae44562b104b61e6b14444da00061fd87652506579d7eb2c40b1/ml_dtypes-0.5.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c205cac07d24a29840c163d6469f61069ce4b065518519216297fc2f261f8db9", size = 4930911, upload-time = "2025-07-29T18:39:02.405Z" }, + { url = "https://files.pythonhosted.org/packages/53/21/783dfb51f40d2660afeb9bccf3612b99f6a803d980d2a09132b0f9d216ab/ml_dtypes-0.5.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:e12e29764a0e66a7a31e9b8bf1de5cc0423ea72979f45909acd4292de834ccd3", size = 689324, upload-time = "2025-07-29T18:39:07.567Z" }, + { url = "https://files.pythonhosted.org/packages/09/f7/a82d249c711abf411ac027b7163f285487f5e615c3e0716c61033ce996ab/ml_dtypes-0.5.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19f6c3a4f635c2fc9e2aa7d91416bd7a3d649b48350c51f7f715a09370a90d93", size = 5275917, upload-time = "2025-07-29T18:39:09.339Z" }, + { url = "https://files.pythonhosted.org/packages/7f/3c/541c4b30815ab90ebfbb51df15d0b4254f2f9f1e2b4907ab229300d5e6f2/ml_dtypes-0.5.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ab039ffb40f3dc0aeeeba84fd6c3452781b5e15bef72e2d10bcb33e4bbffc39", size = 5285284, upload-time = "2025-07-29T18:39:11.532Z" }, +] + +[[package]] +name = "mqt-core-catalyst-plugin" +version = "0.1.0" +source = { editable = "." } +dependencies = [ + { name = "pennylane-catalyst", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, +] + +[package.dev-dependencies] +build = [ + { name = "pennylane-catalyst", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "scikit-build-core", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, +] +catalyst = [ + { name = "pennylane-catalyst", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, +] +dev = [ + { name = "lit", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "pennylane-catalyst", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "pytest", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "pytest-cov", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "scikit-build-core", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, +] +test = [ + { name = "lit", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "pytest", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "pytest-cov", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, +] + +[package.metadata] +requires-dist = [{ name = "pennylane-catalyst", specifier = "~=0.12.0" }] + +[package.metadata.requires-dev] +build = [ + { name = "pennylane-catalyst", specifier = "~=0.12.0" }, + { name = "scikit-build-core", specifier = ">=0.11.5" }, +] +catalyst = [{ name = "pennylane-catalyst", specifier = "~=0.12.0" }] +dev = [ + { name = "lit", specifier = ">=18.1.8" }, + { name = "pennylane-catalyst", specifier = "~=0.12.0" }, + { name = "pytest", specifier = ">=8.3.5" }, + { name = "pytest-cov", specifier = ">=6.1.1" }, + { name = "scikit-build-core", specifier = ">=0.11.5" }, +] +test = [ + { name = "lit", specifier = ">=18.1.8" }, + { name = "pytest", specifier = ">=8.3.5" }, + { name = "pytest-cov", specifier = ">=6.1.1" }, +] + +[[package]] +name = "networkx" +version = "3.4.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and sys_platform == 'linux'", +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" }, +] + +[[package]] +name = "networkx" +version = "3.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'linux'", +] +sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and sys_platform == 'linux'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, +] + +[[package]] +name = "numpy" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'linux'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/19/95b3d357407220ed24c139018d2518fab0a61a948e68286a25f1a4d049ff/numpy-2.3.3.tar.gz", hash = "sha256:ddc7c39727ba62b80dfdbedf400d1c10ddfa8eefbd7ec8dcb118be8b56d31029", size = 20576648, upload-time = "2025-09-09T16:54:12.543Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7a/45/e80d203ef6b267aa29b22714fb558930b27960a0c5ce3c19c999232bb3eb/numpy-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ffc4f5caba7dfcbe944ed674b7eef683c7e94874046454bb79ed7ee0236f59d", size = 21259253, upload-time = "2025-09-09T15:56:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/52/18/cf2c648fccf339e59302e00e5f2bc87725a3ce1992f30f3f78c9044d7c43/numpy-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7e946c7170858a0295f79a60214424caac2ffdb0063d4d79cb681f9aa0aa569", size = 14450980, upload-time = "2025-09-09T15:56:05.926Z" }, + { url = "https://files.pythonhosted.org/packages/93/fb/9af1082bec870188c42a1c239839915b74a5099c392389ff04215dcee812/numpy-2.3.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:cd4260f64bc794c3390a63bf0728220dd1a68170c169088a1e0dfa2fde1be12f", size = 5379709, upload-time = "2025-09-09T15:56:07.95Z" }, + { url = "https://files.pythonhosted.org/packages/75/0f/bfd7abca52bcbf9a4a65abc83fe18ef01ccdeb37bfb28bbd6ad613447c79/numpy-2.3.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:f0ddb4b96a87b6728df9362135e764eac3cfa674499943ebc44ce96c478ab125", size = 6913923, upload-time = "2025-09-09T15:56:09.443Z" }, + { url = "https://files.pythonhosted.org/packages/79/55/d69adad255e87ab7afda1caf93ca997859092afeb697703e2f010f7c2e55/numpy-2.3.3-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:afd07d377f478344ec6ca2b8d4ca08ae8bd44706763d1efb56397de606393f48", size = 14589591, upload-time = "2025-09-09T15:56:11.234Z" }, + { url = "https://files.pythonhosted.org/packages/10/a2/010b0e27ddeacab7839957d7a8f00e91206e0c2c47abbb5f35a2630e5387/numpy-2.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bc92a5dedcc53857249ca51ef29f5e5f2f8c513e22cfb90faeb20343b8c6f7a6", size = 16938714, upload-time = "2025-09-09T15:56:14.637Z" }, + { url = "https://files.pythonhosted.org/packages/1c/6b/12ce8ede632c7126eb2762b9e15e18e204b81725b81f35176eac14dc5b82/numpy-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7af05ed4dc19f308e1d9fc759f36f21921eb7bbfc82843eeec6b2a2863a0aefa", size = 16370592, upload-time = "2025-09-09T15:56:17.285Z" }, + { url = "https://files.pythonhosted.org/packages/b4/35/aba8568b2593067bb6a8fe4c52babb23b4c3b9c80e1b49dff03a09925e4a/numpy-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:433bf137e338677cebdd5beac0199ac84712ad9d630b74eceeb759eaa45ddf30", size = 18884474, upload-time = "2025-09-09T15:56:20.943Z" }, + { url = "https://files.pythonhosted.org/packages/51/5d/bb7fc075b762c96329147799e1bcc9176ab07ca6375ea976c475482ad5b3/numpy-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cfdd09f9c84a1a934cde1eec2267f0a43a7cd44b2cca4ff95b7c0d14d144b0bf", size = 20957014, upload-time = "2025-09-09T15:56:29.966Z" }, + { url = "https://files.pythonhosted.org/packages/6b/0e/c6211bb92af26517acd52125a237a92afe9c3124c6a68d3b9f81b62a0568/numpy-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb32e3cf0f762aee47ad1ddc6672988f7f27045b0783c887190545baba73aa25", size = 14185220, upload-time = "2025-09-09T15:56:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/22/f2/07bb754eb2ede9073f4054f7c0286b0d9d2e23982e090a80d478b26d35ca/numpy-2.3.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:396b254daeb0a57b1fe0ecb5e3cff6fa79a380fa97c8f7781a6d08cd429418fe", size = 5113918, upload-time = "2025-09-09T15:56:34.175Z" }, + { url = "https://files.pythonhosted.org/packages/81/0a/afa51697e9fb74642f231ea36aca80fa17c8fb89f7a82abd5174023c3960/numpy-2.3.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:067e3d7159a5d8f8a0b46ee11148fc35ca9b21f61e3c49fbd0a027450e65a33b", size = 6647922, upload-time = "2025-09-09T15:56:36.149Z" }, + { url = "https://files.pythonhosted.org/packages/5d/f5/122d9cdb3f51c520d150fef6e87df9279e33d19a9611a87c0d2cf78a89f4/numpy-2.3.3-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c02d0629d25d426585fb2e45a66154081b9fa677bc92a881ff1d216bc9919a8", size = 14281991, upload-time = "2025-09-09T15:56:40.548Z" }, + { url = "https://files.pythonhosted.org/packages/51/64/7de3c91e821a2debf77c92962ea3fe6ac2bc45d0778c1cbe15d4fce2fd94/numpy-2.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9192da52b9745f7f0766531dcfa978b7763916f158bb63bdb8a1eca0068ab20", size = 16641643, upload-time = "2025-09-09T15:56:43.343Z" }, + { url = "https://files.pythonhosted.org/packages/30/e4/961a5fa681502cd0d68907818b69f67542695b74e3ceaa513918103b7e80/numpy-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cd7de500a5b66319db419dc3c345244404a164beae0d0937283b907d8152e6ea", size = 16056787, upload-time = "2025-09-09T15:56:46.141Z" }, + { url = "https://files.pythonhosted.org/packages/99/26/92c912b966e47fbbdf2ad556cb17e3a3088e2e1292b9833be1dfa5361a1a/numpy-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:93d4962d8f82af58f0b2eb85daaf1b3ca23fe0a85d0be8f1f2b7bb46034e56d7", size = 18579598, upload-time = "2025-09-09T15:56:49.844Z" }, + { url = "https://files.pythonhosted.org/packages/7d/b9/984c2b1ee61a8b803bf63582b4ac4242cf76e2dbd663efeafcb620cc0ccb/numpy-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f5415fb78995644253370985342cd03572ef8620b934da27d77377a2285955bf", size = 20949588, upload-time = "2025-09-09T15:56:59.087Z" }, + { url = "https://files.pythonhosted.org/packages/a6/e4/07970e3bed0b1384d22af1e9912527ecbeb47d3b26e9b6a3bced068b3bea/numpy-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d00de139a3324e26ed5b95870ce63be7ec7352171bc69a4cf1f157a48e3eb6b7", size = 14177802, upload-time = "2025-09-09T15:57:01.73Z" }, + { url = "https://files.pythonhosted.org/packages/35/c7/477a83887f9de61f1203bad89cf208b7c19cc9fef0cebef65d5a1a0619f2/numpy-2.3.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:9dc13c6a5829610cc07422bc74d3ac083bd8323f14e2827d992f9e52e22cd6a6", size = 5106537, upload-time = "2025-09-09T15:57:03.765Z" }, + { url = "https://files.pythonhosted.org/packages/52/47/93b953bd5866a6f6986344d045a207d3f1cfbad99db29f534ea9cee5108c/numpy-2.3.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d79715d95f1894771eb4e60fb23f065663b2298f7d22945d66877aadf33d00c7", size = 6640743, upload-time = "2025-09-09T15:57:07.921Z" }, + { url = "https://files.pythonhosted.org/packages/23/83/377f84aaeb800b64c0ef4de58b08769e782edcefa4fea712910b6f0afd3c/numpy-2.3.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:952cfd0748514ea7c3afc729a0fc639e61655ce4c55ab9acfab14bda4f402b4c", size = 14278881, upload-time = "2025-09-09T15:57:11.349Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a5/bf3db6e66c4b160d6ea10b534c381a1955dfab34cb1017ea93aa33c70ed3/numpy-2.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b83648633d46f77039c29078751f80da65aa64d5622a3cd62aaef9d835b6c93", size = 16636301, upload-time = "2025-09-09T15:57:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/a2/59/1287924242eb4fa3f9b3a2c30400f2e17eb2707020d1c5e3086fe7330717/numpy-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b001bae8cea1c7dfdb2ae2b017ed0a6f2102d7a70059df1e338e307a4c78a8ae", size = 16053645, upload-time = "2025-09-09T15:57:16.534Z" }, + { url = "https://files.pythonhosted.org/packages/e6/93/b3d47ed882027c35e94ac2320c37e452a549f582a5e801f2d34b56973c97/numpy-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8e9aced64054739037d42fb84c54dd38b81ee238816c948c8f3ed134665dcd86", size = 18578179, upload-time = "2025-09-09T15:57:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/11/d0/0d1ddec56b162042ddfafeeb293bac672de9b0cfd688383590090963720a/numpy-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:eda59e44957d272846bb407aad19f89dc6f58fecf3504bd144f4c5cf81a7eacc", size = 21048025, upload-time = "2025-09-09T15:57:27.257Z" }, + { url = "https://files.pythonhosted.org/packages/36/9e/1996ca6b6d00415b6acbdd3c42f7f03ea256e2c3f158f80bd7436a8a19f3/numpy-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:823d04112bc85ef5c4fda73ba24e6096c8f869931405a80aa8b0e604510a26bc", size = 14301053, upload-time = "2025-09-09T15:57:30.077Z" }, + { url = "https://files.pythonhosted.org/packages/05/24/43da09aa764c68694b76e84b3d3f0c44cb7c18cdc1ba80e48b0ac1d2cd39/numpy-2.3.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:40051003e03db4041aa325da2a0971ba41cf65714e65d296397cc0e32de6018b", size = 5229444, upload-time = "2025-09-09T15:57:32.733Z" }, + { url = "https://files.pythonhosted.org/packages/bc/14/50ffb0f22f7218ef8af28dd089f79f68289a7a05a208db9a2c5dcbe123c1/numpy-2.3.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ee9086235dd6ab7ae75aba5662f582a81ced49f0f1c6de4260a78d8f2d91a19", size = 6738039, upload-time = "2025-09-09T15:57:34.328Z" }, + { url = "https://files.pythonhosted.org/packages/55/52/af46ac0795e09657d45a7f4db961917314377edecf66db0e39fa7ab5c3d3/numpy-2.3.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:94fcaa68757c3e2e668ddadeaa86ab05499a70725811e582b6a9858dd472fb30", size = 14352314, upload-time = "2025-09-09T15:57:36.255Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b1/dc226b4c90eb9f07a3fff95c2f0db3268e2e54e5cce97c4ac91518aee71b/numpy-2.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da1a74b90e7483d6ce5244053399a614b1d6b7bc30a60d2f570e5071f8959d3e", size = 16701722, upload-time = "2025-09-09T15:57:38.622Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9d/9d8d358f2eb5eced14dba99f110d83b5cd9a4460895230f3b396ad19a323/numpy-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2990adf06d1ecee3b3dcbb4977dfab6e9f09807598d647f04d385d29e7a3c3d3", size = 16132755, upload-time = "2025-09-09T15:57:41.16Z" }, + { url = "https://files.pythonhosted.org/packages/b6/27/b3922660c45513f9377b3fb42240bec63f203c71416093476ec9aa0719dc/numpy-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ed635ff692483b8e3f0fcaa8e7eb8a75ee71aa6d975388224f70821421800cea", size = 18651560, upload-time = "2025-09-09T15:57:43.459Z" }, + { url = "https://files.pythonhosted.org/packages/6b/01/342ad585ad82419b99bcf7cebe99e61da6bedb89e213c5fd71acc467faee/numpy-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cd052f1fa6a78dee696b58a914b7229ecfa41f0a6d96dc663c1220a55e137593", size = 20951527, upload-time = "2025-09-09T15:57:52.006Z" }, + { url = "https://files.pythonhosted.org/packages/ef/d8/204e0d73fc1b7a9ee80ab1fe1983dd33a4d64a4e30a05364b0208e9a241a/numpy-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:414a97499480067d305fcac9716c29cf4d0d76db6ebf0bf3cbce666677f12652", size = 14186159, upload-time = "2025-09-09T15:57:54.407Z" }, + { url = "https://files.pythonhosted.org/packages/22/af/f11c916d08f3a18fb8ba81ab72b5b74a6e42ead4c2846d270eb19845bf74/numpy-2.3.3-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:50a5fe69f135f88a2be9b6ca0481a68a136f6febe1916e4920e12f1a34e708a7", size = 5114624, upload-time = "2025-09-09T15:57:56.5Z" }, + { url = "https://files.pythonhosted.org/packages/fb/11/0ed919c8381ac9d2ffacd63fd1f0c34d27e99cab650f0eb6f110e6ae4858/numpy-2.3.3-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:b912f2ed2b67a129e6a601e9d93d4fa37bef67e54cac442a2f588a54afe5c67a", size = 6642627, upload-time = "2025-09-09T15:57:58.206Z" }, + { url = "https://files.pythonhosted.org/packages/ee/83/deb5f77cb0f7ba6cb52b91ed388b47f8f3c2e9930d4665c600408d9b90b9/numpy-2.3.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9e318ee0596d76d4cb3d78535dc005fa60e5ea348cd131a51e99d0bdbe0b54fe", size = 14296926, upload-time = "2025-09-09T15:58:00.035Z" }, + { url = "https://files.pythonhosted.org/packages/77/cc/70e59dcb84f2b005d4f306310ff0a892518cc0c8000a33d0e6faf7ca8d80/numpy-2.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce020080e4a52426202bdb6f7691c65bb55e49f261f31a8f506c9f6bc7450421", size = 16638958, upload-time = "2025-09-09T15:58:02.738Z" }, + { url = "https://files.pythonhosted.org/packages/b6/5a/b2ab6c18b4257e099587d5b7f903317bd7115333ad8d4ec4874278eafa61/numpy-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e6687dc183aa55dae4a705b35f9c0f8cb178bcaa2f029b241ac5356221d5c021", size = 16071920, upload-time = "2025-09-09T15:58:05.029Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f1/8b3fdc44324a259298520dd82147ff648979bed085feeacc1250ef1656c0/numpy-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d8f3b1080782469fdc1718c4ed1d22549b5fb12af0d57d35e992158a772a37cf", size = 18577076, upload-time = "2025-09-09T15:58:07.745Z" }, + { url = "https://files.pythonhosted.org/packages/d5/df/ee2f1c0a9de7347f14da5dd3cd3c3b034d1b8607ccb6883d7dd5c035d631/numpy-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9cc48e09feb11e1db00b320e9d30a4151f7369afb96bd0e48d942d09da3a0d00", size = 21047987, upload-time = "2025-09-09T15:58:16.889Z" }, + { url = "https://files.pythonhosted.org/packages/d6/92/9453bdc5a4e9e69cf4358463f25e8260e2ffc126d52e10038b9077815989/numpy-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:901bf6123879b7f251d3631967fd574690734236075082078e0571977c6a8e6a", size = 14301076, upload-time = "2025-09-09T15:58:20.343Z" }, + { url = "https://files.pythonhosted.org/packages/13/77/1447b9eb500f028bb44253105bd67534af60499588a5149a94f18f2ca917/numpy-2.3.3-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:7f025652034199c301049296b59fa7d52c7e625017cae4c75d8662e377bf487d", size = 5229491, upload-time = "2025-09-09T15:58:22.481Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f9/d72221b6ca205f9736cb4b2ce3b002f6e45cd67cd6a6d1c8af11a2f0b649/numpy-2.3.3-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:533ca5f6d325c80b6007d4d7fb1984c303553534191024ec6a524a4c92a5935a", size = 6737913, upload-time = "2025-09-09T15:58:24.569Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5f/d12834711962ad9c46af72f79bb31e73e416ee49d17f4c797f72c96b6ca5/numpy-2.3.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0edd58682a399824633b66885d699d7de982800053acf20be1eaa46d92009c54", size = 14352811, upload-time = "2025-09-09T15:58:26.416Z" }, + { url = "https://files.pythonhosted.org/packages/a1/0d/fdbec6629d97fd1bebed56cd742884e4eead593611bbe1abc3eb40d304b2/numpy-2.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:367ad5d8fbec5d9296d18478804a530f1191e24ab4d75ab408346ae88045d25e", size = 16702689, upload-time = "2025-09-09T15:58:28.831Z" }, + { url = "https://files.pythonhosted.org/packages/9b/09/0a35196dc5575adde1eb97ddfbc3e1687a814f905377621d18ca9bc2b7dd/numpy-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8f6ac61a217437946a1fa48d24c47c91a0c4f725237871117dea264982128097", size = 16133855, upload-time = "2025-09-09T15:58:31.349Z" }, + { url = "https://files.pythonhosted.org/packages/7a/ca/c9de3ea397d576f1b6753eaa906d4cdef1bf97589a6d9825a349b4729cc2/numpy-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:179a42101b845a816d464b6fe9a845dfaf308fdfc7925387195570789bb2c970", size = 18652520, upload-time = "2025-09-09T15:58:33.762Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f2/7e0a37cfced2644c9563c529f29fa28acbd0960dde32ece683aafa6f4949/numpy-2.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1e02c7159791cd481e1e6d5ddd766b62a4d5acf8df4d4d1afe35ee9c5c33a41e", size = 21131019, upload-time = "2025-09-09T15:58:42.838Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/3291f505297ed63831135a6cc0f474da0c868a1f31b0dd9a9f03a7a0d2ed/numpy-2.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:dca2d0fc80b3893ae72197b39f69d55a3cd8b17ea1b50aa4c62de82419936150", size = 14376288, upload-time = "2025-09-09T15:58:45.425Z" }, + { url = "https://files.pythonhosted.org/packages/bf/4b/ae02e985bdeee73d7b5abdefeb98aef1207e96d4c0621ee0cf228ddfac3c/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:99683cbe0658f8271b333a1b1b4bb3173750ad59c0c61f5bbdc5b318918fffe3", size = 5305425, upload-time = "2025-09-09T15:58:48.6Z" }, + { url = "https://files.pythonhosted.org/packages/8b/eb/9df215d6d7250db32007941500dc51c48190be25f2401d5b2b564e467247/numpy-2.3.3-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d9d537a39cc9de668e5cd0e25affb17aec17b577c6b3ae8a3d866b479fbe88d0", size = 6819053, upload-time = "2025-09-09T15:58:50.401Z" }, + { url = "https://files.pythonhosted.org/packages/57/62/208293d7d6b2a8998a4a1f23ac758648c3c32182d4ce4346062018362e29/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8596ba2f8af5f93b01d97563832686d20206d303024777f6dfc2e7c7c3f1850e", size = 14420354, upload-time = "2025-09-09T15:58:52.704Z" }, + { url = "https://files.pythonhosted.org/packages/ed/0c/8e86e0ff7072e14a71b4c6af63175e40d1e7e933ce9b9e9f765a95b4e0c3/numpy-2.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1ec5615b05369925bd1125f27df33f3b6c8bc10d788d5999ecd8769a1fa04db", size = 16760413, upload-time = "2025-09-09T15:58:55.027Z" }, +] + +[[package]] +name = "opt-einsum" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/b9/2ac072041e899a52f20cf9510850ff58295003aa75525e58343591b0cbfb/opt_einsum-3.4.0.tar.gz", hash = "sha256:96ca72f1b886d148241348783498194c577fa30a8faac108586b14f1ba4473ac", size = 63004, upload-time = "2024-09-26T14:33:24.483Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/cd/066e86230ae37ed0be70aae89aabf03ca8d9f39c8aea0dec8029455b5540/opt_einsum-3.4.0-py3-none-any.whl", hash = "sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd", size = 71932, upload-time = "2024-09-26T14:33:23.039Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pathspec" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/bc/f35b8446f4531a7cb215605d100cd88b7ac6f44ab3fc94870c120ab3adbf/pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712", size = 51043, upload-time = "2023-12-10T22:30:45Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", size = 31191, upload-time = "2023-12-10T22:30:43.14Z" }, +] + +[[package]] +name = "pennylane" +version = "0.42.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appdirs", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "autograd", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "autoray", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "cachetools", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "diastatic-malt", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, + { name = "networkx", version = "3.5", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux')" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux')" }, + { name = "packaging", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "pennylane-lightning", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "requests", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "rustworkx", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, + { name = "scipy", version = "1.16.2", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux')" }, + { name = "tomlkit", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "typing-extensions", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/27/f2e6f7bf64e588afbe5d552e4f68e4c817c87c4f5037fd5fc7d48a636a41/pennylane-0.42.3-py3-none-any.whl", hash = "sha256:184da954c4a187243d83472c4685420cc61cec080cef60ace9811d7744846bb4", size = 4824484, upload-time = "2025-08-20T21:15:56.789Z" }, +] + +[[package]] +name = "pennylane-catalyst" +version = "0.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "diastatic-malt", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "jax", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "jaxlib", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux')" }, + { name = "pennylane", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "pennylane-lightning", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "scipy-openblas32", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/22/80065cae76eac81cc0532418e8e3a06b747ab7b484e0a8fdbe669893eb99/pennylane_catalyst-0.12.0-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:c875547e52a2f3565f882235808b8f63f2e3f831ab0c25412ac81298667ec40d", size = 63698167, upload-time = "2025-07-15T15:02:08.747Z" }, + { url = "https://files.pythonhosted.org/packages/91/f8/4104be88dc7e138ca805c1eb1ca5d52c791b924ddb4c13c4a74f1df0e2b6/pennylane_catalyst-0.12.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:5e1522bece8a37e003280f2ad51c5d674e8d488b9abaa49ea42ba8c9d7155da1", size = 72279892, upload-time = "2025-07-15T15:00:59.628Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3b/87053f1e05e9db6862ae6ce0178eada66ef1f1ff1c8fa17e8d1801cc5189/pennylane_catalyst-0.12.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:199d860104df51aae52755cdc9ce2f1d8ce7372591d52ebb15a211826e83b1d5", size = 72435476, upload-time = "2025-07-15T14:59:38.545Z" }, + { url = "https://files.pythonhosted.org/packages/28/10/b678a9b9d72769e11e49d67348407118edcf41c03911c8413c25f02b69e2/pennylane_catalyst-0.12.0-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:5177a3c46604ef4c627bdfd1c7b8fa9a68e8c40f1ac9256f3dcb2140a64ff71f", size = 63697604, upload-time = "2025-07-15T15:02:19.975Z" }, + { url = "https://files.pythonhosted.org/packages/fb/cb/000c00fb260b623de098a7c450ac0b04d7a1590b57dbcf061f660ae56a55/pennylane_catalyst-0.12.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:109a204c45c69f59ad47356c6a3e90ca0600e49820ac77985fc9da602135bde2", size = 72279986, upload-time = "2025-07-15T15:01:11.465Z" }, + { url = "https://files.pythonhosted.org/packages/1b/d6/c9301cea2c7b64bccd9c7cfe16aeeeb9f5be3e15d437765e0fa3ba8647bc/pennylane_catalyst-0.12.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:40a43f5adfe1419154778b13b14aad3af90ecc3647dd0e4e6956a3e4cfa3b33e", size = 72435843, upload-time = "2025-07-15T14:59:51.164Z" }, + { url = "https://files.pythonhosted.org/packages/6c/64/4f47f4ba0f4607c43473bc5c8b69ef861e59d7492fb957d4befa70332888/pennylane_catalyst-0.12.0-cp312-abi3-macosx_13_0_arm64.whl", hash = "sha256:e6f5cd871177079cc0189487e5fdeef822abf186992edf4cab60accbb9be62bd", size = 63691948, upload-time = "2025-07-15T15:02:30.542Z" }, + { url = "https://files.pythonhosted.org/packages/a3/75/70ffbd7bcd04bf0470c3f83ae8261bdbdcb2c8611f832b5cf3b0d8d6d2ab/pennylane_catalyst-0.12.0-cp312-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2cc0b1282c4051e592e59c5e2939aa52783e0a5ba90c84572c00716f100f99c7", size = 72236101, upload-time = "2025-07-15T15:01:22.925Z" }, + { url = "https://files.pythonhosted.org/packages/91/4f/fd30b20406dde3b561129995ed9106f05e7d3081e6a0878e2ffe5d106439/pennylane_catalyst-0.12.0-cp312-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:612454d4cbcb1f24080ced7477c6586fa49e5f5eac83288cf22447556de3bf6b", size = 72392433, upload-time = "2025-07-15T15:00:03.112Z" }, +] + +[[package]] +name = "pennylane-lightning" +version = "0.42.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pennylane", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "scipy-openblas32", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/08/2600aebaa79625bb298ee4fde4df63f21ab6dcdaae182591a532eb985523/pennylane_lightning-0.42.0.tar.gz", hash = "sha256:37b877285e76c0b524df88c62679f4c182454d32a3dc1fac2fea7244ae21bb62", size = 774266, upload-time = "2025-07-15T14:48:14.529Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ad/e3/b132a9fa49f799aa2f4bf5da16ddd7dc894d4ca11fa52319806c4ee1f8f7/pennylane_lightning-0.42.0-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:823f60e4594751cad071c145bed4467bb985612eeda18d4fa24d9dd86200c165", size = 1796787, upload-time = "2025-07-15T14:46:56.425Z" }, + { url = "https://files.pythonhosted.org/packages/93/d2/ac642366a09b95337ab27bf2ecf728afb34944499ae56c9cd77c497ceab8/pennylane_lightning-0.42.0-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:369c7f63329fccee0c19d1e0054e8ed39089bb81d0ea92fcf3e0f9ca67ee7591", size = 2305762, upload-time = "2025-07-15T14:46:58.117Z" }, + { url = "https://files.pythonhosted.org/packages/89/2c/d220357d4966085ce5ff3f3383ae6f48473614832b5d25fac36995271486/pennylane_lightning-0.42.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6544a044dc3cb6e241437aefee952e4d21754b4a9c3fc05aa332e38512e15868", size = 2108016, upload-time = "2025-07-15T14:46:59.709Z" }, + { url = "https://files.pythonhosted.org/packages/d0/24/e95792186daccb6b5e7f65c58c523471e3fb10b87c4ebcfe901e6e284cee/pennylane_lightning-0.42.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c5e09d96eec2e5ebfc7267c00bd6006cecceace78e4b868e0ba1b363e1736a27", size = 2587690, upload-time = "2025-07-15T14:47:01.097Z" }, + { url = "https://files.pythonhosted.org/packages/4e/63/9bec2d4f1d4dfb77f636a27b742e9bd5e6a2999faeb2857e1eee1b2a7d15/pennylane_lightning-0.42.0-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:60d2a432b0715d267af5c9d54ede05ee9b9adc295c9d6a27a02f1da27c253064", size = 1798194, upload-time = "2025-07-15T14:47:04.624Z" }, + { url = "https://files.pythonhosted.org/packages/7e/1d/04c942d21a8076adac6c07a753ae92d4ba862aee27e08f54997e564aeb51/pennylane_lightning-0.42.0-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:858bdfd1a2e323ceb62bf8892bf38bd6395f7d879458b017de77907015718d3e", size = 2307208, upload-time = "2025-07-15T14:47:05.935Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9f/dbaa229bb3af4a3138f11b0b4b12de5e7ea1d98725d99a2b1b30e7b4992d/pennylane_lightning-0.42.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:444eb821d46d5be18ad8659f5e1f80ebb731d2d929eb7ea5fb15e485df8cacb0", size = 2112446, upload-time = "2025-07-15T14:47:07.43Z" }, + { url = "https://files.pythonhosted.org/packages/5c/92/85f3263455c8793cca6478959a1c40c01d33e2c7e8ddd38bda742af451cf/pennylane_lightning-0.42.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:72a11da3e8455c59715e826bd1ee362287031a62ddeb4cbf6a03168a8bc4a8e4", size = 2589596, upload-time = "2025-07-15T14:47:09.225Z" }, + { url = "https://files.pythonhosted.org/packages/25/45/9261aa6eb271fcc4263a8abefbbea3c35c67107410483fffa3b401274f8b/pennylane_lightning-0.42.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:e1fc1ec297eee8a11131d132faa012eef67a96ed62e57b9d7a38df0e21a3c8cb", size = 1799027, upload-time = "2025-07-15T14:47:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/1a/bf/ce837890903f2c91d9f39c0c0a8480005c9b78c3637c9af4dce9b24898e6/pennylane_lightning-0.42.0-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:4e16dbcc941c18d5e7a8d7b3f32748c8c773f80ea47ff596469f68622a18d462", size = 2310520, upload-time = "2025-07-15T14:47:13.892Z" }, + { url = "https://files.pythonhosted.org/packages/8f/38/2999a42a5be59813d1da89ab3c4ca875714304b1db3d39c07965cb11fe91/pennylane_lightning-0.42.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d6c1f65e7631299c1c7bcdbbe09b8cb7a0a5ef4325c520644663471569f5d5c4", size = 2108480, upload-time = "2025-07-15T14:47:15.132Z" }, + { url = "https://files.pythonhosted.org/packages/60/b8/ad74ec15b1f891498ec3e63e33d3d8832c1a30d524cfc8bfef8a7ec772d6/pennylane_lightning-0.42.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:f22d10dab16adb9045e32a29b06d113d3f7f6dcecc25bca184d77f427f65680e", size = 2593675, upload-time = "2025-07-15T14:47:16.842Z" }, + { url = "https://files.pythonhosted.org/packages/25/a1/915b518afb468b810c023cc8616033eb713998f12f6b7c71296d4e449f1d/pennylane_lightning-0.42.0-cp313-cp313-macosx_13_0_arm64.whl", hash = "sha256:ae46b36af37af5f33e188cd1e707dc2c54f52a6beb3a345ce35a70d7de3756cf", size = 1799056, upload-time = "2025-07-15T14:47:20.871Z" }, + { url = "https://files.pythonhosted.org/packages/97/69/64a43d916d8fd6168c758131b884464a836f4a7fd6cb5c50343e9c5935bf/pennylane_lightning-0.42.0-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:b2accb37866f47af47ec0881c17c29012d2937b0dc310ca6e44eeca8da6d81a8", size = 2310613, upload-time = "2025-07-15T14:47:22.421Z" }, + { url = "https://files.pythonhosted.org/packages/82/b4/3a0e9194c71bdad712e6d20fd56728eb2ccccaffd68df2cac8dbe7894c23/pennylane_lightning-0.42.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:7f7bc8f01acbc9f723ca0c73cca89c56f1e5ef25fbe5cacbd5d84e001f3b09a8", size = 2108639, upload-time = "2025-07-15T14:47:24.193Z" }, + { url = "https://files.pythonhosted.org/packages/7e/1c/48a121e31879685e15e07ed255f8f41b1e3e77b9b657fda2c2bf2d77253c/pennylane_lightning-0.42.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b207764a2c56cb3cbb01bc3fe5e730cd1ac232d946bb7f47022e6444bfb16ebc", size = 2592456, upload-time = "2025-07-15T14:47:25.538Z" }, + { url = "https://files.pythonhosted.org/packages/06/c8/24e7d13fd5674d12a140e18fdb57c6136e59273ad2e6e807bd0e3d7a7b67/pennylane_lightning-0.42.0-py3-none-any.whl", hash = "sha256:13e60b74c495023d8bc7a49debab521e6358f947c6be9dbd2b71e24ea232e061", size = 1026234, upload-time = "2025-07-15T14:47:28.721Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, + { name = "iniconfig", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "packaging", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "pluggy", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "pygments", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "tomli", marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + +[[package]] +name = "pytest-cov" +version = "7.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "coverage", extra = ["toml"], marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "pluggy", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "pytest", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5e/f7/c933acc76f5208b3b00089573cf6a2bc26dc80a8aece8f52bb7d6b1855ca/pytest_cov-7.0.0.tar.gz", hash = "sha256:33c97eda2e049a0c5298e91f519302a1334c26ac65c1a483d6206fd458361af1", size = 54328, upload-time = "2025-09-09T10:57:02.113Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/49/1377b49de7d0c1ce41292161ea0f721913fa8722c19fb9c1e3aa0367eecb/pytest_cov-7.0.0-py3-none-any.whl", hash = "sha256:3b8e9558b16cc1479da72058bdecf8073661c7f57f7d3c5f22a1c23507f2d861", size = 22424, upload-time = "2025-09-09T10:57:00.695Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "charset-normalizer", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "idna", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "urllib3", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "rustworkx" +version = "0.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e7/b0/66d96f02120f79eeed86b5c5be04029b6821155f31ed4907a4e9f1460671/rustworkx-0.17.1.tar.gz", hash = "sha256:59ea01b4e603daffa4e8827316c1641eef18ae9032f0b1b14aa0181687e3108e", size = 399407, upload-time = "2025-09-15T16:29:46.429Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/24/8972ed631fa05fdec05a7bb7f1fc0f8e78ee761ab37e8a93d1ed396ba060/rustworkx-0.17.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c08fb8db041db052da404839b064ebfb47dcce04ba9a3e2eb79d0c65ab011da4", size = 2257491, upload-time = "2025-08-13T01:43:31.466Z" }, + { url = "https://files.pythonhosted.org/packages/23/ae/7b6bbae5e0487ee42072dc6a46edf5db9731a0701ed648db22121fb7490c/rustworkx-0.17.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:4ef8e327dadf6500edd76fedb83f6d888b9266c58bcdbffd5a40c33835c9dd26", size = 2040175, upload-time = "2025-08-13T01:43:33.762Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ea/c17fb9428c8f0dcc605596f9561627a5b9ef629d356204ee5088cfcf52c6/rustworkx-0.17.1-cp39-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b809e0aa2927c68574b196f993233e269980918101b0dd235289c4f3ddb2115", size = 2324771, upload-time = "2025-08-13T01:43:35.553Z" }, + { url = "https://files.pythonhosted.org/packages/d7/40/ec8b3b8b0f8c0b768690c454b8dcc2781b4f2c767f9f1215539c7909e35b/rustworkx-0.17.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7e82c46a92fb0fd478b7372e15ca524c287485fdecaed37b8bb68f4df2720f2", size = 2068584, upload-time = "2025-08-13T01:43:37.261Z" }, + { url = "https://files.pythonhosted.org/packages/d9/22/713b900d320d06ce8677e71bba0ec5df0037f1d83270bff5db3b271c10d7/rustworkx-0.17.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:42170075d8a7319e89ff63062c2f1d1116ced37b6f044f3bf36d10b60a107aa4", size = 2380949, upload-time = "2025-08-13T01:52:17.435Z" }, + { url = "https://files.pythonhosted.org/packages/20/4b/54be84b3b41a19caf0718a2b6bb280dde98c8626c809c969f16aad17458f/rustworkx-0.17.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65cba97fa95470239e2d65eb4db1613f78e4396af9f790ff771b0e5476bfd887", size = 2562069, upload-time = "2025-08-13T02:09:27.222Z" }, + { url = "https://files.pythonhosted.org/packages/39/5b/281bb21d091ab4e36cf377088366d55d0875fa2347b3189c580ec62b44c7/rustworkx-0.17.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246cc252053f89e36209535b9c58755960197e6ae08d48d3973760141c62ac95", size = 2221186, upload-time = "2025-08-13T01:43:38.598Z" }, + { url = "https://files.pythonhosted.org/packages/cc/2d/30a941a21b81e9db50c4c3ef8a64c5ee1c8eea3a90506ca0326ce39d021f/rustworkx-0.17.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c10d25e9f0e87d6a273d1ea390b636b4fb3fede2094bf0cb3fe565d696a91b48", size = 2123510, upload-time = "2025-08-13T01:43:40.288Z" }, + { url = "https://files.pythonhosted.org/packages/4f/ef/c9199e4b6336ee5a9f1979c11b5779c5cf9ab6f8386e0b9a96c8ffba7009/rustworkx-0.17.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:48784a673cf8d04f3cd246fa6b53fd1ccc4d83304503463bd561c153517bccc1", size = 2302783, upload-time = "2025-08-13T01:43:42.073Z" }, +] + +[[package]] +name = "scikit-build-core" +version = "0.11.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "exceptiongroup", marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, + { name = "packaging", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "pathspec", marker = "(platform_machine == 'arm64' and sys_platform == 'darwin') or sys_platform == 'linux'" }, + { name = "tomli", marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/b2/c11aaa746f3dfcdb46499affbc5f9784c991d354a80ca92f96a0f0f5aadf/scikit_build_core-0.11.6.tar.gz", hash = "sha256:5982ccd839735be99cfd3b92a8847c6c196692f476c215da84b79d2ad12f9f1b", size = 286006, upload-time = "2025-08-22T22:11:56.112Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/49/ec16b3db6893db788ae35f98506ff5a9c25dca7eb18cc38ada8a4c1dc944/scikit_build_core-0.11.6-py3-none-any.whl", hash = "sha256:ce6d8fe64e6b4c759ea0fb95d2f8a68f60d2df31c2989838633b8ec930736360", size = 185764, upload-time = "2025-08-22T22:11:52.438Z" }, +] + +[[package]] +name = "scipy" +version = "1.15.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version < '3.11' and sys_platform == 'linux'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version < '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version < '3.11' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" }, + { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" }, + { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" }, + { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" }, + { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" }, + { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" }, + { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" }, + { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, + { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, + { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, + { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, + { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, + { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, + { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, + { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" }, + { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" }, + { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" }, + { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" }, + { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" }, + { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" }, + { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" }, + { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" }, + { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" }, + { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" }, + { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" }, + { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" }, +] + +[[package]] +name = "scipy" +version = "1.16.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version == '3.12.*' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version == '3.11.*' and platform_machine == 'arm64' and sys_platform == 'darwin'", + "python_full_version >= '3.13' and sys_platform == 'linux'", + "python_full_version == '3.12.*' and sys_platform == 'linux'", + "python_full_version == '3.11.*' and sys_platform == 'linux'", +] +dependencies = [ + { name = "numpy", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "(python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and sys_platform == 'linux')" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4c/3b/546a6f0bfe791bbb7f8d591613454d15097e53f906308ec6f7c1ce588e8e/scipy-1.16.2.tar.gz", hash = "sha256:af029b153d243a80afb6eabe40b0a07f8e35c9adc269c019f364ad747f826a6b", size = 30580599, upload-time = "2025-09-11T17:48:08.271Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/ef/37ed4b213d64b48422df92560af7300e10fe30b5d665dd79932baebee0c6/scipy-1.16.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:6ab88ea43a57da1af33292ebd04b417e8e2eaf9d5aa05700be8d6e1b6501cd92", size = 36619956, upload-time = "2025-09-11T17:39:20.5Z" }, + { url = "https://files.pythonhosted.org/packages/85/ab/5c2eba89b9416961a982346a4d6a647d78c91ec96ab94ed522b3b6baf444/scipy-1.16.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c95e96c7305c96ede73a7389f46ccd6c659c4da5ef1b2789466baeaed3622b6e", size = 28931117, upload-time = "2025-09-11T17:39:29.06Z" }, + { url = "https://files.pythonhosted.org/packages/80/d1/eed51ab64d227fe60229a2d57fb60ca5898cfa50ba27d4f573e9e5f0b430/scipy-1.16.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:87eb178db04ece7c698220d523c170125dbffebb7af0345e66c3554f6f60c173", size = 20921997, upload-time = "2025-09-11T17:39:34.892Z" }, + { url = "https://files.pythonhosted.org/packages/be/7c/33ea3e23bbadde96726edba6bf9111fb1969d14d9d477ffa202c67bec9da/scipy-1.16.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:4e409eac067dcee96a57fbcf424c13f428037827ec7ee3cb671ff525ca4fc34d", size = 23523374, upload-time = "2025-09-11T17:39:40.846Z" }, + { url = "https://files.pythonhosted.org/packages/96/0b/7399dc96e1e3f9a05e258c98d716196a34f528eef2ec55aad651ed136d03/scipy-1.16.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e574be127bb760f0dad24ff6e217c80213d153058372362ccb9555a10fc5e8d2", size = 33583702, upload-time = "2025-09-11T17:39:49.011Z" }, + { url = "https://files.pythonhosted.org/packages/1a/bc/a5c75095089b96ea72c1bd37a4497c24b581ec73db4ef58ebee142ad2d14/scipy-1.16.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5db5ba6188d698ba7abab982ad6973265b74bb40a1efe1821b58c87f73892b9", size = 35883427, upload-time = "2025-09-11T17:39:57.406Z" }, + { url = "https://files.pythonhosted.org/packages/ab/66/e25705ca3d2b87b97fe0a278a24b7f477b4023a926847935a1a71488a6a6/scipy-1.16.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ec6e74c4e884104ae006d34110677bfe0098203a3fec2f3faf349f4cb05165e3", size = 36212940, upload-time = "2025-09-11T17:40:06.013Z" }, + { url = "https://files.pythonhosted.org/packages/d6/fd/0bb911585e12f3abdd603d721d83fc1c7492835e1401a0e6d498d7822b4b/scipy-1.16.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:912f46667d2d3834bc3d57361f854226475f695eb08c08a904aadb1c936b6a88", size = 38865092, upload-time = "2025-09-11T17:40:15.143Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8d/6396e00db1282279a4ddd507c5f5e11f606812b608ee58517ce8abbf883f/scipy-1.16.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:89d6c100fa5c48472047632e06f0876b3c4931aac1f4291afc81a3644316bb0d", size = 36646259, upload-time = "2025-09-11T17:40:39.329Z" }, + { url = "https://files.pythonhosted.org/packages/3b/93/ea9edd7e193fceb8eef149804491890bde73fb169c896b61aa3e2d1e4e77/scipy-1.16.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:ca748936cd579d3f01928b30a17dc474550b01272d8046e3e1ee593f23620371", size = 28888976, upload-time = "2025-09-11T17:40:46.82Z" }, + { url = "https://files.pythonhosted.org/packages/91/4d/281fddc3d80fd738ba86fd3aed9202331180b01e2c78eaae0642f22f7e83/scipy-1.16.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:fac4f8ce2ddb40e2e3d0f7ec36d2a1e7f92559a2471e59aec37bd8d9de01fec0", size = 20879905, upload-time = "2025-09-11T17:40:52.545Z" }, + { url = "https://files.pythonhosted.org/packages/69/40/b33b74c84606fd301b2915f0062e45733c6ff5708d121dd0deaa8871e2d0/scipy-1.16.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:033570f1dcefd79547a88e18bccacff025c8c647a330381064f561d43b821232", size = 23553066, upload-time = "2025-09-11T17:40:59.014Z" }, + { url = "https://files.pythonhosted.org/packages/55/a7/22c739e2f21a42cc8f16bc76b47cff4ed54fbe0962832c589591c2abec34/scipy-1.16.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ea3421209bf00c8a5ef2227de496601087d8f638a2363ee09af059bd70976dc1", size = 33336407, upload-time = "2025-09-11T17:41:06.796Z" }, + { url = "https://files.pythonhosted.org/packages/53/11/a0160990b82999b45874dc60c0c183d3a3a969a563fffc476d5a9995c407/scipy-1.16.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f66bd07ba6f84cd4a380b41d1bf3c59ea488b590a2ff96744845163309ee8e2f", size = 35673281, upload-time = "2025-09-11T17:41:15.055Z" }, + { url = "https://files.pythonhosted.org/packages/96/53/7ef48a4cfcf243c3d0f1643f5887c81f29fdf76911c4e49331828e19fc0a/scipy-1.16.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e9feab931bd2aea4a23388c962df6468af3d808ddf2d40f94a81c5dc38f32ef", size = 36004222, upload-time = "2025-09-11T17:41:23.868Z" }, + { url = "https://files.pythonhosted.org/packages/49/7f/71a69e0afd460049d41c65c630c919c537815277dfea214031005f474d78/scipy-1.16.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03dfc75e52f72cf23ec2ced468645321407faad8f0fe7b1f5b49264adbc29cb1", size = 38664586, upload-time = "2025-09-11T17:41:31.021Z" }, + { url = "https://files.pythonhosted.org/packages/c1/27/c5b52f1ee81727a9fc457f5ac1e9bf3d6eab311805ea615c83c27ba06400/scipy-1.16.2-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:84f7bf944b43e20b8a894f5fe593976926744f6c185bacfcbdfbb62736b5cc70", size = 36604856, upload-time = "2025-09-11T17:41:47.695Z" }, + { url = "https://files.pythonhosted.org/packages/32/a9/15c20d08e950b540184caa8ced675ba1128accb0e09c653780ba023a4110/scipy-1.16.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5c39026d12edc826a1ef2ad35ad1e6d7f087f934bb868fc43fa3049c8b8508f9", size = 28864626, upload-time = "2025-09-11T17:41:52.642Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fc/ea36098df653cca26062a627c1a94b0de659e97127c8491e18713ca0e3b9/scipy-1.16.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e52729ffd45b68777c5319560014d6fd251294200625d9d70fd8626516fc49f5", size = 20855689, upload-time = "2025-09-11T17:41:57.886Z" }, + { url = "https://files.pythonhosted.org/packages/dc/6f/d0b53be55727f3e6d7c72687ec18ea6d0047cf95f1f77488b99a2bafaee1/scipy-1.16.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:024dd4a118cccec09ca3209b7e8e614931a6ffb804b2a601839499cb88bdf925", size = 23512151, upload-time = "2025-09-11T17:42:02.303Z" }, + { url = "https://files.pythonhosted.org/packages/11/85/bf7dab56e5c4b1d3d8eef92ca8ede788418ad38a7dc3ff50262f00808760/scipy-1.16.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7a5dc7ee9c33019973a470556081b0fd3c9f4c44019191039f9769183141a4d9", size = 33329824, upload-time = "2025-09-11T17:42:07.549Z" }, + { url = "https://files.pythonhosted.org/packages/da/6a/1a927b14ddc7714111ea51f4e568203b2bb6ed59bdd036d62127c1a360c8/scipy-1.16.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c2275ff105e508942f99d4e3bc56b6ef5e4b3c0af970386ca56b777608ce95b7", size = 35681881, upload-time = "2025-09-11T17:42:13.255Z" }, + { url = "https://files.pythonhosted.org/packages/c1/5f/331148ea5780b4fcc7007a4a6a6ee0a0c1507a796365cc642d4d226e1c3a/scipy-1.16.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:af80196eaa84f033e48444d2e0786ec47d328ba00c71e4299b602235ffef9acb", size = 36006219, upload-time = "2025-09-11T17:42:18.765Z" }, + { url = "https://files.pythonhosted.org/packages/46/3a/e991aa9d2aec723b4a8dcfbfc8365edec5d5e5f9f133888067f1cbb7dfc1/scipy-1.16.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9fb1eb735fe3d6ed1f89918224e3385fbf6f9e23757cacc35f9c78d3b712dd6e", size = 38682147, upload-time = "2025-09-11T17:42:25.177Z" }, + { url = "https://files.pythonhosted.org/packages/09/d9/60679189bcebda55992d1a45498de6d080dcaf21ce0c8f24f888117e0c2d/scipy-1.16.2-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:53d8d2ee29b925344c13bda64ab51785f016b1b9617849dac10897f0701b20c1", size = 37012682, upload-time = "2025-09-11T17:42:30.677Z" }, + { url = "https://files.pythonhosted.org/packages/83/be/a99d13ee4d3b7887a96f8c71361b9659ba4ef34da0338f14891e102a127f/scipy-1.16.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:9e05e33657efb4c6a9d23bd8300101536abd99c85cca82da0bffff8d8764d08a", size = 29389926, upload-time = "2025-09-11T17:42:35.845Z" }, + { url = "https://files.pythonhosted.org/packages/bf/0a/130164a4881cec6ca8c00faf3b57926f28ed429cd6001a673f83c7c2a579/scipy-1.16.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:7fe65b36036357003b3ef9d37547abeefaa353b237e989c21027b8ed62b12d4f", size = 21381152, upload-time = "2025-09-11T17:42:40.07Z" }, + { url = "https://files.pythonhosted.org/packages/47/a6/503ffb0310ae77fba874e10cddfc4a1280bdcca1d13c3751b8c3c2996cf8/scipy-1.16.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6406d2ac6d40b861cccf57f49592f9779071655e9f75cd4f977fa0bdd09cb2e4", size = 23914410, upload-time = "2025-09-11T17:42:44.313Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c7/1147774bcea50d00c02600aadaa919facbd8537997a62496270133536ed6/scipy-1.16.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff4dc42bd321991fbf611c23fc35912d690f731c9914bf3af8f417e64aca0f21", size = 33481880, upload-time = "2025-09-11T17:42:49.325Z" }, + { url = "https://files.pythonhosted.org/packages/6a/74/99d5415e4c3e46b2586f30cdbecb95e101c7192628a484a40dd0d163811a/scipy-1.16.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:654324826654d4d9133e10675325708fb954bc84dae6e9ad0a52e75c6b1a01d7", size = 35791425, upload-time = "2025-09-11T17:42:54.711Z" }, + { url = "https://files.pythonhosted.org/packages/1b/ee/a6559de7c1cc710e938c0355d9d4fbcd732dac4d0d131959d1f3b63eb29c/scipy-1.16.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:63870a84cd15c44e65220eaed2dac0e8f8b26bbb991456a033c1d9abfe8a94f8", size = 36178622, upload-time = "2025-09-11T17:43:00.375Z" }, + { url = "https://files.pythonhosted.org/packages/4e/7b/f127a5795d5ba8ece4e0dce7d4a9fb7cb9e4f4757137757d7a69ab7d4f1a/scipy-1.16.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:fa01f0f6a3050fa6a9771a95d5faccc8e2f5a92b4a2e5440a0fa7264a2398472", size = 38783985, upload-time = "2025-09-11T17:43:06.661Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ac/ad8951250516db71619f0bd3b2eb2448db04b720a003dd98619b78b692c0/scipy-1.16.2-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:567e77755019bb7461513c87f02bb73fb65b11f049aaaa8ca17cfaa5a5c45d77", size = 36595109, upload-time = "2025-09-11T17:43:35.713Z" }, + { url = "https://files.pythonhosted.org/packages/ff/f6/5779049ed119c5b503b0f3dc6d6f3f68eefc3a9190d4ad4c276f854f051b/scipy-1.16.2-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:17d9bb346194e8967296621208fcdfd39b55498ef7d2f376884d5ac47cec1a70", size = 28859110, upload-time = "2025-09-11T17:43:40.814Z" }, + { url = "https://files.pythonhosted.org/packages/82/09/9986e410ae38bf0a0c737ff8189ac81a93b8e42349aac009891c054403d7/scipy-1.16.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:0a17541827a9b78b777d33b623a6dcfe2ef4a25806204d08ead0768f4e529a88", size = 20850110, upload-time = "2025-09-11T17:43:44.981Z" }, + { url = "https://files.pythonhosted.org/packages/0d/ad/485cdef2d9215e2a7df6d61b81d2ac073dfacf6ae24b9ae87274c4e936ae/scipy-1.16.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:d7d4c6ba016ffc0f9568d012f5f1eb77ddd99412aea121e6fa8b4c3b7cbad91f", size = 23497014, upload-time = "2025-09-11T17:43:49.074Z" }, + { url = "https://files.pythonhosted.org/packages/a7/74/f6a852e5d581122b8f0f831f1d1e32fb8987776ed3658e95c377d308ed86/scipy-1.16.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9702c4c023227785c779cba2e1d6f7635dbb5b2e0936cdd3a4ecb98d78fd41eb", size = 33401155, upload-time = "2025-09-11T17:43:54.661Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f5/61d243bbc7c6e5e4e13dde9887e84a5cbe9e0f75fd09843044af1590844e/scipy-1.16.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d1cdf0ac28948d225decdefcc45ad7dd91716c29ab56ef32f8e0d50657dffcc7", size = 35691174, upload-time = "2025-09-11T17:44:00.101Z" }, + { url = "https://files.pythonhosted.org/packages/03/99/59933956331f8cc57e406cdb7a483906c74706b156998f322913e789c7e1/scipy-1.16.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:70327d6aa572a17c2941cdfb20673f82e536e91850a2e4cb0c5b858b690e1548", size = 36070752, upload-time = "2025-09-11T17:44:05.619Z" }, + { url = "https://files.pythonhosted.org/packages/c6/7d/00f825cfb47ee19ef74ecf01244b43e95eae74e7e0ff796026ea7cd98456/scipy-1.16.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5221c0b2a4b58aa7c4ed0387d360fd90ee9086d383bb34d9f2789fafddc8a936", size = 38701010, upload-time = "2025-09-11T17:44:11.322Z" }, + { url = "https://files.pythonhosted.org/packages/51/b9/60929ce350c16b221928725d2d1d7f86cf96b8bc07415547057d1196dc92/scipy-1.16.2-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:9ea2a3fed83065d77367775d689401a703d0f697420719ee10c0780bcab594d8", size = 37013193, upload-time = "2025-09-11T17:44:16.757Z" }, + { url = "https://files.pythonhosted.org/packages/2a/41/ed80e67782d4bc5fc85a966bc356c601afddd175856ba7c7bb6d9490607e/scipy-1.16.2-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:7280d926f11ca945c3ef92ba960fa924e1465f8d07ce3a9923080363390624c4", size = 29390172, upload-time = "2025-09-11T17:44:21.783Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a3/2f673ace4090452696ccded5f5f8efffb353b8f3628f823a110e0170b605/scipy-1.16.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:8afae1756f6a1fe04636407ef7dbece33d826a5d462b74f3d0eb82deabefd831", size = 21381326, upload-time = "2025-09-11T17:44:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/42/bf/59df61c5d51395066c35836b78136accf506197617c8662e60ea209881e1/scipy-1.16.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:5c66511f29aa8d233388e7416a3f20d5cae7a2744d5cee2ecd38c081f4e861b3", size = 23915036, upload-time = "2025-09-11T17:44:30.527Z" }, + { url = "https://files.pythonhosted.org/packages/91/c3/edc7b300dc16847ad3672f1a6f3f7c5d13522b21b84b81c265f4f2760d4a/scipy-1.16.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:efe6305aeaa0e96b0ccca5ff647a43737d9a092064a3894e46c414db84bc54ac", size = 33484341, upload-time = "2025-09-11T17:44:35.981Z" }, + { url = "https://files.pythonhosted.org/packages/26/c7/24d1524e72f06ff141e8d04b833c20db3021020563272ccb1b83860082a9/scipy-1.16.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7f3a337d9ae06a1e8d655ee9d8ecb835ea5ddcdcbd8d23012afa055ab014f374", size = 35790840, upload-time = "2025-09-11T17:44:41.76Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b7/5aaad984eeedd56858dc33d75efa59e8ce798d918e1033ef62d2708f2c3d/scipy-1.16.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bab3605795d269067d8ce78a910220262711b753de8913d3deeaedb5dded3bb6", size = 36174716, upload-time = "2025-09-11T17:44:47.316Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c2/e276a237acb09824822b0ada11b028ed4067fdc367a946730979feacb870/scipy-1.16.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b0348d8ddb55be2a844c518cd8cc8deeeb8aeba707cf834db5758fc89b476a2c", size = 38790088, upload-time = "2025-09-11T17:44:53.011Z" }, +] + +[[package]] +name = "scipy-openblas32" +version = "0.3.30.0.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/36/8c/0339efcf29621ae123f7c2a3acc7243a20ae26c74c8cc2fa86a6e84c752d/scipy_openblas32-0.3.30.0.2-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:ab85a8ed306e601d748d318572d1dcb158d8a3652d0c114a9d8325f58fe5b356", size = 14544433, upload-time = "2025-07-20T21:36:21.877Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c3/25ac2f353dc8bb201fbcd2445163b59a22de0a264452788f9499e547ad4a/scipy_openblas32-0.3.30.0.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e85c50851bd12d3c3cf7e6464e729e05c3c94b3cf29e11b604e3ef4fb8b4d58d", size = 9402767, upload-time = "2025-07-20T21:36:29.364Z" }, + { url = "https://files.pythonhosted.org/packages/3e/30/9b3e83df98da71694d780ece3560a5ee6686f6b92c80ae9d4c68a2bfdfe7/scipy_openblas32-0.3.30.0.2-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1780974dd181948718d72ddf9c2143936b76ca289fe0f330616e280d71a1dcb4", size = 8233894, upload-time = "2025-07-20T21:36:36.603Z" }, + { url = "https://files.pythonhosted.org/packages/1a/75/786d45d2179ca1027d9d4078f5faabb565a82b7d95d5a233689da516345d/scipy_openblas32-0.3.30.0.2-py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:013414daf808b11379e7d68109babd1781796b8b015ee4a63bd44caa0d0d5f8a", size = 6753689, upload-time = "2025-07-20T21:36:42.895Z" }, + { url = "https://files.pythonhosted.org/packages/45/b7/a21d4fa4a45aaabea052b16f6d9daf7f3cd2999313c82a3e9b62d25516ae/scipy_openblas32-0.3.30.0.2-py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:9629f5d76ff274f4afe3a3cc3a85c94942937187eb805db89eee9268ddbabdf3", size = 9118417, upload-time = "2025-07-20T21:36:53.498Z" }, + { url = "https://files.pythonhosted.org/packages/e6/99/767ad00904959f9a0a7d6bc42034b90bbe60f9aab87137696862e80d785d/scipy_openblas32-0.3.30.0.2-py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b6c2d1ea186104b34590327cd892fc4b5d1f2cf7f631e01b07c634e019b04071", size = 6301963, upload-time = "2025-07-20T21:36:58.862Z" }, + { url = "https://files.pythonhosted.org/packages/01/78/4a113ac866d2cb8190564387050f1402e25d5b9b926111f12d8dd3f9e354/scipy_openblas32-0.3.30.0.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60112d5f051f843dba93602446270ec7c6dd176ad1e3f07f51b0bc3060a43946", size = 8594393, upload-time = "2025-07-20T21:37:04.818Z" }, + { url = "https://files.pythonhosted.org/packages/08/bf/3224e294da644a790769147262bbd0c3eb9b296bc56d196e386e065d1dba/scipy_openblas32-0.3.30.0.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:32fbdb72817d85eb40623657c7947eea84f9f91a33381e2f5f1649c9daddadad", size = 8943438, upload-time = "2025-07-20T21:37:11.582Z" }, + { url = "https://files.pythonhosted.org/packages/17/ab/a2bafcaebdb72c33fe92744023c6c48973cd37fd9b61573dcb1d29bef7be/scipy_openblas32-0.3.30.0.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:783220fcdf737911ddd1da411c0ec4a4b23e332689f80fe0bacfdec0e2bc28d5", size = 9404849, upload-time = "2025-07-20T21:37:18.83Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "termcolor" +version = "3.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ca/6c/3d75c196ac07ac8749600b60b03f4f6094d54e132c4d94ebac6ee0e0add0/termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970", size = 14324, upload-time = "2025-04-30T11:37:53.791Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/bd/de8d508070629b6d84a30d01d57e4a65c69aa7f5abe7560b8fad3b50ea59/termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa", size = 7684, upload-time = "2025-04-30T11:37:52.382Z" }, +] + +[[package]] +name = "tomli" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/87/302344fed471e44a87289cf4967697d07e532f2421fdaf868a303cbae4ff/tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff", size = 17175, upload-time = "2024-11-27T22:38:36.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ca/75707e6efa2b37c77dadb324ae7d9571cb424e61ea73fad7c56c2d14527f/tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249", size = 131077, upload-time = "2024-11-27T22:37:54.956Z" }, + { url = "https://files.pythonhosted.org/packages/c7/16/51ae563a8615d472fdbffc43a3f3d46588c264ac4f024f63f01283becfbb/tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6", size = 123429, upload-time = "2024-11-27T22:37:56.698Z" }, + { url = "https://files.pythonhosted.org/packages/f1/dd/4f6cd1e7b160041db83c694abc78e100473c15d54620083dbd5aae7b990e/tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a", size = 226067, upload-time = "2024-11-27T22:37:57.63Z" }, + { url = "https://files.pythonhosted.org/packages/a9/6b/c54ede5dc70d648cc6361eaf429304b02f2871a345bbdd51e993d6cdf550/tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee", size = 236030, upload-time = "2024-11-27T22:37:59.344Z" }, + { url = "https://files.pythonhosted.org/packages/1f/47/999514fa49cfaf7a92c805a86c3c43f4215621855d151b61c602abb38091/tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e", size = 240898, upload-time = "2024-11-27T22:38:00.429Z" }, + { url = "https://files.pythonhosted.org/packages/73/41/0a01279a7ae09ee1573b423318e7934674ce06eb33f50936655071d81a24/tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4", size = 229894, upload-time = "2024-11-27T22:38:02.094Z" }, + { url = "https://files.pythonhosted.org/packages/55/18/5d8bc5b0a0362311ce4d18830a5d28943667599a60d20118074ea1b01bb7/tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106", size = 245319, upload-time = "2024-11-27T22:38:03.206Z" }, + { url = "https://files.pythonhosted.org/packages/92/a3/7ade0576d17f3cdf5ff44d61390d4b3febb8a9fc2b480c75c47ea048c646/tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8", size = 238273, upload-time = "2024-11-27T22:38:04.217Z" }, + { url = "https://files.pythonhosted.org/packages/52/e1/f8af4c2fcde17500422858155aeb0d7e93477a0d59a98e56cbfe75070fd0/tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea", size = 132762, upload-time = "2024-11-27T22:38:07.731Z" }, + { url = "https://files.pythonhosted.org/packages/03/b8/152c68bb84fc00396b83e7bbddd5ec0bd3dd409db4195e2a9b3e398ad2e3/tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8", size = 123453, upload-time = "2024-11-27T22:38:09.384Z" }, + { url = "https://files.pythonhosted.org/packages/c8/d6/fc9267af9166f79ac528ff7e8c55c8181ded34eb4b0e93daa767b8841573/tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192", size = 233486, upload-time = "2024-11-27T22:38:10.329Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/51c3f2884d7bab89af25f678447ea7d297b53b5a3b5730a7cb2ef6069f07/tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222", size = 242349, upload-time = "2024-11-27T22:38:11.443Z" }, + { url = "https://files.pythonhosted.org/packages/ab/df/bfa89627d13a5cc22402e441e8a931ef2108403db390ff3345c05253935e/tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77", size = 252159, upload-time = "2024-11-27T22:38:13.099Z" }, + { url = "https://files.pythonhosted.org/packages/9e/6e/fa2b916dced65763a5168c6ccb91066f7639bdc88b48adda990db10c8c0b/tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6", size = 237243, upload-time = "2024-11-27T22:38:14.766Z" }, + { url = "https://files.pythonhosted.org/packages/b4/04/885d3b1f650e1153cbb93a6a9782c58a972b94ea4483ae4ac5cedd5e4a09/tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd", size = 259645, upload-time = "2024-11-27T22:38:15.843Z" }, + { url = "https://files.pythonhosted.org/packages/9c/de/6b432d66e986e501586da298e28ebeefd3edc2c780f3ad73d22566034239/tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e", size = 244584, upload-time = "2024-11-27T22:38:17.645Z" }, + { url = "https://files.pythonhosted.org/packages/04/90/2ee5f2e0362cb8a0b6499dc44f4d7d48f8fff06d28ba46e6f1eaa61a1388/tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7", size = 132708, upload-time = "2024-11-27T22:38:21.659Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ec/46b4108816de6b385141f082ba99e315501ccd0a2ea23db4a100dd3990ea/tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c", size = 123582, upload-time = "2024-11-27T22:38:22.693Z" }, + { url = "https://files.pythonhosted.org/packages/a0/bd/b470466d0137b37b68d24556c38a0cc819e8febe392d5b199dcd7f578365/tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13", size = 232543, upload-time = "2024-11-27T22:38:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/d9/e5/82e80ff3b751373f7cead2815bcbe2d51c895b3c990686741a8e56ec42ab/tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281", size = 241691, upload-time = "2024-11-27T22:38:26.081Z" }, + { url = "https://files.pythonhosted.org/packages/05/7e/2a110bc2713557d6a1bfb06af23dd01e7dde52b6ee7dadc589868f9abfac/tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272", size = 251170, upload-time = "2024-11-27T22:38:27.921Z" }, + { url = "https://files.pythonhosted.org/packages/64/7b/22d713946efe00e0adbcdfd6d1aa119ae03fd0b60ebed51ebb3fa9f5a2e5/tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140", size = 236530, upload-time = "2024-11-27T22:38:29.591Z" }, + { url = "https://files.pythonhosted.org/packages/38/31/3a76f67da4b0cf37b742ca76beaf819dca0ebef26d78fc794a576e08accf/tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2", size = 258666, upload-time = "2024-11-27T22:38:30.639Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/5af1293da642aded87e8a988753945d0cf7e00a9452d3911dd3bb354c9e2/tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744", size = 243954, upload-time = "2024-11-27T22:38:31.702Z" }, + { url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.13.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/18/0bbf3884e9eaa38819ebe46a7bd25dcd56b67434402b66a58c4b8e552575/tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1", size = 185207, upload-time = "2025-06-05T07:13:44.947Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/75/8539d011f6be8e29f339c42e633aae3cb73bffa95dd0f9adec09b9c58e85/tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0", size = 38901, upload-time = "2025-06-05T07:13:43.546Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/15/22/9ee70a2574a4f4599c47dd506532914ce044817c7752a79b6a51286319bc/urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", size = 393185, upload-time = "2025-06-18T14:07:41.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc", size = 129795, upload-time = "2025-06-18T14:07:40.39Z" }, +] + +[[package]] +name = "wheel" +version = "0.45.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8a/98/2d9906746cdc6a6ef809ae6338005b3f21bb568bea3165cfc6a243fdc25c/wheel-0.45.1.tar.gz", hash = "sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729", size = 107545, upload-time = "2024-11-23T00:18:23.513Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", size = 72494, upload-time = "2024-11-23T00:18:21.207Z" }, +] diff --git a/pyproject.toml b/pyproject.toml index 07cb5e5ccc..cd18e76d48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -118,6 +118,7 @@ git-only = [ "docs/*", "test/*", "mlir/*", + "plugins/*", ] [tool.scikit-build.cmake.define]