diff --git a/.cirrus.yml b/.cirrus.yml index 7e64908f..c92cda44 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -1,61 +1,110 @@ -build_and_store_wheels: &BUILD_AND_STORE_WHEELS - install_cibuildwheel_script: - - python -m pip install cibuildwheel~=2.14.0 - run_cibuildwheel_script: - - cibuildwheel - wheels_artifacts: - path: "wheelhouse/*" - .clone_script: &clone | if [ -z "$CIRRUS_PR" ]; then - git clone --branch=$CIRRUS_BRANCH https://x-access-token:${CIRRUS_REPO_CLONE_TOKEN}@github.com/${CIRRUS_REPO_FULL_NAME}.git $CIRRUS_WORKING_DIR + git clone --recursive --branch=$CIRRUS_BRANCH https://x-access-token:${CIRRUS_REPO_CLONE_TOKEN}@github.com/${CIRRUS_REPO_FULL_NAME}.git $CIRRUS_WORKING_DIR + git reset --hard $CIRRUS_CHANGE_IN_REPO else git clone https://x-access-token:${CIRRUS_REPO_CLONE_TOKEN}@github.com/${CIRRUS_REPO_FULL_NAME}.git $CIRRUS_WORKING_DIR git fetch origin pull/$CIRRUS_PR/head:pull/$CIRRUS_PR + git checkout $CIRRUS_BASE_BRANCH + git -c user.email="you@example.com" merge --no-commit pull/$CIRRUS_PR + git submodule update --init --recursive fi - git reset --hard $CIRRUS_CHANGE_IN_REPO - git submodule update --init --recursive -linux_aarch64_task: - name: Build Linux aarch64 wheels. +.statistics_script: &statistics | + ccache -s -v + echo $(python -m pip cache dir) + echo $(python -m pip cache list) + +macos_arm64_test_task: + name: 🐍 Test / 🍎 arm64 + alias: macos_arm64_test + clone_script: *clone + macos_instance: + image: ghcr.io/cirruslabs/macos-monterey-xcode:latest + ccache_cache: + folder: .ccache + populate_script: + - mkdir -p .ccache + fingerprint_key: ccache-macosx_arm64 + pip_cache: + folder: /Users/admin/Library/Caches/pip + prepare_env_script: | + brew install python@3.10 ccache pipx ninja nox + ln -s $(which python3.10) python + export PATH=/opt/homebrew/opt/python@3.10/libexec/bin:$PATH + export PATH=/opt/homebrew/opt/ccache/libexec:$PATH + export PATH=/opt/homebrew/opt/pipx/libexec:$PATH + export PATH=/opt/homebrew/opt/ninja/libexec:$PATH + export PATH=/opt/homebrew/opt/nox/libexec:$PATH + echo "PATH=$PATH" >> $CIRRUS_ENV + echo "CCACHE_DIR=$PWD/.ccache" >> $CIRRUS_ENV + run_nox_script: + - nox -s tests-3.10 --verbose + statistics_script: *statistics + +build_and_store_wheels: &BUILD_AND_STORE_WHEELS + install_cibuildwheel_script: + - python -m pip install cibuildwheel~=2.15.0 + run_cibuildwheel_script: + - cibuildwheel + wheels_artifacts: + path: "wheelhouse/*" + +linux_aarch64_wheels_task: + only_if: "$CIRRUS_RELEASE != ''" + name: 🐍 Packaging / 🎡 🐧 arm64 + alias: linux_aarch64_wheels clone_script: *clone compute_engine_instance: image_project: cirrus-images image: family/docker-builder-arm64 architecture: arm64 platform: linux - cpu: 4 - env: - CIBW_SKIP: "*-musllinux_*" - - install_pre_requirements_script: - - apt install -y python3-venv python-is-python3 + cpu: 8 + setup_pyhton_script: + - apt-get install -y python3-venv python-is-python3 <<: *BUILD_AND_STORE_WHEELS -macos_arm64_task: - name: Build macOS arm64 wheels. +macos_arm64_wheels_task: + only_if: "$CIRRUS_RELEASE != ''" + name: 🐍 Packaging / 🎡 🍎 arm64 + alias: macos_arm64_wheels clone_script: *clone macos_instance: image: ghcr.io/cirruslabs/macos-monterey-xcode:latest - - env: - PATH: /opt/homebrew/opt/python@3.10/bin:$PATH - install_pre_requirements_script: - - brew install python@3.10 - - ln -s python3 /opt/homebrew/opt/python@3.10/bin/python + ccache_cache: + folder: .ccache + populate_script: + - mkdir -p .ccache + fingerprint_key: ccache-macosx_arm64-wheels + pip_cache: + folder: /Users/admin/Library/Caches/pip + prepare_env_script: | + brew install python@3.10 ccache pipx ninja nox + ln -s $(which python3.10) python + export PATH=/opt/homebrew/opt/python@3.10/libexec/bin:$PATH + export PATH=/opt/homebrew/opt/ccache/libexec:$PATH + export PATH=/opt/homebrew/opt/pipx/libexec:$PATH + export PATH=/opt/homebrew/opt/ninja/libexec:$PATH + export PATH=/opt/homebrew/opt/nox/libexec:$PATH + echo "PATH=$PATH" >> $CIRRUS_ENV + echo "CCACHE_DIR=$PWD/.ccache" >> $CIRRUS_ENV <<: *BUILD_AND_STORE_WHEELS + statistics_script: *statistics publish_task: - name: Upload to PyPI + name: 🚀 Deploy to PyPI container: { image: "python:3.10-bullseye" } depends_on: - - Build Linux aarch64 wheels. - - Build macOS arm64 wheels. + - linux_aarch64_wheels + - macos_arm64_wheels only_if: "$CIRRUS_RELEASE != ''" env: TWINE_REPOSITORY: pypi TWINE_USERNAME: __token__ - TWINE_PASSWORD: ENCRYPTED[963630b0d5f7e9ad0aba1e51f63d69ecc36ea550ccce3a382cee179da6b286ccc644aebd482f97a919f1f6a1810a0d25] + TWINE_PASSWORD: "ENCRYPTED\ + [853587a6e1033bef6a80fbf7cf11762676107052030566cf\ + 1eca86bfaae3452433a1f05c7e6f7690d027b52d22a217c2]" install_script: pip install twine publish_script: - curl -L https://api.cirrus-ci.com/v1/artifact/build/$CIRRUS_BUILD_ID/wheels.zip -o wheels.zip diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index dfed3180..c9b57a53 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -1,9 +1,6 @@ name: 🐛 Bug report description: Something is not working correctly. title: "🐛 " -labels: ["bug"] -assignees: - - burgholzer body: - type: markdown attributes: @@ -13,35 +10,19 @@ body: ⚠ Verify first that your issue is not [already reported on GitHub](https://github.com/cda-tum/qcec/search?q=is%3Aissue&type=issues). - If you are having general questions, please consider [starting a discussion](https://github.com/cda-tum/qcec/discussions). - - type: markdown - attributes: - value: >- - **Environment** - - type: input - attributes: - label: mqt.qcec version - placeholder: For example, mqt.qcec===2.0.0 - validations: - required: true - - type: input + If you have general questions, please consider [starting a discussion](https://github.com/cda-tum/qcec/discussions). + - type: textarea attributes: - label: OS - placeholder: For example, Ubuntu 22.04, macOS Big Sur, Windows etc. + label: Environment information + description: >- + Please provide information about your environment. For example, OS, C++ compiler, mqt.core version etc. + placeholder: | + - OS: + - C++ compiler: + - mqt.core version: + - Additional environment information: validations: required: true - - type: input - attributes: - label: Python version - placeholder: For example, Python 3.10 - - type: input - attributes: - label: C++ compiler - placeholder: For example, gcc-10 - - type: textarea - attributes: - label: Additional environment information - description: Feel free to add more information about your environment here. - type: textarea attributes: label: Description diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml index 2e6f90e8..812010c3 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yml +++ b/.github/ISSUE_TEMPLATE/feature-request.yml @@ -1,9 +1,6 @@ name: ✨ Feature request description: Suggest an idea title: "✨ <title>" -labels: ["enhancement"] -assignees: - - burgholzer body: - type: markdown attributes: diff --git a/.github/codecov.yml b/.github/codecov.yml index 1712aff5..356666bf 100644 --- a/.github/codecov.yml +++ b/.github/codecov.yml @@ -1,6 +1,6 @@ ignore: - "extern/**/*" - - "mqt/**/*.cpp" + - "**/python" - "test/**/*" coverage: @@ -12,10 +12,12 @@ coverage: flag_management: default_rules: + carryforward: true statuses: - type: project target: auto threshold: 0.5% + removed_code_behavior: adjust_base - type: patch target: 90% threshold: 1% @@ -24,12 +26,15 @@ flag_management: paths: - "include" - "src" + after_n_builds: 1 - name: python paths: - - "mqt/**/*.py" + - "src/mqt/**/*.py" + after_n_builds: 3 statuses: - type: project threshold: 0.5% + removed_code_behavior: adjust_base - type: patch target: 95% threshold: 1% @@ -40,6 +45,7 @@ parsers: conditional: no loop: no -codecov: - notify: - after_n_builds: 4 +comment: + layout: "reach, diff, flags, files" + require_changes: true + show_carryforward_flags: true diff --git a/.github/codeql-config.yml b/.github/codeql-config.yml deleted file mode 100644 index a9899683..00000000 --- a/.github/codeql-config.yml +++ /dev/null @@ -1,4 +0,0 @@ -name: GitHub CodeQL Config - -queries: - - uses: security-and-quality diff --git a/.github/contributing.rst b/.github/contributing.rst index 0d2bfc39..9c34b2e7 100644 --- a/.github/contributing.rst +++ b/.github/contributing.rst @@ -6,7 +6,7 @@ We value contributions from people with all levels of experience. In particular if this is your first pull request not everything has to be perfect. We will guide you through the process. -We use GitHub to `host code <https://github.com/cda-tum/qcec>`_, to `track issues and feature requests <https://github.com/cda-tum/qcec/issues>`_, as well as accept `pull requests <https://github.com/cda-tum/qcec/pulls>`_. +We use GitHub to `host code <https://github.com/cda-tum/mqt-qcec>`_, to `track issues and feature requests <https://github.com/cda-tum/mqt-qcec/issues>`_, as well as accept `pull requests <https://github.com/cda-tum/mqt-qcec/pulls>`_. See https://docs.github.com/en/get-started/quickstart for a general introduction to working with GitHub and contributing to projects. Types of Contributions @@ -15,19 +15,19 @@ Types of Contributions You can contribute in several ways: - 🐛 Report Bugs - Report bugs at https://github.com/cda-tum/qcec/issues using the *🐛 Bug report* issue template. Please make sure to fill out all relevant information in the respective issue form. + Report bugs at https://github.com/cda-tum/mqt-qcec/issues using the *🐛 Bug report* issue template. Please make sure to fill out all relevant information in the respective issue form. - 🐛 Fix Bugs - Look through the `GitHub Issues <https://github.com/cda-tum/qcec/issues>`_ for bugs. Anything tagged with "bug" is open to whoever wants to try and fix it. + Look through the `GitHub Issues <https://github.com/cda-tum/mqt-qcec/issues>`_ for bugs. Anything tagged with "bug" is open to whoever wants to try and fix it. - ✨ Propose New Features - Propose new features at https://github.com/cda-tum/qcec/issues using the *✨ Feature request* issue template. Please make sure to fill out all relevant information in the respective issue form. + Propose new features at https://github.com/cda-tum/mqt-qcec/issues using the *✨ Feature request* issue template. Please make sure to fill out all relevant information in the respective issue form. - ✨ Implement New Features - Look through the `GitHub Issues <https://github.com/cda-tum/qcec/issues>`_ for features. Anything tagged with "feature" is open to whoever wants to implement it. We highly appreciate external contributions to the project. + Look through the `GitHub Issues <https://github.com/cda-tum/mqt-qcec/issues>`_ for features. Anything tagged with "feature" is open to whoever wants to implement it. We highly appreciate external contributions to the project. - 📝 Write Documentation - QCEC could always use some more `documentation <https://qcec.readthedocs.io/en/latest/>`_, and we appreciate any help with that. + QCEC could always use some more `documentation <https://mqt.readthedocs.io/projects/qcec>`_, and we appreciate any help with that. 🎉 Get Started ############## diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2eac078d..ea60e7e9 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,14 +2,21 @@ version: 2 updates: - package-ecosystem: "gitsubmodule" directory: "/" + groups: + submodules: + patterns: + - "*" schedule: interval: "monthly" - day: "friday" time: "06:00" timezone: "Europe/Vienna" - package-ecosystem: "github-actions" directory: "/" + groups: + github-actions: + patterns: + - "*" schedule: interval: "weekly" day: "friday" @@ -18,6 +25,10 @@ updates: - package-ecosystem: "pip" directory: "/" + groups: + python-dependencies: + patterns: + - "*" schedule: interval: "weekly" day: "friday" diff --git a/.github/matchers/pylint.json b/.github/matchers/pylint.json new file mode 100644 index 00000000..e3a6bd16 --- /dev/null +++ b/.github/matchers/pylint.json @@ -0,0 +1,32 @@ +{ + "problemMatcher": [ + { + "severity": "warning", + "pattern": [ + { + "regexp": "^([^:]+):(\\d+):(\\d+): ([A-DF-Z]\\d+): \\033\\[[\\d;]+m([^\\033]+).*$", + "file": 1, + "line": 2, + "column": 3, + "code": 4, + "message": 5 + } + ], + "owner": "pylint-warning" + }, + { + "severity": "error", + "pattern": [ + { + "regexp": "^([^:]+):(\\d+):(\\d+): (E\\d+): \\033\\[[\\d;]+m([^\\033]+).*$", + "file": 1, + "line": 2, + "column": 3, + "code": 4, + "message": 5 + } + ], + "owner": "pylint-error" + } + ] +} diff --git a/.github/support.rst b/.github/support.rst index 16903462..2f5862f5 100644 --- a/.github/support.rst +++ b/.github/support.rst @@ -1,12 +1,12 @@ Support ======= -If you are stuck with a problem using QCEC or are having questions, please do get in touch at our `Issues <https://github.com/cda-tum/qcec/issues>`_ or `Discussions <https://github.com/cda-tum/qcec/discussions>`_. We'd love to help. +If you are stuck with a problem using QCEC or are having questions, please do get in touch at our `Issues <https://github.com/cda-tum/mqt-qcec/issues>`_ or `Discussions <https://github.com/cda-tum/mqt-qcec/discussions>`_. We'd love to help. You can save time by following this procedure when reporting a problem: -- Do try to solve the problem on your own first. Make sure to consult the `Documentation <https://qcec.readthedocs.io/en/latest/>`_. -- Search through past `Issues <https://github.com/cda-tum/qcec/issues>`_ to see if someone else already had the same problem. +- Do try to solve the problem on your own first. Make sure to consult the `Documentation <https://mqt.readthedocs.io/projects/qcec>`_. +- Search through past `Issues <https://github.com/cda-tum/mqt-qcec/issues>`_ to see if someone else already had the same problem. - Before filing a bug report, try to create a minimal working example (MWE) that reproduces the problem. It's much easier to identify the cause for the problem if a handful of lines suffice to show that something isn't working. You can also always reach us at `quantum.cda@xcit.tum.de <mailto:quantum.cda@xcit.tum.de>`_. diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 00000000..ea88cea2 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,27 @@ +name: CD +on: + release: + types: [published] + workflow_dispatch: + +jobs: + python-packaging: + name: 🐍 Packaging + uses: cda-tum/mqt-core/.github/workflows/reusable-python-packaging.yml@main + + deploy: + if: github.event_name == 'release' && github.event.action == 'published' + name: 🚀 Deploy to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/mqt.qcec + permissions: + id-token: write + needs: [python-packaging] + steps: + - uses: actions/download-artifact@v3 + with: + name: artifact + path: dist + - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5ac5dd4..93e782d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,58 +1,83 @@ -name: C++ - +name: CI on: - pull_request: - merge_group: push: branches: - main + pull_request: + merge_group: workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true -env: - CMAKE_BUILD_PARALLEL_LEVEL: 3 - CTEST_PARALLEL_LEVEL: 3 - -defaults: - run: - shell: bash - jobs: + change-detection: + name: 🔍 Change + uses: cda-tum/mqt-core/.github/workflows/reusable-change-detection.yml@main + cpp-tests: - name: Tests ${{ matrix.config.os }} - runs-on: ${{ matrix.config.os }} - strategy: - fail-fast: false - matrix: - config: - - { os: ubuntu-latest, toolchain: "" } - - { os: macos-latest, toolchain: "" } - - { os: windows-latest, toolchain: "-T ClangCl" } + name: 🇨‌ Test + needs: change-detection + if: fromJSON(needs.change-detection.outputs.run-cpp-tests) + uses: cda-tum/mqt-core/.github/workflows/reusable-cpp-ci.yml@main + secrets: + token: ${{ secrets.CODECOV_TOKEN }} + with: + cmake-args: "" + cmake-args-ubuntu: -G Ninja + cmake-args-macos: -G Ninja + cmake-args-windows: -T ClangCL + + cpp-linter: + name: 🇨‌ Lint + needs: change-detection + if: fromJSON(needs.change-detection.outputs.run-cpp-linter) + uses: cda-tum/mqt-core/.github/workflows/reusable-cpp-linter.yml@main + + python-tests: + name: 🐍 Test + needs: change-detection + if: fromJSON(needs.change-detection.outputs.run-python-tests) + uses: cda-tum/mqt-core/.github/workflows/reusable-python-ci.yml@main + secrets: + token: ${{ secrets.CODECOV_TOKEN }} + + code-ql: + name: 📝 CodeQL + needs: change-detection + if: fromJSON(needs.change-detection.outputs.run-code-ql) + uses: cda-tum/mqt-core/.github/workflows/reusable-code-ql.yml@main + + required-checks-pass: # This job does nothing and is only used for branch protection + name: 🚦 Check + if: always() + needs: + - change-detection + - cpp-tests + - cpp-linter + - python-tests + - code-ql + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Configure CMake - run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_QCEC_TESTS=ON ${{ matrix.config.toolchain }} - - name: Build - run: cmake --build build --config Release - - name: Test - run: ctest -C Release --output-on-failure --test-dir build --repeat until-pass:3 --timeout 300 - - if: runner.os == 'Linux' - name: Coverage - run: | - cmake -S . -B buildCov -DCMAKE_BUILD_TYPE=Debug -DBUILD_QCEC_TESTS=ON -DENABLE_COVERAGE=ON - cmake --build buildCov --config Debug --target qcec_test - ctest -C Debug --output-on-failure --test-dir buildCov --repeat until-pass:3 --timeout 300 - - if: runner.os == 'Linux' - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3.1.4 + - name: Decide whether the needed jobs succeeded or failed + uses: re-actors/alls-green@release/v1 with: - fail_ci_if_error: true - flags: cpp - gcov: true - gcov_ignore: "extern/**/*" - token: ${{ secrets.CODECOV_TOKEN }} + allowed-skips: >- + ${{ + fromJSON(needs.change-detection.outputs.run-cpp-tests) + && '' || 'cpp-tests,' + }} + ${{ + fromJSON(needs.change-detection.outputs.run-cpp-linter) + && '' || 'cpp-linter,' + }} + ${{ + fromJSON(needs.change-detection.outputs.run-python-tests) + && '' || 'python-tests,' + }} + ${{ + fromJSON(needs.change-detection.outputs.run-code-ql) + && '' || 'code-ql,' + }} + jobs: ${{ toJSON(needs) }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 64a75ea5..00000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: "CodeQL" - -on: - push: - pull_request: - merge_group: - schedule: - - cron: "15 21 * * 6" - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -env: - CMAKE_BUILD_PARALLEL_LEVEL: 3 - -jobs: - analyze: - name: Analyze ${{ matrix.language }} - runs-on: ubuntu-latest - permissions: - security-events: write - - strategy: - fail-fast: false - matrix: - language: ["cpp", "python"] - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - submodules: recursive - fetch-depth: 0 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - config-file: .github/codeql-config.yml - - - if: matrix.language == 'cpp' - name: Configure CMake - run: cmake -S . -B build -DBINDINGS=ON -DBUILD_QCEC_TESTS=ON - - - if: matrix.language == 'cpp' - name: Build - run: cmake --build build - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 - with: - upload: False - output: sarif-results - - - name: filter-sarif - uses: advanced-security/filter-sarif@main - with: - patterns: | - -**/extern/** - input: sarif-results/${{ matrix.language }}.sarif - output: sarif-results/${{ matrix.language }}.sarif - - - name: Upload SARIF - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: sarif-results/${{ matrix.language }}.sarif diff --git a/.github/workflows/cpp-linter.yml b/.github/workflows/cpp-linter.yml deleted file mode 100644 index 4c99c12a..00000000 --- a/.github/workflows/cpp-linter.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: cpp-linter - -on: - pull_request: - merge_group: - push: - branches: - - main - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - cpp-linter: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - name: Generate compilation database - run: CC=clang-14 CXX=clang++-14 cmake -S . -B build -DBINDINGS=ON -DBUILD_QCEC_TESTS=ON - - name: Run cpp-linter - id: linter - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - pipx run cpp-linter \ - --version=14 \ - --style="" \ - --tidy-checks="" \ - --thread-comments=true \ - --files-changed-only=true \ - --ignore="build" \ - --database=build - - name: Fail if linter found errors - if: steps.linter.outputs.checks-failed > 0 - run: echo "Linter found errors" && exit 1 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index f39a6ea0..00000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: Python Packaging - -on: - release: - types: [published] - pull_request: - merge_group: - push: - branches: [main] - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - build_wheels: - name: ${{ matrix.os }} wheels - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - submodules: recursive - - uses: ilammy/msvc-dev-cmd@v1 - - name: Build wheels - uses: pypa/cibuildwheel@v2.14 - - uses: actions/upload-artifact@v3 - with: - path: ./wheelhouse/*.whl - - build_sdist: - name: sdist - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - submodules: recursive - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: "3.10" - - name: Build SDist - run: pipx run build --sdist - - name: Install sdist - run: python -m pip install --verbose dist/*.tar.gz - - uses: actions/upload-artifact@v3 - with: - path: dist/*.tar.gz - - upload_pypi: - needs: [build_wheels, build_sdist] - runs-on: ubuntu-latest - if: github.event_name == 'release' && github.event.action == 'published' - steps: - - uses: actions/download-artifact@v3 - with: - name: artifact - path: dist - - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.pypi_password }} - skip-existing: true - verbose: true diff --git a/.github/workflows/emulated-wheels.yml b/.github/workflows/emulated-wheels.yml deleted file mode 100644 index 96228e00..00000000 --- a/.github/workflows/emulated-wheels.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Python Packaging Emulated Wheels - -on: - release: - types: [published] - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - build_wheels_emulation: - name: ${{ matrix.python }} wheels on ${{ matrix.arch }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - arch: ["s390x", "ppc64le"] - python: ["cp38-*", "cp39-*", "cp310-*", "cp311-*"] - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - submodules: recursive - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Build wheels - uses: pypa/cibuildwheel@v2.14 - env: - CIBW_ARCHS_LINUX: ${{ matrix.arch }} - CIBW_BUILD: ${{ matrix.python }} - CIBW_TEST_SKIP: "cp*" - - uses: actions/upload-artifact@v3 - with: - path: ./wheelhouse/*.whl - - upload_pypi: - needs: [build_wheels_emulation] - runs-on: ubuntu-latest - if: github.event_name == 'release' && github.event.action == 'published' - steps: - - uses: actions/download-artifact@v3 - with: - name: artifact - path: dist - - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.pypi_password }} - skip_existing: true - verbose: true diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml deleted file mode 100644 index 765e0abb..00000000 --- a/.github/workflows/python-ci.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: Python - -on: - pull_request: - merge_group: - push: - branches: - - main - workflow_dispatch: - -env: - FORCE_COLOR: 3 - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - python-tests: - name: 🐍 ${{ matrix.python-version }} Tests on ${{ matrix.runs-on }} - runs-on: ${{ matrix.runs-on }} - strategy: - fail-fast: false - matrix: - runs-on: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.8", "3.11"] - include: - - runs-on: ubuntu-latest - python-version: "3.9" - - runs-on: ubuntu-latest - python-version: "3.10" - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - fetch-depth: 0 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Setup nox - run: pipx install nox - - name: Test on 🐍 ${{ matrix.python-version }} - run: nox -s tests-${{ matrix.python-version }} - - min-qiskit-version: - name: ⚛️ Min. Qiskit Test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - fetch-depth: 0 - - name: Setup nox - run: pipx install nox - - name: Run session - run: nox -s min_qiskit_version -- --cov-report=xml - - name: Upload Coverage to Codecov - uses: codecov/codecov-action@v3.1.4 - with: - fail_ci_if_error: true - flags: python - token: ${{ secrets.CODECOV_TOKEN }} - - coverage: - name: 🐍 ${{ matrix.python-version }} Coverage - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - python-version: ["3.8", "3.11"] - steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - fetch-depth: 0 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Setup nox - run: pipx install nox - - name: Run session - run: nox -s coverage-${{ matrix.python-version }} -- --cov-report=xml - - name: Upload Coverage to Codecov - uses: codecov/codecov-action@v3.1.4 - with: - fail_ci_if_error: true - flags: python - token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 566753dd..ce8c4f17 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -4,8 +4,6 @@ on: push: branches: - main - pull_request: - types: [opened, reopened, synchronize] pull_request_target: types: [opened, reopened, synchronize] @@ -14,6 +12,7 @@ permissions: jobs: update_release_draft: + name: Run permissions: contents: write pull-requests: write @@ -21,4 +20,4 @@ jobs: steps: - uses: release-drafter/release-drafter@v5 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ github.token }} diff --git a/.gitignore b/.gitignore index b0d6c004..3e3ac324 100644 --- a/.gitignore +++ b/.gitignore @@ -1,48 +1,171 @@ -/build* -/cmake-build-* -.idea/* -!.idea/runConfigurations -!.idea/cmake.xml -.vscode/* -*~ - +# Byte-compiled / optimized / DLL files __pycache__/ -*.so *.py[cod] +*$py.class -*.egg -*.eggs -*.egg-info -bin/ -build/ +# C extensions +*.so +.ccache/ +cmake-build-* + +# Distribution / packaging +.Python +/build/ +/test/*/build develop-eggs/ dist/ +downloads/ eggs/ +.eggs/ lib/ lib64/ parts/ sdist/ var/ +wheels/ +share/python-wheels/ +*.egg-info/ .installed.cfg +*.egg +MANIFEST -.cache/ -.pytest_cache/ +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ .coverage -.coverage* -.tox -.nox +.coverage.* +.cache +nosetests.xml coverage.xml -htmlcov +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ -docs/_build +# Celery stuff +celerybeat-schedule +celerybeat.pid -.env -.venv -env/ -venv/ +# SageMath parsed files +*.sage.py + +# Environments +.env* +.venv* +env*/ +venv*/ ENV/ env.bak/ venv.bak/ -.ruff_cache/ +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy .mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# setuptools_scm +src/*/_version.py + +# SKBuild cache dir +_skbuild/ + +# Any build dirs in the tests +test/**/build/ +/src/mqt/qcec/_version.py + +# Common editor files +*~ +*.swp + +# RPM spec file +!/distro/*.spec +/distro/*.tar.gz +*.rpm + +# ruff +.ruff_cache/ + +# OS specific stuff +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +.idea/ +.vscode/ +# tmt setup +/distro/main.fmf +/distro/plans/main.fmf +/distro/tests/main.fmf + +/docs/**/build +.vs +out/build diff --git a/.idea/cmake.xml b/.idea/cmake.xml deleted file mode 100644 index 5645856a..00000000 --- a/.idea/cmake.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="CMakeSharedSettings"> - <configurations> - <configuration PROFILE_NAME="Debug" ENABLED="true" CONFIG_NAME="Debug" GENERATION_OPTIONS="-DBUILD_QCEC_TESTS=ON -DBINDINGS=ON" /> - <configuration PROFILE_NAME="Release" ENABLED="true" CONFIG_NAME="Release" GENERATION_OPTIONS="-DBUILD_QCEC_TESTS=ON -DBINDINGS=ON" /> - </configurations> - </component> -</project> diff --git a/.idea/runConfigurations/pip.xml b/.idea/runConfigurations/pip.xml deleted file mode 100644 index acc07a30..00000000 --- a/.idea/runConfigurations/pip.xml +++ /dev/null @@ -1,23 +0,0 @@ -<component name="ProjectRunConfigurationManager"> - <configuration default="false" name="pip" type="PythonConfigurationType" factoryName="Python" singleton="false" nameIsGenerated="true"> - <module name="qcec" /> - <option name="INTERPRETER_OPTIONS" value="" /> - <option name="PARENT_ENVS" value="true" /> - <envs> - <env name="PYTHONUNBUFFERED" value="1" /> - </envs> - <option name="SDK_HOME" value="$PROJECT_DIR$/venv/bin/python" /> - <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" /> - <option name="IS_MODULE_SDK" value="true" /> - <option name="ADD_CONTENT_ROOTS" value="true" /> - <option name="ADD_SOURCE_ROOTS" value="true" /> - <option name="SCRIPT_NAME" value="pip" /> - <option name="PARAMETERS" value="install --editable ".[dev]" -vvv" /> - <option name="SHOW_COMMAND_LINE" value="false" /> - <option name="EMULATE_TERMINAL" value="false" /> - <option name="MODULE_MODE" value="true" /> - <option name="REDIRECT_INPUT" value="false" /> - <option name="INPUT_FILE" value="" /> - <method v="2" /> - </configuration> -</component> diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d8ac162f..d0a9e3de 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,11 +10,12 @@ ci: autoupdate_commit_msg: "⬆️🪝 update pre-commit hooks" autofix_commit_msg: "🎨 pre-commit fixes" + skip: [mypy] repos: # Standard hooks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-added-large-files - id: check-case-conflict @@ -29,7 +30,7 @@ repos: # Clean jupyter notebooks - repo: https://github.com/srstevenson/nb-clean - rev: 2.4.0 + rev: 3.0.1 hooks: - id: nb-clean @@ -50,24 +51,24 @@ repos: # Run ruff (subsumes pyupgrade, isort, flake8+plugins, and more) - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.0.282 + rev: v0.0.292 hooks: - id: ruff args: ["--fix", "--show-fixes"] # Run code formatting with Black - - repo: https://github.com/psf/black - rev: 23.7.0 # Keep in sync with blacken-docs + - repo: https://github.com/psf/black-pre-commit-mirror + rev: 23.9.1 # Keep in sync with blacken-docs hooks: - id: black-jupyter # Also run Black on examples in the documentation - - repo: https://github.com/asottile/blacken-docs - rev: 1.15.0 + - repo: https://github.com/adamchainz/blacken-docs + rev: 1.16.0 hooks: - id: blacken-docs additional_dependencies: - - black==23.7.0 # keep in sync with black hook + - black==23.9.1 # keep in sync with black hook # CMake format and lint the CMakeLists.txt files - repo: https://github.com/cheshirekow/cmake-format-precommit @@ -87,27 +88,46 @@ repos: # Format configuration files with prettier - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v3.0.1" + rev: v3.0.3 hooks: - id: prettier types_or: [yaml, markdown, html, css, scss, javascript, json] # Check static types with mypy - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.4.1 + rev: v1.5.1 hooks: - id: mypy - files: ^(mqt/qcec|test/python|setup.py) + files: ^(src/mqt|test/python) args: ["--enable-incomplete-feature=Unpack"] additional_dependencies: - importlib_resources - numpy - pytest - - types-setuptools # Check for spelling - repo: https://github.com/codespell-project/codespell - rev: v2.2.5 + rev: v2.2.6 hooks: - id: codespell args: ["-L", "wille,linz", "--skip", "*.ipynb"] + + # Catch common capitalization mistakes + - repo: local + hooks: + - id: disallow-caps + name: Disallow improper capitalization + language: pygrep + entry: PyBind|Numpy|Cmake|CCache|Github|PyTest|Mqt|Tum + exclude: .pre-commit-config.yaml + + # Checking sdist validity + - repo: https://github.com/henryiii/check-sdist + rev: v0.1.3 + hooks: + - id: check-sdist + args: [--inject-junk] + additional_dependencies: + - scikit-build-core[pyproject]>=0.5.0 + - setuptools-scm>=7 + - pybind11>=2.11 diff --git a/.readthedocs.yaml b/.readthedocs.yaml index c43c13fe..4d7470aa 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -8,8 +8,16 @@ build: os: ubuntu-22.04 tools: python: "3.11" - apt_packages: - - cmake + jobs: + post_checkout: + # Skip docs build if the commit message contains "skip ci" + - (git --no-pager log --pretty="tformat:%s -- %b" -1 | grep -viq "skip ci") || exit 183 + # Skip docs build if there are no changes related to docs + - | + if [ "$READTHEDOCS_VERSION_TYPE" = "external" ] && git diff --quiet origin/main -- docs/ .readthedocs.yaml src/mqt/ src/python include/python .github/contributing* .github/workflows/support*; + then + exit 183; + fi sphinx: configuration: docs/source/conf.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 8442cd82..3396a02f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,5 @@ # set required cmake version -cmake_minimum_required(VERSION 3.19) - -# This avoids googletest complaining that this (IPO) policy is not set -cmake_policy(SET CMP0069 NEW) -set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) +cmake_minimum_required(VERSION 3.19...3.27) project( qcec @@ -23,19 +19,36 @@ endmacro() check_submodule_present(mqt-core) +option(BUILD_MQT_QCEC_BINDINGS "Build the MQT QCEC Python bindings" OFF) +if(BUILD_MQT_QCEC_BINDINGS) + # ensure that the BINDINGS option is set + set(BINDINGS + ON + CACHE BOOL "Enable settings related to Python bindings" FORCE) + # cmake-lint: disable=C0103 + set(Python_FIND_VIRTUALENV + FIRST + CACHE STRING "Give precedence to virtualenvs when searching for Python") + # cmake-lint: disable=C0103 + set(Python_ARTIFACTS_INTERACTIVE + ON + CACHE + BOOL + "Prevent multiple searches for Python and instead cache the results.") + # top-level call to find Python + find_package( + Python 3.8 REQUIRED + COMPONENTS Interpreter Development.Module + OPTIONAL_COMPONENTS Development.SABIModule) +endif() + # add main library code add_subdirectory(src) # add test code -option(BUILD_QCEC_TESTS "Also build tests for QMAP project") -if(BUILD_QCEC_TESTS) +option(BUILD_MQT_QCEC_TESTS "Also build tests for the MQT QCEC project" ON) +if(BUILD_MQT_QCEC_TESTS) enable_testing() include(GoogleTest) add_subdirectory(test) endif() - -# add Python binding code -option(BINDINGS "Configure for building Python bindings") -if(BINDINGS) - add_subdirectory(mqt/qcec) -endif() diff --git a/LICENSE.md b/LICENSE.md index e1cb6596..207d0e91 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Chair for Design Automation, Technical University of Munich +Copyright (c) 2023 Chair for Design Automation, Technical University of Munich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 837d28a8..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,22 +0,0 @@ -prune docs -prune test -prune .idea - -graft extern/mqt-core -prune **/.github -prune **/apps -prune **/doc -prune **/docs -prune **/meta -prune **/plots -prune **/test -prune **/tests - -prune extern/mqt-core/extern/json/include -prune extern/mqt-core/extern/googletest -prune extern/mqt-core/extern/boost/config/checks -prune extern/mqt-core/extern/boost/config/tools -prune extern/mqt-core/extern/boost/multiprecision/config -prune extern/mqt-core/extern/boost/multiprecision/example -prune extern/mqt-core/extern/boost/multiprecision/performance -prune extern/mqt-core/extern/boost/multiprecision/tools diff --git a/README.md b/README.md index 48510da1..8fb9d300 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ [![PyPI](https://img.shields.io/pypi/v/mqt.qcec?logo=pypi&style=flat-square)](https://pypi.org/project/mqt.qcec/) ![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) -[![CI](https://img.shields.io/github/actions/workflow/status/cda-tum/qcec/ci.yml?branch=main&style=flat-square&logo=github&label=c%2B%2B)](https://github.com/cda-tum/qcec/actions/workflows/ci.yml) -[![Python CI](https://img.shields.io/github/actions/workflow/status/cda-tum/qcec/python-ci.yml?branch=main&style=flat-square&logo=github&label=python)](https://github.com/cda-tum/qcec/actions/workflows/python-ci.yml) -[![Bindings](https://img.shields.io/github/actions/workflow/status/cda-tum/qcec/deploy.yml?branch=main&style=flat-square&logo=github&label=packaging)](https://github.com/cda-tum/qcec/actions/workflows/deploy.yml) -[![Documentation](https://img.shields.io/readthedocs/qcec?logo=readthedocs&style=flat-square)](https://qcec.readthedocs.io/en/latest/) -[![codecov](https://img.shields.io/codecov/c/github/cda-tum/qcec?style=flat-square&logo=codecov)](https://codecov.io/gh/cda-tum/qcec) +[![CI](https://img.shields.io/github/actions/workflow/status/cda-tum/mqt-qcec/ci.yml?branch=main&style=flat-square&logo=github&label=ci)](https://github.com/cda-tum/mqt-qcec/actions/workflows/ci.yml) +[![CD](https://img.shields.io/github/actions/workflow/status/cda-tum/mqt-qcec/cd.yml?style=flat-square&logo=github&label=cd)](https://github.com/cda-tum/mqt-qcec/actions/workflows/cd.yml) +[![Documentation](https://img.shields.io/readthedocs/qcec?logo=readthedocs&style=flat-square)](https://mqt.readthedocs.io/projects/qcec) +[![codecov](https://img.shields.io/codecov/c/github/cda-tum/mqt-qcec?style=flat-square&logo=codecov)](https://codecov.io/gh/cda-tum/mqt-qcec) <p align="center"> <picture> @@ -16,21 +15,20 @@ # MQT QCEC - A tool for Quantum Circuit Equivalence Checking -A tool for quantum circuit equivalence checking developed as part of the _Munich Quantum Toolkit_ (_MQT_)[^1] by the [Chair for Design Automation](https://www.cda.cit.tum.de/) at the [Technical University of Munich](https://www.tum.de/). -It builds upon [our quantum functionality representation (QFR)](https://github.com/cda-tum/qfr), [our decision diagram (DD) package](https://github.com/cda-tum/dd_package.git), and [our ZX-diagram package](https://github.com/cda-tum/zx.git) -. +A tool for quantum circuit equivalence checking developed as part of the [_Munich Quantum Toolkit_](https://mqt.readthedocs.io) (_MQT_)[^1] by the [Chair for Design Automation](https://www.cda.cit.tum.de/) at the [Technical University of Munich](https://www.tum.de/). +It builds upon [MQT Core](https://github.com/cda-tum/mqt-core), which forms the backbone of the MQT. <p align="center"> - <a href="https://qcec.readthedocs.io/en/latest/"> + <a href="https://mqt.readthedocs.io/projects/qcec"> <img width=30% src="https://img.shields.io/badge/documentation-blue?style=for-the-badge&logo=read%20the%20docs" alt="Documentation" /> </a> </p> -If you have any questions, feel free to contact us via [quantum.cda@xcit.tum.de](mailto:quantum.cda@xcit.tum.de) or by creating an issue on [GitHub](https://github.com/cda-tum/qcec/issues). +If you have any questions, feel free to contact us via [quantum.cda@xcit.tum.de](mailto:quantum.cda@xcit.tum.de) or by creating an issue on [GitHub](https://github.com/cda-tum/mqt-qcec/issues). ## Getting Started -QCEC is available via [PyPI](https://pypi.org/project/mqt.qcec/) for Linux, macOS, and Windows and supports Python 3.8 to 3.11. +QCEC is available via [PyPI](https://pypi.org/project/mqt.qcec/) for Linux, macOS, and Windows and supports Python 3.8 to 3.12. ```console (venv) $ pip install mqt.qcec @@ -48,12 +46,12 @@ result = qcec.verify("circ1.qasm", "circ2.qasm") print(result.equivalence) ``` -**Detailed documentation on all available methods, options, and input formats is available at [ReadTheDocs](https://qcec.readthedocs.io/en/latest/).** +**Detailed documentation on all available methods, options, and input formats is available at [ReadTheDocs](https://mqt.readthedocs.io/projects/qcec).** ## System Requirements and Building The implementation is compatible with any C++17 compiler, a minimum CMake version of 3.19, and Python 3.8+. -Please refer to the [documentation](https://qcec.readthedocs.io/en/latest/) on how to build the project. +Please refer to the [documentation](https://mqt.readthedocs.io/projects/qcec) on how to build the project. Building (and running) is continuously tested under Linux, macOS, and Windows using the [latest available system versions for GitHub Actions](https://github.com/actions/virtual-environments). diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index d0c3cbf1..00000000 --- a/docs/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -# Minimal makefile for Sphinx documentation -# - -# You can set these variables from the command line, and also -# from the environment for the first two. -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = source -BUILDDIR = build - -# Put it first so that "make" without argument is like "make help". -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 061f32f9..00000000 --- a/docs/make.bat +++ /dev/null @@ -1,35 +0,0 @@ -@ECHO OFF - -pushd %~dp0 - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set SOURCEDIR=source -set BUILDDIR=build - -if "%1" == "" goto help - -%SPHINXBUILD% >NUL 2>NUL -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.https://www.sphinx-doc.org/ - exit /b 1 -) - -%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% -goto end - -:help -%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% - -:end -popd diff --git a/docs/source/DevelopmentGuide.rst b/docs/source/DevelopmentGuide.rst index b5a586f9..e35e784a 100644 --- a/docs/source/DevelopmentGuide.rst +++ b/docs/source/DevelopmentGuide.rst @@ -6,13 +6,13 @@ Ready to contribute to the project? Here is how to set up a local development en Initial Setup ############# -1. Fork the `cda-tum/qcec <https://github.com/cda-tum/qcec>`_ repository on GitHub (see https://docs.github.com/en/get-started/quickstart/fork-a-repo). +1. Fork the `cda-tum/qcec <https://github.com/cda-tum/mqt-qcec>`_ repository on GitHub (see https://docs.github.com/en/get-started/quickstart/fork-a-repo). 2. Clone your fork locally .. code-block:: console - $ git clone git@github.com:your_name_here/qcec --recursive + $ git clone git@github.com:your_name_here/mqt-qcec --recursive .. warning:: @@ -23,7 +23,7 @@ Initial Setup .. code-block:: console - $ cd qcec + $ cd mqt-qcec 4. Create a branch for local development diff --git a/docs/source/Installation.rst b/docs/source/Installation.rst index df029391..e0f22239 100644 --- a/docs/source/Installation.rst +++ b/docs/source/Installation.rst @@ -1,7 +1,7 @@ Installation ============ -QCEC is mainly developed as a C++ library that builds upon `our quantum functionality representation (QFR) <https://github.com/cda-tum/qfr>`_, `our decision diagram (DD) package <https://github.com/cda-tum/dd_package.git>`_, and `our ZX-diagram package <https://github.com/cda-tum/zx.git>`_. +QCEC is mainly developed as a C++ library that builds upon `MQT Core <https://github.com/cda-tum/mqt-core>`_, which forms the backbone of the `MQT <https://mqt.readthedocs.io>`_. In order to make the tool as accessible as possible, it comes with an easy-to-use Python interface. We encourage installing QCEC via pip (preferably in a `virtual environment <https://docs.python.org/3/library/venv.html>`_): diff --git a/docs/source/conf.py b/docs/source/conf.py index 886aeb9b..73110f24 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -1,19 +1,37 @@ """Sphinx configuration file.""" from __future__ import annotations -import sys - -if sys.version_info < (3, 10, 0): - import importlib_metadata as metadata -else: - from importlib import metadata - +import warnings +from importlib import metadata +from pathlib import Path from typing import TYPE_CHECKING import pybtex.plugin from pybtex.style.formatting.unsrt import Style as UnsrtStyle from pybtex.style.template import field, href +ROOT = Path(__file__).parent.parent.resolve() + + +try: + from mqt.qcec import __version__ as version +except ModuleNotFoundError: + try: + version = metadata.version("mqt.qcec") + except ModuleNotFoundError: + msg = ( + "Package should be installed to produce documentation! " + "Assuming a modern git archive was used for version discovery." + ) + warnings.warn(msg, stacklevel=1) + + from setuptools_scm import get_version + + version = get_version(root=str(ROOT), fallback_root=ROOT) + +# Filter git details from version +release = version.split("+")[0] + if TYPE_CHECKING: from pybtex.database import Entry from pybtex.richtext import HRef @@ -21,20 +39,17 @@ # -- Project information ----------------------------------------------------- project = "QCEC" author = "Lukas Burgholzer" - -release = metadata.version("mqt.qcec") -version = ".".join(release.split(".")[:3]) language = "en" project_copyright = "Chair for Design Automation, Technical University of Munich" # -- General configuration --------------------------------------------------- extensions = [ + "sphinx.ext.napoleon", "sphinx.ext.autodoc", - "sphinx.ext.autosectionlabel", - "sphinx.ext.intersphinx", "sphinx.ext.autosummary", "sphinx.ext.mathjax", - "sphinx.ext.napoleon", + "sphinx.ext.intersphinx", + "sphinx.ext.autosectionlabel", "sphinx.ext.viewcode", "sphinx.ext.githubpages", "sphinxcontrib.bibtex", @@ -45,6 +60,24 @@ "sphinx_autodoc_typehints", ] +pygments_style = "colorful" + +add_module_names = False + +modindex_common_prefix = ["mqt.qcec."] + +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + "typing_extensions": ("https://typing-extensions.readthedocs.io/en/latest/", None), + "qiskit": ("https://qiskit.org/documentation/", None), + "mqt": ("https://mqt.readthedocs.io/en/latest/", None), + "core": ("https://mqt.readthedocs.io/projects/core/en/latest/", None), + "ddsim": ("https://mqt.readthedocs.io/projects/ddsim/en/latest/", None), + "qmap": ("https://mqt.readthedocs.io/projects/qmap/en/latest/", None), + "qecc": ("https://mqt.readthedocs.io/projects/qecc/en/latest/", None), + "syrec": ("https://mqt.readthedocs.io/projects/syrec/en/latest/", None), +} + nbsphinx_execute = "auto" highlight_language = "python3" nbsphinx_execute_arguments = [ @@ -71,11 +104,6 @@ } exclude_patterns = ["_build", "build", "**.ipynb_checkpoints", "Thumbs.db", ".DS_Store", ".env"] -typehints_use_signature = True -typehints_use_signature_return = True -typehints_use_rtype = False -napoleon_use_rtype = False - class CDAStyle(UnsrtStyle): """Custom style for including PDF links.""" @@ -97,14 +125,18 @@ def format_url(self, _e: Entry) -> HRef: autosummary_generate = True +typehints_use_rtype = False +napoleon_use_rtype = False +napoleon_google_docstring = True +napoleon_numpy_docstring = False + # -- Options for HTML output ------------------------------------------------- html_theme = "furo" -html_baseurl = "https://qcec.readthedocs.io/en/latest/" html_static_path = ["_static"] html_theme_options = { "light_logo": "mqt_dark.png", "dark_logo": "mqt_light.png", - "source_repository": "https://github.com/cda-tum/qcec/", + "source_repository": "https://github.com/cda-tum/mqt-qcec/", "source_branch": "main", "source_directory": "docs/source", "navigation_with_keys": True, diff --git a/docs/source/index.rst b/docs/source/index.rst index 7ace1fca..caba4e09 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,7 +1,7 @@ Welcome to QCEC's documentation! ================================ -QCEC is a tool for :doc:`quantum circuit equivalence checking <EquivalenceChecking>` developed as part of the *Munich Quantum Toolkit* (*MQT*) [#]_ by the `Chair for Design Automation <https://www.cda.cit.tum.de/>`_ at the `Technical University of Munich <https://www.tum.de>`_. It builds upon `our quantum functionality representation (QFR) <https://github.com/cda-tum/qfr>`_, `our decision diagram (DD) package <https://github.com/cda-tum/dd_package.git>`_, and `our ZX-diagram package <https://github.com/cda-tum/zx.git>`_. +QCEC is a tool for :doc:`quantum circuit equivalence checking <EquivalenceChecking>` developed as part of the `Munich Quantum Toolkit <https://mqt.readthedocs.io>`_ (*MQT*) [#]_ by the `Chair for Design Automation <https://www.cda.cit.tum.de/>`_ at the `Technical University of Munich <https://www.tum.de>`_. It builds upon `MQT Core <https://github.com/cda-tum/mqt-core>`_, which forms the backbone of the MQT. We recommend you to start with the :doc:`installation instructions <Installation>`. Then proceed to the :doc:`quickstart guide <Quickstart>` and read the :doc:`reference documentation <library/Library>`. diff --git a/docs/source/library/ApplicationScheme.rst b/docs/source/library/ApplicationScheme.rst index 1b1b8e54..e2238906 100644 --- a/docs/source/library/ApplicationScheme.rst +++ b/docs/source/library/ApplicationScheme.rst @@ -8,6 +8,8 @@ See :doc:`/CompilationFlowVerification` for more information on the dedicated ap In case of the other checkers, which consider both circuits individually, using a non-sequential application scheme can significantly boost the operation caching performance in the underlying decision diagram package. + .. autoclass:: mqt.qcec.types.ApplicationSchemeName + .. autoclass:: mqt.qcec.ApplicationScheme :undoc-members: :members: diff --git a/docs/source/library/Configuration.rst b/docs/source/library/Configuration.rst index a06f660a..95712278 100644 --- a/docs/source/library/Configuration.rst +++ b/docs/source/library/Configuration.rst @@ -4,7 +4,7 @@ Configuration .. currentmodule:: mqt.qcec .. autoclass:: Configuration -The :class:`Configuration` class provides all the means to configure QCEC. All of the options are split into the following categories: +The :class:`~mqt.qcec.Configuration` class provides all the means to configure QCEC. All of the options are split into the following categories: .. toctree:: :maxdepth: 2 @@ -23,6 +23,6 @@ All of these options can be passed to the :meth:`~mqt.qcec.verify` and :meth:`~m :members: :undoc-members: -There, they are incorporated into the :class:`Configuration` using the :func:`~mqt.qcec.configuration.augment_config_from_kwargs` function. +There, they are incorporated into the :class:`~mqt.qcec.Configuration` using the :func:`~mqt.qcec.configuration.augment_config_from_kwargs` function. .. autofunction:: augment_config_from_kwargs diff --git a/docs/source/library/EquivalenceCheckingManager.rst b/docs/source/library/EquivalenceCheckingManager.rst index f269580e..ae5a85a3 100644 --- a/docs/source/library/EquivalenceCheckingManager.rst +++ b/docs/source/library/EquivalenceCheckingManager.rst @@ -17,7 +17,7 @@ This constructs the manager using all the default options. The circuits to be ve * Python objects: - * `QuantumCircuit` from IBM's `Qiskit <https://github.com/Qiskit/qiskit>`_ **(preferred)** + * :class:`qiskit.circuit.QuantumCircuit` from IBM's `Qiskit <https://github.com/Qiskit/qiskit>`_ **(preferred)** * `QasmQobjExperiment` from IBM's `Qiskit <https://github.com/Qiskit/qiskit>`_ * Files diff --git a/docs/source/library/EquivalenceCriterion.rst b/docs/source/library/EquivalenceCriterion.rst index 60912283..195514f8 100644 --- a/docs/source/library/EquivalenceCriterion.rst +++ b/docs/source/library/EquivalenceCriterion.rst @@ -5,6 +5,8 @@ Notions of Equivalence This class captures all the different notions of equivalence that can be the :class:`result <mqt.qcec.EquivalenceCheckingManager.Results>` of a :func:`~mqt.qcec.EquivalenceCheckingManager.run`. + .. autoclass:: mqt.qcec.types.EquivalenceCriterionName + .. autoclass:: mqt.qcec.EquivalenceCriterion :undoc-members: :members: diff --git a/docs/source/library/StateType.rst b/docs/source/library/StateType.rst index a15911aa..8e4c65fd 100644 --- a/docs/source/library/StateType.rst +++ b/docs/source/library/StateType.rst @@ -11,6 +11,8 @@ The type of states that is used in the :ref:`simulation checker <EquivalenceChec For details, see :cite:p:`burgholzer2021randomStimuliGenerationQuantum`. + .. autoclass:: mqt.qcec.types.StateTypeName + .. autoclass:: mqt.qcec.StateType :undoc-members: :members: diff --git a/extern/mqt-core b/extern/mqt-core index 3ea94709..c6eb0673 160000 --- a/extern/mqt-core +++ b/extern/mqt-core @@ -1 +1 @@ -Subproject commit 3ea94709c1f2d38301db2faa2372a3dabaee86ff +Subproject commit c6eb0673bd7e9aa5a1297bd81925e9e7a50aed3f diff --git a/include/checker/dd/DDSimulationChecker.hpp b/include/checker/dd/DDSimulationChecker.hpp index ce581196..ba18bb80 100644 --- a/include/checker/dd/DDSimulationChecker.hpp +++ b/include/checker/dd/DDSimulationChecker.hpp @@ -19,13 +19,13 @@ class DDSimulationChecker final void setRandomInitialState(StateGenerator& generator); [[nodiscard]] dd::CVec getInitialVector() const { - return dd->getVector(initialState); + return initialState.getVector(); } [[nodiscard]] dd::CVec getInternalVector1() const { - return dd->getVector(taskManager1.getInternalState()); + return taskManager1.getInternalState().getVector(); } [[nodiscard]] dd::CVec getInternalVector2() const { - return dd->getVector(taskManager2.getInternalState()); + return taskManager2.getInternalState().getVector(); } void json(nlohmann::json& j) const noexcept override { diff --git a/include/checker/dd/applicationscheme/LookaheadApplicationScheme.hpp b/include/checker/dd/applicationscheme/LookaheadApplicationScheme.hpp index 0a9215fe..7bcf3352 100644 --- a/include/checker/dd/applicationscheme/LookaheadApplicationScheme.hpp +++ b/include/checker/dd/applicationscheme/LookaheadApplicationScheme.hpp @@ -45,11 +45,11 @@ class LookaheadApplicationScheme final // compute both possible applications and measure the resulting size auto saved = *internalState; const auto dd1 = package->multiply(op1, saved); - const auto size1 = package->size(dd1); + const auto size1 = dd1.size(); const auto dd2 = package->multiply(saved, op2); // greedily chose the smaller resulting decision diagram - if (const auto size2 = package->size(dd2); size1 <= size2) { + if (const auto size2 = dd2.size(); size1 <= size2) { assert(!this->taskManager1.finished()); *internalState = dd1; package->decRef(op1); diff --git a/include/checker/dd/applicationscheme/ProportionalApplicationScheme.hpp b/include/checker/dd/applicationscheme/ProportionalApplicationScheme.hpp index 848f2ed8..31d6cc59 100644 --- a/include/checker/dd/applicationscheme/ProportionalApplicationScheme.hpp +++ b/include/checker/dd/applicationscheme/ProportionalApplicationScheme.hpp @@ -25,10 +25,13 @@ class ProportionalApplicationScheme final [[nodiscard]] std::size_t computeGateRatio() const noexcept { const std::size_t size1 = this->taskManager1.getCircuit()->size(); const std::size_t size2 = this->taskManager2.getCircuit()->size(); + if (size1 == 0U) { + return size2; + } return std::max((size2 + (size1 / 2U)) / size1, static_cast<std::size_t>(1U)); } - const std::size_t gateRatio; + std::size_t gateRatio; }; } // namespace ec diff --git a/mqt/qcec/CMakeLists.txt b/mqt/qcec/CMakeLists.txt deleted file mode 100644 index 6249847b..00000000 --- a/mqt/qcec/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -pybind11_add_module(py${PROJECT_NAME} bindings.cpp) -target_link_libraries(py${PROJECT_NAME} PRIVATE ${PROJECT_NAME} MQT::CorePython) -target_compile_definitions(py${PROJECT_NAME} - PRIVATE VERSION_INFO=${QCEC_VERSION_INFO}) diff --git a/noxfile.py b/noxfile.py index b120b5e4..d5d66801 100644 --- a/noxfile.py +++ b/noxfile.py @@ -2,94 +2,117 @@ from __future__ import annotations +import argparse import os +import sys from typing import TYPE_CHECKING import nox if TYPE_CHECKING: - from nox.sessions import Session + from collections.abc import Sequence -nox.options.sessions = ["lint", "tests"] +nox.options.sessions = ["lint", "pylint", "tests"] -PYTHON_ALL_VERSIONS = ["3.8", "3.9", "3.10", "3.11"] +PYTHON_ALL_VERSIONS = ["3.8", "3.9", "3.10", "3.11", "3.12"] if os.environ.get("CI", None): nox.options.error_on_missing_interpreters = True -@nox.session(python=PYTHON_ALL_VERSIONS) -def tests(session: Session) -> None: - """Run the test suite. +@nox.session(reuse_venv=True) +def lint(session: nox.Session) -> None: + """Lint the Python part of the codebase using pre-commit. - Simply execute `nox -rs tests` to run all tests. - Run as `nox -rs tests -- skip-install` to skip installing the package and its dependencies. + Simply execute `nox -rs lint` to run all configured hooks. """ - run_install = True - if session.posargs and "skip-install" in session.posargs: - run_install = False - session.posargs.remove("skip-install") - if run_install: - session.install("-e", ".[test]") - session.run("pip", "show", "qiskit-terra") - session.run("pytest", *session.posargs) - - -@nox.session(python=PYTHON_ALL_VERSIONS) -def coverage(session: Session) -> None: - """Run the test suite and generate a coverage report. - - Simply execute `nox -rs coverage -- --cov-report=html` to generate a HTML report. - Run as `nox -rs coverage -- skip-install` to skip installing the package and its dependencies. + session.install("pre-commit") + session.run("pre-commit", "run", "--all-files", *session.posargs) + + +@nox.session(reuse_venv=True) +def pylint(session: nox.Session) -> None: + """Run PyLint. + + Simply execute `nox -rs pylint` to run PyLint. """ - run_install = True - if session.posargs and "skip-install" in session.posargs: - run_install = False - session.posargs.remove("skip-install") - if run_install: - session.install("-e", ".[coverage]") - session.run("pip", "show", "qiskit-terra") - session.run("pytest", "--cov", *session.posargs) + session.install("scikit-build-core[pyproject]", "setuptools_scm", "pybind11") + session.install("--no-build-isolation", "-ve.", "pylint") + session.run("pylint", "mqt.qcec", *session.posargs) -@nox.session() -def min_qiskit_version(session: Session) -> None: - """Installs the minimum supported version of Qiskit, runs the test suite and collects the coverage.""" - session.install("qiskit-terra~=0.20.0") - session.install("-e", ".[coverage]") - session.run("pip", "show", "qiskit-terra") - session.run("pytest", "--cov", *session.posargs) +def _run_tests( + session: nox.Session, + *, + install_args: Sequence[str] = (), + run_args: Sequence[str] = (), + extras: Sequence[str] = (), +) -> None: + posargs = list(session.posargs) + env = {"PIP_DISABLE_PIP_VERSION_CHECK": "1"} + if os.environ.get("CI", None) and sys.platform == "win32": + env["SKBUILD_CMAKE_ARGS"] = "-T ClangCL" -@nox.session -def lint(session: Session) -> None: - """Lint the Python part of the codebase using pre-commit. + _extras = ["test", *extras] + if "--cov" in posargs: + _extras.append("coverage") + posargs.append("--cov-config=pyproject.toml") - Simply execute `nox -rs lint` to run all configured hooks. - """ - session.install("pre-commit") - session.run("pre-commit", "run", "--all-files", *session.posargs) + session.install("scikit-build-core[pyproject]", "setuptools_scm", "pybind11", *install_args, env=env) + install_arg = f"-ve.[{','.join(_extras)}]" + session.install("--no-build-isolation", install_arg, *install_args, env=env) + session.run("pytest", *run_args, *posargs, env=env) -@nox.session -def docs(session: Session) -> None: - """Build the documentation. +@nox.session(reuse_venv=True, python=PYTHON_ALL_VERSIONS) +def tests(session: nox.Session) -> None: + """Run the test suite.""" + _run_tests(session) - Simply execute `nox -rs docs -- serve` to locally build and serve the docs. - Run as `nox -rs docs -- skip-install` to skip installing the package and its dependencies. - """ - run_install = True - if session.posargs and "skip-install" in session.posargs: - run_install = False - session.posargs.remove("skip-install") - if run_install: - session.install("-e", ".[docs]") + +@nox.session() +def minimums(session: nox.Session) -> None: + """Test the minimum versions of dependencies.""" + _run_tests( + session, + install_args=["--constraint=test/python/constraints.txt"], + run_args=["-Wdefault"], + ) + session.run("pip", "list") + + +@nox.session(reuse_venv=True) +def docs(session: nox.Session) -> None: + """Build the docs. Pass "--serve" to serve. Pass "-b linkcheck" to check links.""" + parser = argparse.ArgumentParser() + parser.add_argument("--serve", action="store_true", help="Serve after building") + parser.add_argument("-b", dest="builder", default="html", help="Build target (default: html)") + args, posargs = parser.parse_known_args(session.posargs) + + if args.builder != "html" and args.serve: + session.error("Must not specify non-HTML builder with --serve") + + build_requirements = ["scikit-build-core[pyproject]", "setuptools_scm", "pybind11"] + extra_installs = ["sphinx-autobuild"] if args.serve else [] + session.install(*build_requirements, *extra_installs) + session.install("--no-build-isolation", "-ve.[docs]") session.chdir("docs") - session.run("sphinx-build", "-M", "html", "source", "_build") - - if session.posargs: - if "serve" in session.posargs: - print("Launching docs at http://localhost:8000/ - use Ctrl-C to quit") - session.run("python", "-m", "http.server", "8000", "-d", "_build/html") - else: - print("Unsupported argument to docs") + + if args.builder == "linkcheck": + session.run("sphinx-build", "-b", "linkcheck", "source", "_build/linkcheck", *posargs) + return + + shared_args = ( + "-n", # nitpicky mode + "-T", # full tracebacks + f"-b={args.builder}", + "source", + f"_build/{args.builder}", + *posargs, + ) + + if args.serve: + session.run("sphinx-autobuild", *shared_args) + else: + session.run("sphinx-build", "--keep-going", *shared_args) diff --git a/pyproject.toml b/pyproject.toml index 1627626c..ffeef1d4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,38 +1,37 @@ [build-system] -requires = [ - "setuptools>=61", - "setuptools_scm[toml]>=7", - "ninja>=1.10; sys_platform != 'win32'", - "cmake>=3.19", -] -build-backend = "setuptools.build_meta" +requires = ["scikit-build-core>=0.5.0", "setuptools-scm>=7", "pybind11>=2.11"] +build-backend = "scikit_build_core.build" [project] name = "mqt.qcec" description = "A tool for Quantum Circuit Equivalence Checking" readme = "README.md" authors = [ - { name = "Lukas Burgholzer", email = "lukas.burgholzer@jku.at"}, + { name = "Lukas Burgholzer", email = "lukas.burgholzer@tum.de"}, { name = "Tom Peham", email = "tom.peham@tum.de" } ] -keywords = ["MQT", "quantum computing", "design automation", "equivalence checking", "verification"] +keywords = ["MQT", "quantum-computing", "design-automation", "equivalence-checking", "verification"] license = { file = "LICENSE.md" } -classifiers=[ - "Development Status :: 5 - Production/Stable", +classifiers = [ + "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", + "Intended Audience :: Science/Research", + "Natural Language :: English", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", + "License :: OSI Approved :: MIT License", + "Programming Language :: C++", + "Programming Language :: Python", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", - "Programming Language :: C++", - "License :: OSI Approved :: MIT License", - "Operating System :: Microsoft :: Windows", - "Operating System :: MacOS", - "Operating System :: POSIX :: Linux", - "Intended Audience :: Science/Research", - "Natural Language :: English", - "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", + "Programming Language :: Python :: 3.12", + "Development Status :: 5 - Production/Stable", + "Typing :: Typed", ] requires-python = ">=3.8" dependencies = [ @@ -43,16 +42,16 @@ dependencies = [ dynamic = ["version"] [project.optional-dependencies] -test = ["pytest>=7.2"] -coverage = ["mqt.qcec[test]", "pytest-cov[toml]"] +test = ["pytest>=7.0"] +coverage = ["mqt.qcec[test]", "pytest-cov"] docs = [ - "sphinx>=5", - "furo", + "furo>=2023.08.17", + "sphinx~=7.0", + "setuptools-scm>=7", "sphinxcontrib-bibtex>=2.4.2", "sphinx-copybutton", "sphinx-hoverxref", "pybtex>=0.24", - "importlib_metadata>=4.4; python_version < '3.10'", "ipython", "ipykernel", "nbsphinx", @@ -63,38 +62,74 @@ docs = [ dev = ["mqt.qcec[coverage, docs]"] [project.urls] -Homepage = "https://github.com/cda-tum/qcec" -Documentation = "https://qcec.readthedocs.io" -"Bug Tracker" = "https://github.com/cda-tum/qcec/issues" -Discussions = "https://github.com/cda-tum/qcec/discussions" -Research = "https://www.cda.cit.tum.de/research/quantum_verification/" +Homepage = "https://github.com/cda-tum/mqt-qcec" +Documentation = "https://mqt.readthedocs.io/projects/qcec" +Issues = "https://github.com/cda-tum/mqt-qcec/issues" +Discussions = "https://github.com/cda-tum/mqt-qcec/discussions" + + +[tool.scikit-build] +# Protect the configuration against future changes in scikit-build-core +minimum-version = "0.5.0" + +# Set the target to build +cmake.targets = ["pyqcec"] + +# Set required CMake and Ninja versions +cmake.minimum-version = "3.19" +ninja.minimum-version = "1.10" + +# Setuptools-style build caching in a local directory +build-dir = "build/{wheel_tag}" + +# Build stable ABI wheels for CPython 3.12+ +wheel.py-api = "cp312" + +# Explicitly set the package directory +wheel.packages = ["src/mqt"] + +metadata.version.provider = "scikit_build_core.metadata.setuptools_scm" +sdist.include = ["src/mqt/qcec/_version.py"] +sdist.exclude = [ + "**/.github", + "**/doc", + "**/docs", + "**/meta", + "**/plots", + "**/test", + "**/tests", + "extern/mqt-core/extern/json/include", + "extern/mqt-core/extern/googletest", + "extern/mqt-core/extern/boost/config/checks", + "extern/mqt-core/extern/boost/config/tools", + "extern/mqt-core/extern/boost/multiprecision/config", + "extern/mqt-core/extern/boost/multiprecision/example", + "extern/mqt-core/extern/boost/multiprecision/performance", + "extern/mqt-core/extern/boost/multiprecision/tools" +] -[tool.setuptools.packages.find] -include = ["mqt.*"] +[tool.scikit-build.cmake.define] +BUILD_MQT_QCEC_TESTS = "OFF" +BUILD_MQT_QCEC_BINDINGS = "ON" +ENABLE_IPO = "ON" -[tool.setuptools_scm] -[tool.cibuildwheel] -build = "cp3*" -skip = "*-musllinux_*" -archs = "auto64" -test-extras = ["test"] -test-command = "python -c \"from mqt import qcec\"" -environment = { DEPLOY = "ON" } -build-frontend = "build" -build-verbosity = 3 +[tool.check-sdist] +sdist-only = ["src/mqt/qcec/_version.py"] +git-only = [ + "docs/*", + "extern/*", + "test/*", + ".idea/*", +] -[tool.cibuildwheel.linux] -[tool.cibuildwheel.macos] -environment = { MACOSX_DEPLOYMENT_TARGET = "10.15", DEPLOY = "ON" } +[tool.setuptools_scm] +write_to = "src/mqt/qcec/_version.py" -[tool.cibuildwheel.windows] -before-build = "pip install delvewheel" -repair-wheel-command = "delvewheel repair -v -w {dest_dir} {wheel}" [tool.pytest.ini_options] -minversion = "7.2" +minversion = "7.0" testpaths = ["test/python"] addopts = ["-ra", "--strict-markers", "--strict-config", "--showlocals"] log_cli_level = "INFO" @@ -103,34 +138,24 @@ filterwarnings = [ "error", 'ignore:.*`product` is deprecated.*:DeprecationWarning:qiskit:', 'ignore:.*qiskit.__qiskit_version__.*:DeprecationWarning:qiskit:', + 'ignore:.*encountered in det.*:RuntimeWarning:numpy.linalg:', ] -[tool.coverage.run] -source = ["mqt.qcec"] -omit = ["mqt/qcec/types.py"] - -[tool.coverage.report] -show_missing = true -skip_empty = true -precision = 1 - -[tool.check-manifest] -ignore = [ - "docs/**", - ".*", - ".*/**", - "noxfile.py" +[tool.coverage] +run.source = ["mqt.qcec"] +run.omit = ["src/mqt/qcec/types.py"] +report.exclude_also = [ + '\.\.\.', + 'if TYPE_CHECKING:', ] [tool.black] line-length = 120 -[tool.isort] -profile = "black" -src_paths = ["mqt/qcec", "test/python"] [tool.mypy] -files = ["mqt/qcec", "test/python", "setup.py"] +files = ["src/mqt", "test/python"] +mypy_path = ["$MYPY_CONFIG_FILE_DIR/src"] python_version = "3.8" strict = true show_error_codes = true @@ -157,6 +182,7 @@ select = [ "EM", # flake8-errmsg "EXE", # flake8-executable "FA", # flake8-future-annotations + "FLY", # flynt "I", # isort "ICN", # flake8-import-conventions "ISC", # flake8-implicit-str-concat @@ -188,6 +214,7 @@ extend-ignore = [ "E501", # Line too long (Black is enough) "PLR", # Design related pylint codes ] +src = ["src"] flake8-unused-arguments.ignore-variadic-names = true isort.required-imports = ["from __future__ import annotations"] line-length = 120 @@ -202,3 +229,23 @@ line-length = 120 [tool.ruff.pydocstyle] convention = "google" + + +[tool.cibuildwheel] +build = "cp3*" +skip = "*-musllinux_*" +archs = "auto64" +test-command = "python -c \"from mqt import qcec\"" +test-skip = "cp312-*" # Qiskit Terra does not support Python 3.12 yet +build-frontend = "build" + +[tool.cibuildwheel.linux] +environment = { DEPLOY="ON" } + +[tool.cibuildwheel.macos] +environment = { MACOSX_DEPLOYMENT_TARGET = "10.15" } + +[tool.cibuildwheel.windows] +before-build = "pip install delvewheel" +repair-wheel-command = "delvewheel repair -v -w {dest_dir} {wheel}" +environment = { CMAKE_ARGS = "-T ClangCL" } diff --git a/setup.py b/setup.py deleted file mode 100644 index f27ba5a7..00000000 --- a/setup.py +++ /dev/null @@ -1,110 +0,0 @@ -"""Setup script for the MQT QCEC package.""" -from __future__ import annotations - -import os -import re -import subprocess -import sys -from pathlib import Path - -from setuptools import Extension, setup -from setuptools.command.build_ext import build_ext - - -class CMakeExtension(Extension): - """Class that wraps a CMake extension.""" - - def __init__(self, name: str, sourcedir: str = "") -> None: - """Initialize the CMake extension. - - Args: - ---- - name: The name of the extension. - sourcedir: The path to the source directory. - """ - Extension.__init__(self, name, sources=[]) - self.sourcedir = str(Path(sourcedir).resolve()) - - -class CMakeBuild(build_ext): - """Class that builds a CMake extension.""" - - def build_extension(self, ext: CMakeExtension) -> None: - """Build the CMake extension. - - Args: - ---- - ext: The CMake extension to build. - """ - from setuptools_scm import get_version # type: ignore[import] - - version = get_version(relative_to=__file__) - - extdir = Path(self.get_ext_fullpath(ext.name)).parent.resolve() - - cmake_generator = os.environ.get("CMAKE_GENERATOR", "") - cfg = "Debug" if self.debug else "Release" - - cmake_args = [ - f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={extdir}", - f"-DPYTHON_EXECUTABLE={sys.executable}", - f"-DQCEC_VERSION_INFO={version}", - f"-DCMAKE_BUILD_TYPE={cfg}", - "-DBUILD_MQT_CORE_TESTS=OFF", - "-DBINDINGS=ON", - ] - build_args = [] - - if self.compiler.compiler_type != "msvc": - if not cmake_generator: - cmake_args += ["-GNinja"] - else: - # Single config generators are handled "normally" - single_config = any(x in cmake_generator for x in ("NMake", "Ninja")) - # CMake allows an arch-in-generator style for backward compatibility - contains_arch = any(x in cmake_generator for x in ("ARM", "Win64")) - # Convert distutils Windows platform specifiers to CMake -A arguments - plat_to_cmake = { - "win32": "Win32", - "win-amd64": "x64", - "win-arm32": "ARM", - "win-arm64": "ARM64", - } - # Specify the arch if using MSVC generator, but only if it doesn't - # contain a backward-compatibility arch spec already in the - # generator name. - if not single_config and not contains_arch: - cmake_args += ["-A", plat_to_cmake[self.plat_name]] - # Multi-config generators have a different way to specify configs - if not single_config: - cmake_args += [f"-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{cfg.upper()}={extdir}"] - build_args += ["--config", cfg] - - # cross-compile support for macOS - respect ARCHFLAGS if set - if sys.platform.startswith("darwin"): - archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) - if archs: - cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))] - - # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level across all generators. - if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ and hasattr(self, "parallel") and self.parallel: - build_args += [f"-j{self.parallel}"] - - if sys.platform == "win32": - cmake_args += ["-T", "ClangCl"] - - build_dir = Path(self.build_temp) - build_dir.mkdir(parents=True, exist_ok=True) - Path(build_dir / "CMakeCache.txt").unlink(missing_ok=True) - - subprocess.check_call(["cmake", ext.sourcedir, *cmake_args], cwd=self.build_temp) - subprocess.check_call( - ["cmake", "--build", ".", "--target", ext.name.split(".")[-1], *build_args], - cwd=self.build_temp, - ) - - -setup( - ext_modules=[CMakeExtension("mqt.qcec.pyqcec")], - cmdclass={"build_ext": CMakeBuild}, -) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index da065308..6a6ba080 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,7 @@ -# add MQT::qfr library +# add MQT::Core target +set(BUILD_MQT_CORE_TESTS + OFF + CACHE BOOL "Build MQT Core tests") add_subdirectory("${PROJECT_SOURCE_DIR}/extern/mqt-core" "extern/mqt-core" EXCLUDE_FROM_ALL) @@ -23,8 +26,12 @@ add_library( target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include ${PROJECT_BINARY_DIR}/include) -# link to the MQT::qfr libraries +# link to the MQT::Core libraries target_link_libraries(${PROJECT_NAME} PUBLIC MQT::CoreDD MQT::CoreZX) # add MQT alias add_library(MQT::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) + +if(BUILD_MQT_QCEC_BINDINGS) + add_subdirectory(python) +endif() diff --git a/src/EquivalenceCheckingManager.cpp b/src/EquivalenceCheckingManager.cpp index ef25e53e..55ac7fcb 100644 --- a/src/EquivalenceCheckingManager.cpp +++ b/src/EquivalenceCheckingManager.cpp @@ -157,6 +157,12 @@ void EquivalenceCheckingManager::run() { return; } + if (qc1.empty() && qc2.empty()) { + results.equivalence = EquivalenceCriterion::Equivalent; + done = true; + return; + } + if (qc1.isVariableFree() && qc2.isVariableFree()) { if (!configuration.execution.parallel || configuration.execution.nthreads <= 1 || diff --git a/src/checker/dd/DDEquivalenceChecker.cpp b/src/checker/dd/DDEquivalenceChecker.cpp index 7d3d1892..4f26ffe0 100644 --- a/src/checker/dd/DDEquivalenceChecker.cpp +++ b/src/checker/dd/DDEquivalenceChecker.cpp @@ -130,11 +130,11 @@ EquivalenceCriterion DDEquivalenceChecker<DDType, Config>::run() { // determine maximum number of nodes used if constexpr (std::is_same_v<DDType, qc::MatrixDD>) { - maxActiveNodes = dd->mUniqueTable.getStats().peakActiveEntryCount; + maxActiveNodes = dd->mUniqueTable.getPeakNumActiveEntries(); } if constexpr (std::is_same_v<DDType, qc::VectorDD>) { - maxActiveNodes = dd->vUniqueTable.getStats().peakActiveEntryCount; + maxActiveNodes = dd->vUniqueTable.getPeakNumActiveEntries(); } const auto end = std::chrono::steady_clock::now(); diff --git a/src/checker/dd/applicationscheme/GateCostApplicationScheme.cpp b/src/checker/dd/applicationscheme/GateCostApplicationScheme.cpp index 4f0a7a79..07205a91 100644 --- a/src/checker/dd/applicationscheme/GateCostApplicationScheme.cpp +++ b/src/checker/dd/applicationscheme/GateCostApplicationScheme.cpp @@ -35,8 +35,6 @@ legacyCostFunction(const GateCostLookupTableKeyType& key) noexcept { case qc::Compound: case qc::Measure: case qc::Reset: - case qc::Snapshot: - case qc::ShowProbabilities: case qc::Barrier: case qc::ClassicControlled: return 1U; diff --git a/src/checker/zx/ZXChecker.cpp b/src/checker/zx/ZXChecker.cpp index 4fb0d8b6..b0b2f6c3 100644 --- a/src/checker/zx/ZXChecker.cpp +++ b/src/checker/zx/ZXChecker.cpp @@ -66,9 +66,12 @@ EquivalenceCriterion ZXEquivalenceChecker::run() { break; } const auto& out = edge.to; - - if (p1.at(static_cast<qc::Qubit>(miter.getVData(in).value().qubit)) != - p2.at(static_cast<qc::Qubit>(miter.getVData(out).value().qubit))) { + const auto& q1 = miter.getVData(in); + const auto& q2 = miter.getVData(out); + assert(q1.has_value()); + assert(q2.has_value()); + if (p1.at(static_cast<qc::Qubit>(q1->qubit)) != + p2.at(static_cast<qc::Qubit>(q2->qubit))) { equivalent = false; break; } @@ -121,8 +124,8 @@ qc::Permutation concat(const qc::Permutation& p1, qc::Permutation complete(const qc::Permutation& p, const std::size_t n) { qc::Permutation pComp = p; - std::vector<bool> mappedTo(n, false); - std::vector<bool> mappedFrom(n, false); + std::unordered_map<std::size_t, bool> mappedTo; + std::unordered_map<std::size_t, bool> mappedFrom; for (const auto [k, v] : p) { mappedFrom[k] = true; mappedTo[v] = true; @@ -152,13 +155,15 @@ qc::Permutation invertPermutations(const qc::QuantumComputation& qc) { } std::size_t ZXEquivalenceChecker::fullReduceApproximate() { - auto nSimplifications = fullReduce(); - std::size_t newSimps{}; - do { + auto nSimplifications = fullReduce(); + while (!isDone()) { miter.approximateCliffords(tolerance); - newSimps = fullReduce(); + const auto newSimps = fullReduce(); + if (newSimps == 0U) { + break; + } nSimplifications += newSimps; - } while (!isDone() && (newSimps > 0U)); + } return nSimplifications; } diff --git a/mqt/qcec/__init__.py b/src/mqt/qcec/__init__.py similarity index 69% rename from mqt/qcec/__init__.py rename to src/mqt/qcec/__init__.py index dca67581..1db4ca03 100644 --- a/mqt/qcec/__init__.py +++ b/src/mqt/qcec/__init__.py @@ -5,16 +5,17 @@ """ from __future__ import annotations -from mqt.qcec.compilation_flow_profiles import AncillaMode, generate_profile -from mqt.qcec.pyqcec import ( +from ._version import version as __version__ +from .compilation_flow_profiles import AncillaMode, generate_profile +from .pyqcec import ( ApplicationScheme, Configuration, EquivalenceCheckingManager, EquivalenceCriterion, StateType, ) -from mqt.qcec.verify import verify -from mqt.qcec.verify_compilation_flow import verify_compilation +from .verify import verify +from .verify_compilation_flow import verify_compilation __all__ = [ "AncillaMode", @@ -26,4 +27,5 @@ "StateType", "verify", "verify_compilation", + "__version__", ] diff --git a/src/mqt/qcec/_version.pyi b/src/mqt/qcec/_version.pyi new file mode 100644 index 00000000..9312dffe --- /dev/null +++ b/src/mqt/qcec/_version.pyi @@ -0,0 +1,4 @@ +__version__: str +version: str +__version_tuple__: tuple[int, int, int, str, str] | tuple[int, int, int] +version_tuple: tuple[int, int, int, str, str] | tuple[int, int, int] diff --git a/mqt/qcec/compilation_flow_profiles.py b/src/mqt/qcec/compilation_flow_profiles.py similarity index 100% rename from mqt/qcec/compilation_flow_profiles.py rename to src/mqt/qcec/compilation_flow_profiles.py diff --git a/mqt/qcec/configuration.py b/src/mqt/qcec/configuration.py similarity index 93% rename from mqt/qcec/configuration.py rename to src/mqt/qcec/configuration.py index c25c0061..3349ea8f 100644 --- a/mqt/qcec/configuration.py +++ b/src/mqt/qcec/configuration.py @@ -4,9 +4,9 @@ from typing import TYPE_CHECKING, TypedDict -if TYPE_CHECKING: # pragma: no cover - from mqt.qcec import ApplicationScheme, Configuration, StateType - from mqt.qcec.types import ApplicationSchemeName, StateTypeName +if TYPE_CHECKING: + from . import ApplicationScheme, Configuration, StateType + from .types import ApplicationSchemeName, StateTypeName class ConfigurationOptions(TypedDict, total=False): diff --git a/mqt/qcec/parameterized.py b/src/mqt/qcec/parameterized.py similarity index 98% rename from mqt/qcec/parameterized.py rename to src/mqt/qcec/parameterized.py index 2b10884b..ada47b23 100644 --- a/mqt/qcec/parameterized.py +++ b/src/mqt/qcec/parameterized.py @@ -6,14 +6,14 @@ from itertools import chain from typing import TYPE_CHECKING -if TYPE_CHECKING: # pragma: no cover +if TYPE_CHECKING: from numpy.typing import NDArray from qiskit import QuantumCircuit import numpy as np from qiskit.circuit import Parameter, ParameterExpression -from mqt.qcec import Configuration, EquivalenceCheckingManager, EquivalenceCriterion +from . import Configuration, EquivalenceCheckingManager, EquivalenceCriterion def __is_parameterized(qc: QuantumCircuit | str) -> bool: diff --git a/mqt/qcec/profiles/qiskit_O0_noancilla.profile b/src/mqt/qcec/profiles/qiskit_O0_noancilla.profile similarity index 100% rename from mqt/qcec/profiles/qiskit_O0_noancilla.profile rename to src/mqt/qcec/profiles/qiskit_O0_noancilla.profile diff --git a/mqt/qcec/profiles/qiskit_O0_recursion.profile b/src/mqt/qcec/profiles/qiskit_O0_recursion.profile similarity index 100% rename from mqt/qcec/profiles/qiskit_O0_recursion.profile rename to src/mqt/qcec/profiles/qiskit_O0_recursion.profile diff --git a/mqt/qcec/profiles/qiskit_O0_v-chain.profile b/src/mqt/qcec/profiles/qiskit_O0_v-chain.profile similarity index 100% rename from mqt/qcec/profiles/qiskit_O0_v-chain.profile rename to src/mqt/qcec/profiles/qiskit_O0_v-chain.profile diff --git a/mqt/qcec/profiles/qiskit_O1_noancilla.profile b/src/mqt/qcec/profiles/qiskit_O1_noancilla.profile similarity index 100% rename from mqt/qcec/profiles/qiskit_O1_noancilla.profile rename to src/mqt/qcec/profiles/qiskit_O1_noancilla.profile diff --git a/mqt/qcec/profiles/qiskit_O1_recursion.profile b/src/mqt/qcec/profiles/qiskit_O1_recursion.profile similarity index 100% rename from mqt/qcec/profiles/qiskit_O1_recursion.profile rename to src/mqt/qcec/profiles/qiskit_O1_recursion.profile diff --git a/mqt/qcec/profiles/qiskit_O1_v-chain.profile b/src/mqt/qcec/profiles/qiskit_O1_v-chain.profile similarity index 100% rename from mqt/qcec/profiles/qiskit_O1_v-chain.profile rename to src/mqt/qcec/profiles/qiskit_O1_v-chain.profile diff --git a/mqt/qcec/profiles/qiskit_O2_noancilla.profile b/src/mqt/qcec/profiles/qiskit_O2_noancilla.profile similarity index 100% rename from mqt/qcec/profiles/qiskit_O2_noancilla.profile rename to src/mqt/qcec/profiles/qiskit_O2_noancilla.profile diff --git a/mqt/qcec/profiles/qiskit_O2_recursion.profile b/src/mqt/qcec/profiles/qiskit_O2_recursion.profile similarity index 100% rename from mqt/qcec/profiles/qiskit_O2_recursion.profile rename to src/mqt/qcec/profiles/qiskit_O2_recursion.profile diff --git a/mqt/qcec/profiles/qiskit_O2_v-chain.profile b/src/mqt/qcec/profiles/qiskit_O2_v-chain.profile similarity index 100% rename from mqt/qcec/profiles/qiskit_O2_v-chain.profile rename to src/mqt/qcec/profiles/qiskit_O2_v-chain.profile diff --git a/mqt/qcec/profiles/qiskit_O3_noancilla.profile b/src/mqt/qcec/profiles/qiskit_O3_noancilla.profile similarity index 100% rename from mqt/qcec/profiles/qiskit_O3_noancilla.profile rename to src/mqt/qcec/profiles/qiskit_O3_noancilla.profile diff --git a/mqt/qcec/profiles/qiskit_O3_recursion.profile b/src/mqt/qcec/profiles/qiskit_O3_recursion.profile similarity index 100% rename from mqt/qcec/profiles/qiskit_O3_recursion.profile rename to src/mqt/qcec/profiles/qiskit_O3_recursion.profile diff --git a/mqt/qcec/profiles/qiskit_O3_v-chain.profile b/src/mqt/qcec/profiles/qiskit_O3_v-chain.profile similarity index 100% rename from mqt/qcec/profiles/qiskit_O3_v-chain.profile rename to src/mqt/qcec/profiles/qiskit_O3_v-chain.profile diff --git a/mqt/qcec/py.typed b/src/mqt/qcec/py.typed similarity index 100% rename from mqt/qcec/py.typed rename to src/mqt/qcec/py.typed diff --git a/mqt/qcec/pyqcec.pyi b/src/mqt/qcec/pyqcec/__init__.pyi similarity index 100% rename from mqt/qcec/pyqcec.pyi rename to src/mqt/qcec/pyqcec/__init__.pyi diff --git a/mqt/qcec/types.py b/src/mqt/qcec/types.py similarity index 100% rename from mqt/qcec/types.py rename to src/mqt/qcec/types.py diff --git a/mqt/qcec/verify.py b/src/mqt/qcec/verify.py similarity index 86% rename from mqt/qcec/verify.py rename to src/mqt/qcec/verify.py index 3b0ffacd..85af329a 100644 --- a/mqt/qcec/verify.py +++ b/src/mqt/qcec/verify.py @@ -4,15 +4,15 @@ from typing import TYPE_CHECKING -if TYPE_CHECKING: # pragma: no cover +if TYPE_CHECKING: from qiskit import QuantumCircuit from typing_extensions import Unpack - from mqt.qcec.configuration import ConfigurationOptions + from .configuration import ConfigurationOptions -from mqt.qcec import Configuration, EquivalenceCheckingManager -from mqt.qcec.configuration import augment_config_from_kwargs -from mqt.qcec.parameterized import __is_parameterized, check_parameterized +from . import Configuration, EquivalenceCheckingManager +from .configuration import augment_config_from_kwargs +from .parameterized import __is_parameterized, check_parameterized def verify( diff --git a/mqt/qcec/verify_compilation_flow.py b/src/mqt/qcec/verify_compilation_flow.py similarity index 89% rename from mqt/qcec/verify_compilation_flow.py rename to src/mqt/qcec/verify_compilation_flow.py index 2d5032fe..b86de492 100644 --- a/mqt/qcec/verify_compilation_flow.py +++ b/src/mqt/qcec/verify_compilation_flow.py @@ -6,22 +6,22 @@ import warnings from typing import TYPE_CHECKING -if TYPE_CHECKING: # pragma: no cover +if TYPE_CHECKING: from typing_extensions import Unpack - from mqt.qcec.configuration import ConfigurationOptions + from .configuration import ConfigurationOptions -if sys.version_info < (3, 10, 0): +if TYPE_CHECKING or sys.version_info < (3, 10, 0): import importlib_resources as resources else: - from importlib import resources # type: ignore[no-redef] + from importlib import resources from qiskit import QuantumCircuit -from mqt.qcec import ApplicationScheme, Configuration, EquivalenceCheckingManager -from mqt.qcec.compilation_flow_profiles import AncillaMode, generate_profile_name -from mqt.qcec.configuration import augment_config_from_kwargs -from mqt.qcec.verify import verify +from . import ApplicationScheme, Configuration, EquivalenceCheckingManager +from .compilation_flow_profiles import AncillaMode, generate_profile_name +from .configuration import augment_config_from_kwargs +from .verify import verify def __check_if_circuit_contains_measurements(circuit: QuantumCircuit) -> None: diff --git a/src/python/CMakeLists.txt b/src/python/CMakeLists.txt new file mode 100644 index 00000000..a20e1379 --- /dev/null +++ b/src/python/CMakeLists.txt @@ -0,0 +1,43 @@ +if(NOT SKBUILD) + message( + NOTICE + "\ + This CMake file is meant to be executed using 'scikit-build'. Running + it directly will almost certainly not produce the desired result. If + you are a user trying to install this package, please use the command + below, which will install all necessary build dependencies, compile + the package in an isolated environment, and then install it. + ===================================================================== + $ pip install . + ===================================================================== + If you are a software developer, and this is your own package, then + it is usually much more efficient to install the build dependencies + in your environment once and use the following command that avoids + a costly creation of a new virtual environment at every compilation: + ===================================================================== + $ pip install 'scikit-build-core[pyproject]' setuptools_scm pybind11 + $ pip install --no-build-isolation -ve . + ===================================================================== + You may optionally add -Ceditable.rebuild=true to auto-rebuild when + the package is imported. Otherwise, you need to re-run the above + after editing C++ files.") +endif() + +if(NOT SKBUILD) + # Manually detect the installed pybind11 package and import it into CMake. + execute_process( + COMMAND "${Python_EXECUTABLE}" -m pybind11 --cmakedir + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE pybind11_DIR) + list(APPEND CMAKE_PREFIX_PATH "${pybind11_DIR}") +endif() + +# Import pybind11 through CMake's find_package mechanism +find_package(pybind11 CONFIG REQUIRED) + +# We are now ready to compile the actual extension module +pybind11_add_module(py${PROJECT_NAME} bindings.cpp) +target_link_libraries(py${PROJECT_NAME} PRIVATE ${PROJECT_NAME} MQT::CorePython) + +# Install directive for scikit-build-core +install(TARGETS py${PROJECT_NAME} LIBRARY DESTINATION mqt/qcec) diff --git a/mqt/qcec/bindings.cpp b/src/python/bindings.cpp similarity index 99% rename from mqt/qcec/bindings.cpp rename to src/python/bindings.cpp index cc509720..c1909bd2 100644 --- a/mqt/qcec/bindings.cpp +++ b/src/python/bindings.cpp @@ -4,7 +4,6 @@ // #include "EquivalenceCheckingManager.hpp" -#include "pybind11/chrono.h" #include "pybind11/pybind11.h" #include "pybind11/stl.h" #include "pybind11_json/pybind11_json.hpp" @@ -14,9 +13,6 @@ #include <exception> #include <memory> -#define STRINGIFY(x) #x -#define MACRO_STRINGIFY(x) STRINGIFY(x) - namespace py = pybind11; using namespace pybind11::literals; @@ -675,11 +671,5 @@ PYBIND11_MODULE(pyqcec, m) { "to simpler equivalence checking instances as the random " "instantiation. This option " "changes how many of those additional checks are performed."); - -#ifdef VERSION_INFO - m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); -#else - m.attr("__version__") = "dev"; -#endif } } // namespace ec diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3270e78b..4806de52 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,9 +3,8 @@ if(NOT TARGET gtest OR NOT TARGET gmock) set(gtest_force_shared_crt # cmake-lint: disable=C0103 ON CACHE BOOL "" FORCE) - add_subdirectory( - "${PROJECT_SOURCE_DIR}/extern/qfr/extern/dd_package/extern/googletest" - "extern/qfr/extern/dd_package/extern/googletest" EXCLUDE_FROM_ALL) + add_subdirectory("${PROJECT_SOURCE_DIR}/extern/mqt-core/extern/googletest" + "extern/mqt-core/extern/googletest" EXCLUDE_FROM_ALL) endif() package_add_test( diff --git a/test/python/constraints.txt b/test/python/constraints.txt new file mode 100644 index 00000000..fbee8761 --- /dev/null +++ b/test/python/constraints.txt @@ -0,0 +1,7 @@ +scikit-build-core==0.5.0 +setuptools-scm==7.0.0 +pybind11==2.11.0 +pytest==7.0.0 +importlib_resources==5.0.0 +typing_extensions==4.2.0 +qiskit-terra==0.20.0 diff --git a/test/test_equality.cpp b/test/test_equality.cpp index 28a82d1f..6f1422ff 100644 --- a/test/test_equality.cpp +++ b/test/test_equality.cpp @@ -179,3 +179,83 @@ TEST_F(EqualityTest, ExceptionInParallelThread) { ec::EquivalenceCheckingManager ecm(qc1, qc1, config); EXPECT_THROW(ecm.run(), std::invalid_argument); } + +TEST_F(EqualityTest, BothCircuitsEmptyAlternatingChecker) { + config.execution.runAlternatingChecker = true; + ec::EquivalenceCheckingManager ecm(qc1, qc2, config); + ecm.setApplicationScheme(ec::ApplicationSchemeType::Proportional); + ecm.run(); + EXPECT_EQ(ecm.equivalence(), ec::EquivalenceCriterion::Equivalent); +} + +TEST_F(EqualityTest, BothCircuitsEmptyConstructionChecker) { + config.execution.runConstructionChecker = true; + ec::EquivalenceCheckingManager ecm(qc1, qc2, config); + ecm.setApplicationScheme(ec::ApplicationSchemeType::Proportional); + ecm.run(); + EXPECT_EQ(ecm.equivalence(), ec::EquivalenceCriterion::Equivalent); +} + +TEST_F(EqualityTest, BothCircuitsEmptySimulationChecker) { + config.execution.runSimulationChecker = true; + ec::EquivalenceCheckingManager ecm(qc1, qc2, config); + ecm.setApplicationScheme(ec::ApplicationSchemeType::Proportional); + ecm.run(); + EXPECT_EQ(ecm.equivalence(), ec::EquivalenceCriterion::Equivalent); +} + +TEST_F(EqualityTest, BothCircuitsEmptyZXChecker) { + config.execution.runZXChecker = true; + ec::EquivalenceCheckingManager ecm(qc1, qc2, config); + ecm.setApplicationScheme(ec::ApplicationSchemeType::Proportional); + ecm.run(); + EXPECT_EQ(ecm.equivalence(), ec::EquivalenceCriterion::Equivalent); +} + +TEST_F(EqualityTest, OneCircuitEmptyAlternatingChecker) { + qc2.h(0); + qc2.x(0); + qc2.h(0); + qc2.z(0); + config.execution.runAlternatingChecker = true; + ec::EquivalenceCheckingManager ecm(qc1, qc2, config); + ecm.setApplicationScheme(ec::ApplicationSchemeType::Proportional); + ecm.run(); + EXPECT_EQ(ecm.equivalence(), ec::EquivalenceCriterion::Equivalent); +} + +TEST_F(EqualityTest, OneCircuitEmptyConstructionChecker) { + qc2.h(0); + qc2.x(0); + qc2.h(0); + qc2.z(0); + config.execution.runConstructionChecker = true; + ec::EquivalenceCheckingManager ecm(qc1, qc2, config); + ecm.setApplicationScheme(ec::ApplicationSchemeType::Proportional); + ecm.run(); + EXPECT_EQ(ecm.equivalence(), ec::EquivalenceCriterion::Equivalent); +} + +TEST_F(EqualityTest, OneCircuitEmptySimulationChecker) { + qc2.h(0); + qc2.x(0); + qc2.h(0); + qc2.z(0); + config.execution.runSimulationChecker = true; + ec::EquivalenceCheckingManager ecm(qc1, qc2, config); + ecm.setApplicationScheme(ec::ApplicationSchemeType::Proportional); + ecm.run(); + EXPECT_EQ(ecm.equivalence(), ec::EquivalenceCriterion::ProbablyEquivalent); +} + +TEST_F(EqualityTest, OneCircuitEmptyZXChecker) { + qc2.h(0); + qc2.x(0); + qc2.h(0); + qc2.z(0); + config.execution.runZXChecker = true; + ec::EquivalenceCheckingManager ecm(qc1, qc2, config); + ecm.setApplicationScheme(ec::ApplicationSchemeType::Proportional); + ecm.run(); + EXPECT_EQ(ecm.equivalence(), ec::EquivalenceCriterion::Equivalent); +}