├── .clang-format ├── .clang-tidy ├── .cmake-format.yaml ├── .editorconfig ├── .git-archival.txt ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.yml │ └── feature-request.yml ├── codecov.yml ├── contributing.rst ├── pull_request_template.md ├── release-drafter.yml ├── renovate.json5 ├── support.rst └── workflows │ ├── cd.yml │ ├── ci.yml │ ├── release-drafter.yml │ └── update-mqt-core.yml ├── .gitignore ├── .license-tools-config.json ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── app ├── CMakeLists.txt └── testDDSimDebugger.cpp ├── cmake ├── ExternalDependencies.cmake └── cmake_uninstall.cmake.in ├── docs ├── .gitignore ├── AssertionRefinement.rst ├── Assertions.rst ├── Contributing.rst ├── Debugging.rst ├── DevelopmentGuide.rst ├── Diagnosis.rst ├── Doxyfile ├── Installation.rst ├── Publications.rst ├── Quickstart.rst ├── RuntimeVerification.rst ├── Support.rst ├── _static │ ├── custom.css │ ├── mqt_dark.png │ └── mqt_light.png ├── _templates │ └── page.html ├── conf.py ├── index.rst ├── library │ ├── Library.rst │ ├── dd │ │ ├── DDSimDebug.rst │ │ ├── DDSimDiagnostics.rst │ │ └── Dd.rst │ ├── interface │ │ ├── Common.rst │ │ ├── Debug.rst │ │ ├── Diagnostics.rst │ │ └── Interface.rst │ ├── parsing │ │ ├── AssertionParsing.rst │ │ ├── CodePreprocessing.rst │ │ ├── Parsing.rst │ │ ├── ParsingError.rst │ │ └── Utils.rst │ └── python │ │ ├── Debug.rst │ │ ├── Python.rst │ │ └── dap │ │ ├── Adapter.rst │ │ ├── DAP.rst │ │ └── Messages.rst ├── lit_header.bib └── refs.bib ├── include ├── backend │ ├── dd │ │ ├── DDSimDebug.hpp │ │ └── DDSimDiagnostics.hpp │ ├── debug.h │ └── diagnostics.h ├── common.h ├── common │ ├── ComplexMathematics.hpp │ ├── Span.hpp │ └── parsing │ │ ├── AssertionParsing.hpp │ │ ├── AssertionTools.hpp │ │ ├── CodePreprocessing.hpp │ │ ├── ParsingError.hpp │ │ └── Utils.hpp ├── frontend │ └── cli │ │ └── CliFrontEnd.hpp └── python │ ├── InterfaceBindings.hpp │ └── dd │ └── DDSimDebugBindings.hpp ├── noxfile.py ├── pyproject.toml ├── src ├── CMakeLists.txt ├── backend │ └── dd │ │ ├── DDSimDebug.cpp │ │ └── DDSimDiagnostics.cpp ├── common │ ├── ComplexMathematics.cpp │ └── parsing │ │ ├── AssertionParsing.cpp │ │ ├── AssertionTools.cpp │ │ ├── CodePreprocessing.cpp │ │ ├── ParsingError.cpp │ │ └── Utils.cpp ├── frontend │ └── cli │ │ └── CliFrontEnd.cpp ├── mqt │ └── debugger │ │ ├── __init__.py │ │ ├── _version.pyi │ │ ├── check │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── calibration.py │ │ ├── result_checker.py │ │ ├── run_preparation.py │ │ └── runtime_check.py │ │ ├── dap │ │ ├── __init__.py │ │ ├── adapter.py │ │ ├── dap_server.py │ │ └── messages │ │ │ ├── __init__.py │ │ │ ├── capabilities_dap_event.py │ │ │ ├── configuration_done_dap_message.py │ │ │ ├── continue_dap_message.py │ │ │ ├── dap_event.py │ │ │ ├── dap_message.py │ │ │ ├── disconnect_dap_message.py │ │ │ ├── exception_info_message.py │ │ │ ├── exited_dap_event.py │ │ │ ├── gray_out_event.py │ │ │ ├── initialize_dap_message.py │ │ │ ├── initialized_dap_event.py │ │ │ ├── launch_dap_message.py │ │ │ ├── next_dap_message.py │ │ │ ├── output_dap_event.py │ │ │ ├── pause_dap_message.py │ │ │ ├── restart_dap_message.py │ │ │ ├── restart_frame_dap_message.py │ │ │ ├── reverse_continue_dap_message.py │ │ │ ├── scopes_dap_message.py │ │ │ ├── set_breakpoints_dap_message.py │ │ │ ├── set_exception_breakpoints_dap_message.py │ │ │ ├── stack_trace_dap_message.py │ │ │ ├── step_back_dap_message.py │ │ │ ├── step_in_dap_message.py │ │ │ ├── step_out_dap_message.py │ │ │ ├── stopped_dap_event.py │ │ │ ├── terminate_dap_message.py │ │ │ ├── terminated_dap_event.py │ │ │ ├── threads_dap_message.py │ │ │ ├── utils.py │ │ │ └── variables_dap_message.py │ │ ├── py.typed │ │ └── pydebugger.pyi └── python │ ├── CMakeLists.txt │ ├── InterfaceBindings.cpp │ ├── bindings.cpp │ └── dd │ └── DDSimDebugBindings.cpp ├── test ├── CMakeLists.txt ├── circuits │ ├── classical-storage.qasm │ ├── complex-jumps.qasm │ ├── diagnose-with-jumps.qasm │ ├── failing-assertions-missing-interaction.qasm │ ├── failing-assertions-multiple-causes.qasm │ ├── failing-assertions-multiple-missing-interaction.qasm │ ├── failing-assertions-multiple-zero-controls.qasm │ ├── failing-assertions.qasm │ ├── runtime-interaction.qasm │ └── zero-controls-with-jumps.qasm ├── python │ ├── resources │ │ ├── bindings │ │ │ ├── classical.qasm │ │ │ ├── ghz-incorrect.qasm │ │ │ └── jumps.qasm │ │ ├── compilation │ │ │ ├── calibration.json │ │ │ ├── original.qasm │ │ │ ├── test-shots.qasm │ │ │ └── test_program_compiled │ │ │ │ ├── slice_1.qasm │ │ │ │ ├── slice_2.qasm │ │ │ │ └── slice_3.qasm │ │ ├── diagnosis │ │ │ ├── control-always-zero.qasm │ │ │ ├── data-dependencies-with-callers.qasm │ │ │ ├── dependencies-with-jumps.qasm │ │ │ ├── dependencies.qasm │ │ │ └── missing-interaction.qasm │ │ └── end-to-end │ │ │ ├── bell.out │ │ │ ├── bell.qasm │ │ │ ├── bv.out │ │ │ ├── bv.qasm │ │ │ ├── dj_4.out │ │ │ ├── dj_4.qasm │ │ │ ├── fail_eq.out │ │ │ ├── fail_eq.qasm │ │ │ ├── fail_ghz.out │ │ │ ├── fail_ghz.qasm │ │ │ ├── ghz_3.out │ │ │ ├── ghz_3.qasm │ │ │ ├── ghz_5.out │ │ │ ├── ghz_5.qasm │ │ │ ├── grover_3.out │ │ │ ├── grover_3.qasm │ │ │ ├── qpe.out │ │ │ └── qpe.qasm │ ├── test_compilation.py │ ├── test_diagnosis.py │ ├── test_end_to_end.py │ └── test_python_bindings.py ├── test_assertion_creation.cpp ├── test_assertion_movement.cpp ├── test_compilation_projective_measurements.cpp ├── test_compilation_statistical_slices.cpp ├── test_custom_code.cpp ├── test_data_retrieval.cpp ├── test_diagnostics.cpp ├── test_parsing.cpp ├── test_simulation.cpp ├── test_utility.cpp ├── utils │ ├── common_fixtures.hpp │ └── utils_test.hpp └── utils_test.cpp └── uv.lock /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IncludeBlocks: Regroup 3 | PointerAlignment: Left 4 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | FormatStyle: file 2 | 3 | Checks: | 4 | bugprone-*, 5 | -bugprone-easily-swappable-parameters, 6 | -bugprone-unchecked-optional-access, 7 | clang-analyzer-*, 8 | -clang-analyzer-core.NullDereference, 9 | clang-diagnostic-*, 10 | cppcoreguidelines-*, 11 | -cppcoreguidelines-non-private-member-variables-in-classes, 12 | -cppcoreguidelines-special-member-functions, 13 | -cppcoreguidelines-avoid-magic-numbers, 14 | -cppcoreguidelines-macro-usage, 15 | google-*, 16 | -google-readability-todo, 17 | -google-build-using-namespace, 18 | misc-*, 19 | -misc-no-recursion, 20 | -misc-non-private-member-variables-in-classes, 21 | modernize-*, 22 | -modernize-use-trailing-return-type, 23 | performance-*, 24 | portability-*, 25 | readability-*, 26 | -readability-identifier-length, 27 | -readability-magic-numbers, 28 | -readability-function-cognitive-complexity 29 | 30 | CheckOptions: 31 | - key: readability-identifier-naming.ClassCase 32 | value: CamelCase 33 | - key: readability-identifier-naming.ClassIgnoredRegexp 34 | value: ".*ZX.*|.*SWAP.*|.*CEX.*|.*DD.*|.*EQ.*" 35 | - key: readability-identifier-naming.ConstantParameterCase 36 | value: camelBack 37 | - key: readability-identifier-naming.EnumCase 38 | value: CamelCase 39 | - key: readability-identifier-naming.EnumConstantCase 40 | value: CamelCase 41 | - key: readability-identifier-naming.FunctionCase 42 | value: camelBack 43 | - key: readability-identifier-naming.FunctionIgnoredRegexp 44 | value: ".*ZX.*|.*SWAP.*|.*CEX.*|.*DD.*|.*EQ.*" 45 | - key: readability-identifier-naming.GlobalConstantCase 46 | value: UPPER_CASE 47 | - key: readability-identifier-naming.IgnoreMainLikeFunctions 48 | value: "true" 49 | - key: readability-identifier-naming.LocalConstantCase 50 | value: camelBack 51 | - key: readability-identifier-naming.LocalVariableCase 52 | value: camelBack 53 | - key: readability-identifier-naming.MemberCase 54 | value: camelBack 55 | - key: readability-identifier-naming.MemberIgnoredRegexp 56 | value: ".*ZX.*|.*SWAP.*|.*CEX.*|.*DD.*|.*EQ.*" 57 | - key: readability-identifier-naming.MethodCase 58 | value: camelBack 59 | - key: readability-identifier-naming.ParameterCase 60 | value: camelBack 61 | - key: readability-identifier-naming.ParameterIgnoredRegexp 62 | value: ".*ZX.*|.*SWAP.*|.*CEX.*|.*DD.*|.*EQ.*" 63 | - key: readability-identifier-naming.NamespaceCase 64 | value: lower_case 65 | - key: readability-identifier-naming.StaticConstantCase 66 | value: UPPER_CASE 67 | - key: readability-identifier-naming.StructCase 68 | value: CamelCase 69 | - key: readability-identifier-naming.VariableCase 70 | value: camelBack 71 | -------------------------------------------------------------------------------- /.cmake-format.yaml: -------------------------------------------------------------------------------- 1 | format: 2 | line_width: 100 3 | keyword_case: "upper" 4 | autosort: true 5 | 6 | markup: 7 | first_comment_is_literal: true 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.{py,pyi}] 12 | indent_size = 4 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | 17 | [{*.{cmake,cmake.in},CMakeLists.txt}] 18 | max_line_length = 100 19 | -------------------------------------------------------------------------------- /.git-archival.txt: -------------------------------------------------------------------------------- 1 | node: $Format:%H$ 2 | node-date: $Format:%cI$ 3 | describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$ 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | .git_archival.txt export-subst 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | name: 🐛 Bug report 2 | description: Something is not working correctly. 3 | title: "🐛 " 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: >- 8 | **Thank you for wanting to report a bug for this project!** 9 | 10 | ⚠ 11 | Verify first that your issue is not [already reported on GitHub](https://github.com/munich-quantum-toolkit/debugger/search?q=is%3Aissue&type=issues). 12 | 13 | If you have general questions, please consider [starting a discussion](https://github.com/munich-quantum-toolkit/debugger/discussions). 14 | - type: textarea 15 | attributes: 16 | label: Environment information 17 | description: >- 18 | Please provide information about your environment. For example, OS, C++ compiler, mqt.core version etc. 19 | placeholder: | 20 | - OS: 21 | - C++ compiler: 22 | - mqt.core version: 23 | - Additional environment information: 24 | validations: 25 | required: true 26 | - type: textarea 27 | attributes: 28 | label: Description 29 | description: A clear and concise description of what the bug is. 30 | validations: 31 | required: true 32 | - type: textarea 33 | attributes: 34 | label: Expected behavior 35 | description: A clear and concise description of what you expected to happen. 36 | - type: textarea 37 | attributes: 38 | label: How to Reproduce 39 | description: Please provide steps to reproduce this bug. 40 | placeholder: | 41 | 1. Get package from '...' 42 | 2. Then run '...' 43 | 3. An error occurs. 44 | validations: 45 | required: true 46 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | name: ✨ Feature request 2 | description: Suggest an idea 3 | title: "✨ <title>" 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: > 8 | **Thank you for wanting to suggest a feature for this project!** 9 | 10 | ⚠ 11 | Verify first that your idea is not [already requested on GitHub](https://github.com/munich-quantum-toolkit/debugger/search?q=is%3Aissue&type=issues). 12 | 13 | - type: textarea 14 | attributes: 15 | label: What's the problem this feature will solve? 16 | description: >- 17 | What are you trying to do, that you are unable to achieve as it currently stands? 18 | placeholder: >- 19 | I'm trying to do X and I'm missing feature Y for this to be 20 | easily achievable. 21 | validations: 22 | required: true 23 | 24 | - type: textarea 25 | attributes: 26 | label: Describe the solution you'd like 27 | description: > 28 | Clear and concise description of what you want to happen. 29 | placeholder: >- 30 | When I do X, I want to achieve Y in a situation when Z. 31 | validations: 32 | required: true 33 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "extern/**/*" 3 | - "**/python" 4 | - "test/**/*" 5 | - "src/mqt/debugger/dap/**/*" 6 | - "src/mqt/debugger/dap/*" 7 | - "app/*" 8 | - "src/frontend/**/*" 9 | - "**/__main__.py" 10 | 11 | coverage: 12 | range: 60..90 13 | precision: 1 14 | status: 15 | project: off 16 | patch: off 17 | 18 | flag_management: 19 | default_rules: 20 | carryforward: true 21 | statuses: 22 | - type: project 23 | target: auto 24 | threshold: 0.5% 25 | removed_code_behavior: adjust_base 26 | - type: patch 27 | target: 90% 28 | threshold: 1% 29 | individual_flags: 30 | - name: cpp 31 | paths: 32 | - "include" 33 | - "src" 34 | - name: python 35 | paths: 36 | - "src/mqt/**/*.py" 37 | statuses: 38 | - type: project 39 | threshold: 0.5% 40 | removed_code_behavior: adjust_base 41 | - type: patch 42 | target: 95% 43 | threshold: 1% 44 | 45 | parsers: 46 | gcov: 47 | branch_detection: 48 | conditional: no 49 | loop: no 50 | 51 | comment: 52 | layout: "reach, diff, flags, files" 53 | require_changes: true 54 | show_carryforward_flags: true 55 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. 4 | 5 | Fixes # (issue) 6 | 7 | ## Checklist: 8 | 9 | <!--- 10 | This checklist serves as a reminder of a couple of things that ensure your pull request will be merged swiftly. 11 | --> 12 | 13 | - [ ] The pull request only contains commits that are related to it. 14 | - [ ] I have added appropriate tests and documentation. 15 | - [ ] I have made sure that all CI jobs on GitHub pass. 16 | - [ ] The pull request introduces no new warnings and follows the project's style guidelines. 17 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: "MQT Debugger $RESOLVED_VERSION Release" 2 | tag-template: "v$RESOLVED_VERSION" 3 | categories: 4 | - title: "🚀 Features and Enhancements" 5 | labels: 6 | - "feature" 7 | - "enhancement" 8 | - "usability" 9 | - title: "🐛 Bug Fixes" 10 | labels: 11 | - "bug" 12 | - "fix" 13 | - title: "📄 Documentation" 14 | labels: 15 | - "documentation" 16 | - title: "🤖 CI" 17 | labels: 18 | - "continuous integration" 19 | - title: "📦 Packaging" 20 | labels: 21 | - "packaging" 22 | - title: "🧹 Code Quality" 23 | labels: 24 | - "code quality" 25 | - title: "⬆️ Dependencies" 26 | collapse-after: 5 27 | labels: 28 | - "dependencies" 29 | - "submodules" 30 | - "github_actions" 31 | change-template: "- $TITLE @$AUTHOR (#$NUMBER)" 32 | change-title-escapes: '\<*_&' 33 | version-resolver: 34 | major: 35 | labels: 36 | - "major" 37 | minor: 38 | labels: 39 | - "minor" 40 | patch: 41 | labels: 42 | - "patch" 43 | default: patch 44 | autolabeler: 45 | - label: "dependencies" 46 | title: 47 | - "/update pre-commit hooks/i" 48 | 49 | template: | 50 | ## 👀 What Changed 51 | 52 | $CHANGES 53 | 54 | **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION 55 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: "https://docs.renovatebot.com/renovate-schema.json", 3 | extends: ["config:recommended", ":gitSignOff"], 4 | prHourlyLimit: 10, 5 | enabledManagers: ["github-actions", "pre-commit", "pep621"], 6 | "pre-commit": { 7 | enabled: true 8 | }, 9 | lockFileMaintenance: { 10 | "enabled": true, 11 | "automerge": true, 12 | }, 13 | configMigration: true, 14 | labels: ["dependencies"], 15 | schedule: ["every weekend"], 16 | packageRules: [ 17 | { 18 | matchManagers: ["github-actions"], 19 | addLabels: ["github-actions"], 20 | commitMessagePrefix: "⬆\uFE0F\uD83D\uDC68\u200D\uD83D\uDCBB" 21 | }, 22 | { 23 | matchManagers: ["pep621"], 24 | addLabels: ["python"], 25 | commitMessagePrefix: "⬆\uFE0F\uD83D\uDC0D" 26 | }, 27 | { 28 | matchManagers: ["pre-commit"], 29 | addLabels: ["pre-commit"], 30 | commitMessagePrefix: "⬆\uFE0F\uD83E\uDE9D", 31 | }, 32 | { 33 | description: "Automerge patch updates", 34 | matchUpdateTypes: ["patch"], 35 | automerge: true 36 | }, 37 | { 38 | description: "Automerge minor updates for stable dependencies", 39 | matchManagers: ["pep621", "pre-commit"], 40 | matchUpdateTypes: ["minor", "patch"], 41 | matchCurrentVersion: "!/^0/", 42 | automerge: true 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /.github/support.rst: -------------------------------------------------------------------------------- 1 | Support 2 | ======= 3 | 4 | If you are stuck with a problem using MQT Debugger or are having questions, please do get in touch at our `Issues <https://github.com/munich-quantum-toolkit/debugger/issues>`_ or `Discussions <https://github.com/munich-quantum-toolkit/debugger/discussions>`_. We'd love to help. 5 | 6 | You can save time by following this procedure when reporting a problem: 7 | 8 | - Do try to solve the problem on your own first. Make sure to consult the `Documentation <https://mqt.readthedocs.io/projects/debugger>`_. 9 | - Search through past `Issues <https://github.com/munich-quantum-toolkit/debugger/issues>`_ to see if someone else already had the same problem. 10 | - 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. 11 | 12 | You can also always reach us at `quantum.cda@xcit.tum.de <mailto:quantum.cda@xcit.tum.de>`_. 13 | -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | name: CD 2 | on: 3 | release: 4 | types: [published] 5 | workflow_dispatch: 6 | pull_request: 7 | paths: 8 | - .github/workflows/cd.yml 9 | 10 | jobs: 11 | python-packaging: 12 | name: 🐍 Packaging 13 | uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging.yml@v1.10.0 14 | 15 | deploy: 16 | if: github.event_name == 'release' && github.event.action == 'published' 17 | name: 🚀 Deploy to PyPI 18 | runs-on: ubuntu-latest 19 | environment: 20 | name: pypi 21 | url: https://pypi.org/p/mqt.debugger 22 | permissions: 23 | id-token: write 24 | attestations: write 25 | contents: read 26 | needs: [python-packaging] 27 | steps: 28 | - uses: actions/download-artifact@v4 29 | with: 30 | pattern: cibw-* 31 | path: dist 32 | merge-multiple: true 33 | - name: Generate artifact attestation for sdist and wheel(s) 34 | uses: actions/attest-build-provenance@v2.3.0 35 | with: 36 | subject-path: "dist/*" 37 | - uses: pypa/gh-action-pypi-publish@release/v1 38 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | - assertion-tools 7 | - assertion-compilation 8 | pull_request: 9 | merge_group: 10 | workflow_dispatch: 11 | 12 | concurrency: 13 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | change-detection: 18 | name: 🔍 Change 19 | uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-change-detection.yml@v1.10.0 20 | 21 | cpp-tests: 22 | name: 🇨‌ Test 23 | needs: change-detection 24 | if: fromJSON(needs.change-detection.outputs.run-cpp-tests) 25 | uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-ci.yml@v1.10.0 26 | permissions: 27 | id-token: write # Explicitly allows the `id-token: write` permission for this job 28 | contents: read 29 | with: 30 | cmake-args: -DEIGEN_BUILD_TESTING=OFF -DEIGEN_BUILD_PKGCONFIG=OFF -DBUILD_TESTING=OFF -DCMAKE_Fortran_COMPILER=OFF 31 | cmake-args-macos: -DMQT_CORE_WITH_GMP=ON 32 | 33 | cpp-linter: 34 | name: 🇨‌ Lint 35 | needs: change-detection 36 | if: fromJSON(needs.change-detection.outputs.run-cpp-linter) 37 | uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-cpp-linter.yml@v1.10.0 38 | 39 | python-tests: 40 | name: 🐍 Test 41 | needs: change-detection 42 | permissions: 43 | id-token: write # Explicitly allows the `id-token: write` permission for this job 44 | contents: read 45 | if: fromJSON(needs.change-detection.outputs.run-python-tests) 46 | uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-ci.yml@v1.10.0 47 | 48 | code-ql: 49 | name: 📝 CodeQL 50 | needs: change-detection 51 | if: fromJSON(needs.change-detection.outputs.run-code-ql) 52 | uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-code-ql.yml@v1.10.0 53 | 54 | required-checks-pass: # This job does nothing and is only used for branch protection 55 | name: 🚦 Check 56 | if: always() 57 | needs: 58 | - change-detection 59 | - cpp-tests 60 | - cpp-linter 61 | - python-tests 62 | - code-ql 63 | runs-on: ubuntu-latest 64 | steps: 65 | - name: Decide whether the needed jobs succeeded or failed 66 | uses: re-actors/alls-green@release/v1 67 | with: 68 | allowed-skips: >- 69 | ${{ 70 | fromJSON(needs.change-detection.outputs.run-cpp-tests) 71 | && '' || 'cpp-tests,' 72 | }} 73 | ${{ 74 | fromJSON(needs.change-detection.outputs.run-cpp-linter) 75 | && '' || 'cpp-linter,' 76 | }} 77 | ${{ 78 | fromJSON(needs.change-detection.outputs.run-python-tests) 79 | && '' || 'python-tests,' 80 | }} 81 | ${{ 82 | fromJSON(needs.change-detection.outputs.run-code-ql) 83 | && '' || 'code-ql,' 84 | }} 85 | jobs: ${{ toJSON(needs) }} 86 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | types: [opened, reopened, synchronize] 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | update_release_draft: 15 | name: Run 16 | permissions: 17 | contents: write 18 | pull-requests: write 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: release-drafter/release-drafter@v6 22 | env: 23 | GITHUB_TOKEN: ${{ github.token }} 24 | -------------------------------------------------------------------------------- /.github/workflows/update-mqt-core.yml: -------------------------------------------------------------------------------- 1 | name: Update MQT Core 2 | on: 3 | schedule: 4 | # run once a month on the first day of the month at 00:00 UTC 5 | - cron: "0 0 1 * *" 6 | workflow_dispatch: 7 | inputs: 8 | update-to-head: 9 | description: "Update to the latest commit on the default branch" 10 | type: boolean 11 | required: false 12 | default: false 13 | pull_request: 14 | paths: 15 | - .github/workflows/update-mqt-core.yml 16 | 17 | concurrency: 18 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 19 | cancel-in-progress: true 20 | 21 | jobs: 22 | update-mqt-core: 23 | name: ⬆️ Update MQT Core 24 | uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-mqt-core-update.yml@v1.10.0 25 | with: 26 | update-to-head: ${{ github.event.inputs.update-to-head == 'true' }} 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | .ccache/ 9 | cmake-build-* 10 | 11 | # Distribution / packaging 12 | .Python 13 | /build/ 14 | /test/*/build 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | cover/ 56 | *.profraw 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | docs/api/ 78 | 79 | # PyBuilder 80 | .pybuilder/ 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # IPython 87 | profile_default/ 88 | ipython_config.py 89 | 90 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 91 | __pypackages__/ 92 | 93 | # Celery stuff 94 | celerybeat-schedule 95 | celerybeat.pid 96 | 97 | # SageMath parsed files 98 | *.sage.py 99 | 100 | # Environments 101 | .env* 102 | .venv* 103 | env*/ 104 | venv*/ 105 | ENV/ 106 | env.bak/ 107 | venv.bak/ 108 | 109 | # Spyder project settings 110 | .spyderproject 111 | .spyproject 112 | 113 | # Rope project settings 114 | .ropeproject 115 | 116 | # mkdocs documentation 117 | /site 118 | 119 | # mypy 120 | .mypy_cache/ 121 | .dmypy.json 122 | dmypy.json 123 | 124 | # Pyre type checker 125 | .pyre/ 126 | 127 | # pytype static type analyzer 128 | .pytype/ 129 | 130 | # Cython debug symbols 131 | cython_debug/ 132 | 133 | # setuptools_scm 134 | src/*/_version.py 135 | 136 | # SKBuild cache dir 137 | _skbuild/ 138 | 139 | # Any build dirs in the tests 140 | test/**/build/ 141 | /src/mqt/**/_version.py 142 | 143 | # Common editor files 144 | *~ 145 | *.swp 146 | 147 | # RPM spec file 148 | !/distro/*.spec 149 | /distro/*.tar.gz 150 | *.rpm 151 | 152 | # ruff 153 | .ruff_cache/ 154 | 155 | # OS specific stuff 156 | .DS_Store 157 | .DS_Store? 158 | ._* 159 | .Spotlight-V100 160 | .Trashes 161 | ehthumbs.db 162 | Thumbs.db 163 | 164 | .idea/ 165 | .vscode/ 166 | # tmt setup 167 | /distro/main.fmf 168 | /distro/plans/main.fmf 169 | /distro/tests/main.fmf 170 | experiments/ 171 | 172 | /docs/**/build 173 | .vs 174 | out/build 175 | 176 | node_modules/ 177 | wheelhouse/ 178 | 179 | .local-tools 180 | 181 | # test code for the CLIFrontEnd 182 | app/code/*.qasm 183 | 184 | .python-version 185 | 186 | .quick-commit-config.yaml 187 | -------------------------------------------------------------------------------- /.license-tools-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": { 3 | "name": "Chair for Design Automation, TUM\nCopyright (c) 2025 Munich Quantum Software Company GmbH", 4 | "years": [2024, 2025] 5 | }, 6 | "force_author": true, 7 | "license": "MIT", 8 | "title": false, 9 | "include": ["**/*"], 10 | "style_override_for_suffix": { 11 | ".pyi": "DOCSTRING_STYLE", 12 | ".yaml": "POUND_STYLE", 13 | ".toml": "POUND_STYLE", 14 | ".in": "POUND_STYLE" 15 | }, 16 | "exclude": [ 17 | "^\\.[^/]+", 18 | "/\\.[^/]+", 19 | ".*\\.qasm", 20 | ".*\\.md", 21 | ".*\\.bib", 22 | ".*\\.cff", 23 | ".*\\.css", 24 | ".*\\.json", 25 | ".*\\.html", 26 | ".*\\.tfc", 27 | ".*\\.qc", 28 | ".*\\.real", 29 | ".*\\.tex", 30 | ".*\\.out", 31 | ".*\\.rst", 32 | ".*\\.txt", 33 | "uv\\.lock", 34 | "py\\.typed", 35 | ".*build.*", 36 | "Doxyfile" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | ci: 2 | autofix_commit_msg: "\U0001F3A8 pre-commit fixes" 3 | autoupdate_commit_msg: "\u2B06\uFE0F\U0001FA9D update pre-commit hooks" 4 | skip: 5 | - mypy 6 | repos: 7 | - hooks: 8 | - id: check-added-large-files 9 | - id: check-case-conflict 10 | - id: check-docstring-first 11 | - id: check-merge-conflict 12 | - id: check-toml 13 | - id: check-yaml 14 | - id: debug-statements 15 | - id: end-of-file-fixer 16 | - id: mixed-line-ending 17 | - id: trailing-whitespace 18 | repo: https://github.com/pre-commit/pre-commit-hooks 19 | rev: v5.0.0 20 | - hooks: 21 | - args: 22 | - --remove-empty-cells 23 | - --preserve-cell-metadata 24 | - raw_mimetype 25 | - -- 26 | id: nb-clean 27 | repo: https://github.com/srstevenson/nb-clean 28 | rev: 4.0.1 29 | - hooks: 30 | - id: fix-ligatures 31 | - id: fix-smartquotes 32 | repo: https://github.com/sirosen/texthooks 33 | rev: 0.6.8 34 | - hooks: 35 | - id: rst-backticks 36 | - id: rst-directive-colons 37 | - id: rst-inline-touching-normal 38 | repo: https://github.com/pre-commit/pygrep-hooks 39 | rev: v1.10.0 40 | - hooks: 41 | - args: 42 | - --fix 43 | - --show-fixes 44 | id: ruff 45 | types_or: 46 | - python 47 | - pyi 48 | - jupyter 49 | - id: ruff-format 50 | types_or: 51 | - python 52 | - pyi 53 | - jupyter 54 | repo: https://github.com/astral-sh/ruff-pre-commit 55 | rev: v0.11.11 56 | - hooks: 57 | - additional_dependencies: 58 | - black==24.* 59 | id: blacken-docs 60 | repo: https://github.com/adamchainz/blacken-docs 61 | rev: 1.19.1 62 | - hooks: 63 | - id: license-tools 64 | repo: https://github.com/emzeat/mz-lictools 65 | rev: v2.7.0 66 | - hooks: 67 | - id: clang-format 68 | types_or: 69 | - c++ 70 | - c 71 | - cuda 72 | repo: https://github.com/pre-commit/mirrors-clang-format 73 | rev: v20.1.5 74 | - hooks: 75 | - id: cmake-format 76 | additional_dependencies: [pyyaml] 77 | types: [file] 78 | files: (\.cmake|CMakeLists.txt)(.in)?$ 79 | repo: https://github.com/cheshirekow/cmake-format-precommit 80 | rev: v0.6.13 81 | - hooks: 82 | - id: prettier 83 | types_or: 84 | - yaml 85 | - markdown 86 | - html 87 | - css 88 | - scss 89 | - javascript 90 | - json 91 | repo: https://github.com/pre-commit/mirrors-prettier 92 | rev: v4.0.0-alpha.8 93 | - hooks: 94 | - additional_dependencies: 95 | - importlib_resources 96 | - numpy 97 | - pytest 98 | args: [] 99 | files: ^(src/mqt|test/python) 100 | id: mypy 101 | repo: https://github.com/pre-commit/mirrors-mypy 102 | rev: v1.16.0 103 | - hooks: 104 | - args: 105 | - -L 106 | - wille,linz,astroid,anc 107 | - --skip 108 | - "*.ipynb" 109 | id: codespell 110 | repo: https://github.com/codespell-project/codespell 111 | rev: v2.4.1 112 | - hooks: 113 | - entry: PyBind|Numpy|Cmake|CCache|Github|PyTest|Mqt|Tum 114 | exclude: .pre-commit-config.yaml 115 | id: disallow-caps 116 | language: pygrep 117 | name: Disallow improper capitalization 118 | repo: local 119 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | formats: 4 | - pdf 5 | - htmlzip 6 | 7 | build: 8 | os: ubuntu-24.04 9 | tools: 10 | python: "3.12" 11 | apt_packages: 12 | - graphviz 13 | - inkscape 14 | jobs: 15 | post_checkout: 16 | # Skip docs build if the commit message contains "skip ci" 17 | - (git --no-pager log --pretty="tformat:%s -- %b" -1 | grep -viq "skip ci") || exit 183 18 | # Skip docs build if there are no changes related to docs 19 | - | 20 | if [ "$READTHEDOCS_VERSION_TYPE" = "external" ] && git diff --quiet origin/main -- docs/ .readthedocs.yaml src/mqt/ src/python include/*/python .github/contributing* .github/support*; 21 | then 22 | exit 183; 23 | fi 24 | # Unshallow the git clone and fetch tags to get proper version information 25 | - git fetch --unshallow --tags 26 | pre_build: 27 | # Set up uv 28 | - asdf plugin add uv 29 | - asdf install uv latest 30 | - asdf global uv latest 31 | build: 32 | html: 33 | - uv run --frozen --no-dev --group docs -m sphinx -T -b html -d docs/_build/doctrees -D language=en docs $READTHEDOCS_OUTPUT/html 34 | htmlzip: 35 | - uv run --frozen --no-dev --group docs -m sphinx -T -b dirhtml -d docs/_build/doctrees -D language=en docs docs/_build/dirhtml 36 | - mkdir -p $READTHEDOCS_OUTPUT/htmlzip 37 | - zip -r $READTHEDOCS_OUTPUT/htmlzip/html.zip docs/_build/dirhtml/* 38 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM Copyright (c) 2025 Munich Quantum 2 | # Software Company GmbH All rights reserved. 3 | # 4 | # SPDX-License-Identifier: MIT 5 | # 6 | # Licensed under the MIT License 7 | 8 | cmake_minimum_required(VERSION 3.26) 9 | cmake_policy(SET CMP0069 NEW) 10 | set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) 11 | 12 | project( 13 | mqt_debugger 14 | LANGUAGES CXX 15 | DESCRIPTION "MQT Debugger - A debugging tool for Quantum Circuits") 16 | 17 | option(BUILD_MQT_DEBUGGER_BINDINGS "Build the MQT Debugger Python bindings" OFF) 18 | option(BUILD_MQT_DEBUGGER_TESTS "Also build tests for the MQT Debugger project" ON) 19 | option(BUILD_MQT_DEBUGGER_APP "Also build the CLI app for the MQT Debugger project" ON) 20 | 21 | set(CMAKE_CXX_STANDARD 17) 22 | 23 | include(CheckIPOSupported) 24 | 25 | if(BUILD_MQT_DEBUGGER_BINDINGS) 26 | # ensure that the BINDINGS option is set 27 | set(BINDINGS 28 | ON 29 | CACHE INTERNAL "Enable settings related to Python bindings") 30 | # Some common settings for finding Python 31 | set(Python_FIND_VIRTUALENV 32 | FIRST 33 | CACHE STRING "Give precedence to virtualenvs when searching for Python") 34 | set(Python_FIND_FRAMEWORK 35 | LAST 36 | CACHE STRING "Prefer Brew/Conda to Apple framework Python") 37 | set(Python_FIND_STRATEGY 38 | LOCATION 39 | CACHE STRING "Prefer Brew/Conda to Apple framework Python") 40 | set(Python_ARTIFACTS_INTERACTIVE 41 | ON 42 | CACHE BOOL "Prevent multiple searches for Python and instead cache the results.") 43 | 44 | # top-level call to find Python 45 | find_package( 46 | Python 3.9 REQUIRED 47 | COMPONENTS Interpreter Development.Module 48 | OPTIONAL_COMPONENTS Development.SABIModule) 49 | endif() 50 | 51 | include(cmake/ExternalDependencies.cmake) 52 | 53 | # add main library code 54 | add_subdirectory(src) 55 | 56 | # add test app 57 | if(BUILD_MQT_DEBUGGER_APP) 58 | add_subdirectory(app) 59 | endif() 60 | 61 | # add test code 62 | if(BUILD_MQT_DEBUGGER_TESTS) 63 | enable_testing() 64 | include(GoogleTest) 65 | add_subdirectory(test) 66 | endif() 67 | 68 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in 69 | ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake IMMEDIATE @ONLY) 70 | add_custom_target(uninstall-debugger COMMAND ${CMAKE_COMMAND} -P 71 | ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) 72 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Chair for Design Automation, Technical University of Munich 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM Copyright (c) 2025 Munich Quantum 2 | # Software Company GmbH All rights reserved. 3 | # 4 | # SPDX-License-Identifier: MIT 5 | # 6 | # Licensed under the MIT License 7 | 8 | add_executable(mqt_debugger_app testDDSimDebugger.cpp) 9 | 10 | # set include directories 11 | target_include_directories(mqt_debugger_app PUBLIC ${PROJECT_SOURCE_DIR}/include 12 | ${PROJECT_BINARY_DIR}/include) 13 | 14 | # link to the MQT::Core libraries 15 | target_link_libraries(mqt_debugger_app PRIVATE ${PROJECT_NAME}) 16 | target_link_libraries(mqt_debugger_app PUBLIC MQT::CoreDD) 17 | target_link_libraries(mqt_debugger_app PRIVATE MQT::ProjectWarnings MQT::ProjectOptions) 18 | -------------------------------------------------------------------------------- /app/testDDSimDebugger.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file testDDSimDebugger.cpp 13 | * @brief A test application that runs the CLI frontend using the DD backend. 14 | */ 15 | 16 | #include "backend/dd/DDSimDebug.hpp" 17 | #include "frontend/cli/CliFrontEnd.hpp" 18 | 19 | #include <fstream> 20 | #include <iostream> 21 | #include <sstream> 22 | #include <string> 23 | 24 | using namespace mqt::debugger; 25 | 26 | int main() { 27 | std::ifstream file("program.qasm"); 28 | if (!file.is_open()) { 29 | file.open("../../app/code/test" 30 | ".qasm"); 31 | } 32 | if (!file.is_open()) { 33 | std::cerr << "Could not open file\n"; 34 | file.close(); 35 | return 1; 36 | } 37 | 38 | std::stringstream buffer; 39 | buffer << file.rdbuf(); 40 | 41 | const std::string code = buffer.str(); 42 | 43 | DDSimulationState state; 44 | createDDSimulationState(&state); 45 | 46 | file.close(); 47 | 48 | CliFrontEnd cli; 49 | cli.run(code.c_str(), &state.interface); 50 | 51 | destroyDDSimulationState(&state); 52 | 53 | return 0; 54 | } 55 | -------------------------------------------------------------------------------- /cmake/ExternalDependencies.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | include(FetchContent) 10 | set(FETCH_PACKAGES "") 11 | 12 | if(BUILD_MQT_DEBUGGER_BINDINGS) 13 | if(NOT SKBUILD) 14 | # Manually detect the installed pybind11 package. 15 | execute_process( 16 | COMMAND "${Python_EXECUTABLE}" -m pybind11 --cmakedir 17 | OUTPUT_STRIP_TRAILING_WHITESPACE 18 | OUTPUT_VARIABLE pybind11_DIR) 19 | 20 | # Add the detected directory to the CMake prefix path. 21 | list(APPEND CMAKE_PREFIX_PATH "${pybind11_DIR}") 22 | endif() 23 | 24 | # add pybind11 library 25 | find_package(pybind11 2.13 CONFIG REQUIRED) 26 | endif() 27 | 28 | # ---------------------------------------------------------------------------------Fetch MQT Core 29 | # cmake-format: off 30 | set(MQT_CORE_VERSION 3.0.2 31 | CACHE STRING "MQT Core version") 32 | set(MQT_CORE_REV "9b6e01482cc77f48c828d988407ee4f8e4e93b56" 33 | CACHE STRING "MQT Core identifier (tag, branch or commit hash)") 34 | set(MQT_CORE_REPO_OWNER "munich-quantum-toolkit" 35 | CACHE STRING "MQT Core repository owner (change when using a fork)") 36 | # cmake-format: on 37 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) 38 | # Fetch MQT Core 39 | FetchContent_Declare( 40 | mqt-core 41 | GIT_REPOSITORY https://github.com/${MQT_CORE_REPO_OWNER}/core.git 42 | GIT_TAG ${MQT_CORE_REV}) 43 | list(APPEND FETCH_PACKAGES mqt-core) 44 | else() 45 | find_package(mqt-core ${MQT_CORE_VERSION} QUIET) 46 | if(NOT mqt-core_FOUND) 47 | FetchContent_Declare( 48 | mqt-core 49 | GIT_REPOSITORY https://github.com/${MQT_CORE_REPO_OWNER}/core.git 50 | GIT_TAG ${MQT_CORE_REV}) 51 | list(APPEND FETCH_PACKAGES mqt-core) 52 | endif() 53 | endif() 54 | 55 | # ---------------------------------------------------------------------------------Fetch Eigen3 56 | # cmake-format: off 57 | set(EIGEN_VERSION 3.4.0 58 | CACHE STRING "Eigen3 version") 59 | # cmake-format: on 60 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) 61 | # Fetch Eigen3 62 | FetchContent_Declare( 63 | Eigen3 64 | GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git 65 | GIT_TAG ${EIGEN_VERSION} 66 | GIT_SHALLOW TRUE) 67 | list(APPEND FETCH_PACKAGES Eigen3) 68 | set(EIGEN_BUILD_TESTING 69 | OFF 70 | CACHE BOOL "Disable testing for Eigen") 71 | set(BUILD_TESTING 72 | OFF 73 | CACHE BOOL "Disable general testing") 74 | set(EIGEN_BUILD_DOC 75 | OFF 76 | CACHE BOOL "Disable documentation build for Eigen") 77 | else() 78 | find_package(Eigen3 ${EIGEN3_VERSION} REQUIRED NO_MODULE) 79 | if(NOT Eigen3_FOUND) 80 | FetchContent_Declare( 81 | Eigen3 82 | GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git 83 | GIT_TAG ${EIGEN3_VERSION} 84 | GIT_SHALLOW TRUE) 85 | list(APPEND FETCH_PACKAGES Eigen3) 86 | set(EIGEN_BUILD_TESTING 87 | OFF 88 | CACHE BOOL "Disable testing for Eigen") 89 | set(BUILD_TESTING 90 | OFF 91 | CACHE BOOL "Disable general testing") 92 | set(EIGEN_BUILD_DOC 93 | OFF 94 | CACHE BOOL "Disable documentation build for Eigen") 95 | endif() 96 | endif() 97 | 98 | if(BUILD_MQT_DEBUGGER_TESTS) 99 | set(gtest_force_shared_crt 100 | ON 101 | CACHE BOOL "" FORCE) 102 | set(GTEST_VERSION 103 | 1.14.0 104 | CACHE STRING "Google Test version") 105 | set(GTEST_URL https://github.com/google/googletest/archive/refs/tags/v${GTEST_VERSION}.tar.gz) 106 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) 107 | FetchContent_Declare(googletest URL ${GTEST_URL} FIND_PACKAGE_ARGS ${GTEST_VERSION} NAMES GTest) 108 | list(APPEND FETCH_PACKAGES googletest) 109 | else() 110 | find_package(googletest ${GTEST_VERSION} QUIET NAMES GTest) 111 | if(NOT googletest_FOUND) 112 | FetchContent_Declare(googletest URL ${GTEST_URL}) 113 | list(APPEND FETCH_PACKAGES googletest) 114 | endif() 115 | endif() 116 | endif() 117 | 118 | if(BUILD_MQT_DEBUGGER_BINDINGS) 119 | # add pybind11_json library 120 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) 121 | FetchContent_Declare( 122 | pybind11_json 123 | GIT_REPOSITORY https://github.com/pybind/pybind11_json 124 | FIND_PACKAGE_ARGS) 125 | list(APPEND FETCH_PACKAGES pybind11_json) 126 | else() 127 | find_package(pybind11_json QUIET) 128 | if(NOT pybind11_json_FOUND) 129 | FetchContent_Declare(pybind11_json GIT_REPOSITORY https://github.com/pybind/pybind11_json) 130 | list(APPEND FETCH_PACKAGES pybind11_json) 131 | endif() 132 | endif() 133 | endif() 134 | 135 | # Make all declared dependencies available. 136 | FetchContent_MakeAvailable(${FETCH_PACKAGES}) 137 | 138 | # Hide Eigen3 warnings 139 | get_target_property(Eigen3_Includes Eigen3::Eigen INTERFACE_INCLUDE_DIRECTORIES) 140 | -------------------------------------------------------------------------------- /cmake/cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | # Source: https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake 10 | 11 | if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") 12 | message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") 13 | endif() 14 | 15 | file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) 16 | string(REGEX REPLACE "\n" ";" files "${files}") 17 | foreach(file ${files}) 18 | message(STATUS "Uninstalling $ENV{DESTDIR}${file}") 19 | if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 20 | exec_program( 21 | "@CMAKE_COMMAND@" ARGS 22 | "-E remove \"$ENV{DESTDIR}${file}\"" 23 | OUTPUT_VARIABLE rm_out 24 | RETURN_VALUE rm_retval) 25 | if(NOT "${rm_retval}" STREQUAL 0) 26 | message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") 27 | endif() 28 | else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 29 | message(STATUS "File $ENV{DESTDIR}${file} does not exist.") 30 | endif() 31 | endforeach() 32 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | doxygen/ 3 | -------------------------------------------------------------------------------- /docs/Assertions.rst: -------------------------------------------------------------------------------- 1 | Assertions 2 | ========== 3 | 4 | This document describes the syntax and semantics of assertions in MQT Debugger. 5 | Assertions are a useful tool to test the correctness of programs by comparing the current state of the system to an expected state. 6 | The assertions in MQT Debugger allow developers to test either the exact or approximate state or certain properties, such as entanglement or superposition. 7 | 8 | The following sections will give an overview of the assertion syntax and the different types of assertions that can be used in MQT Debugger. 9 | 10 | .. note:: 11 | 12 | The targets of an assertion can be either individual qubits or full quantum registers. However, when the selected targets are subset of the full quantum state, 13 | then none of the qubits in this sub-state is allowed to be entangled with any other qubits outside of the sub-state. This is, because in many cases, exact sub-states 14 | as state vectors are not well-defined if they are entangled with other qubits. 15 | 16 | Entanglement Assertion 17 | ###################### 18 | 19 | Entanglement assertions check whether a set of qubits is entangled. For this purpose, every qubit in the set is compared to all other qubits in the set, and 20 | entanglement must exist between each possible pair. 21 | 22 | **QASM Syntax**: 23 | 24 | .. code-block:: 25 | 26 | assert-ent <target_qubit_1> [, <target_qubit_2> ...]*; 27 | 28 | *Example*: 29 | 30 | .. code-block:: 31 | 32 | assert-ent q[0], q[1], q[2]; 33 | 34 | This assertions checks for entanglement between qubits ``q[0]`` and ``q[1]``, ``q[0]`` and ``q[2]``, as well as ``q[1]`` and ``q[2]``. 35 | 36 | An example for a quantum state that would pass this assertion is the GHZ state: :math:`\frac{1}{\sqrt{2}}(|000\rangle + |111\rangle)`. 37 | As none of the individual qubits is separable, this state is entangled. 38 | 39 | An example for a quantum state that would fail this assertion is the state :math:`\frac{1}{\sqrt{2}}(|000\rangle + |110\rangle)`. 40 | As the least-significant qubit is separable, this state is not entangled. 41 | 42 | 43 | Superposition Assertion 44 | ####################### 45 | 46 | Superposition assertions check whether a set of qubits is in a superposition state. 47 | A set of qubits is in a superposition state if there exist at least two basis states of the full system that have different assignments for the target qubits and a non-zero amplitude. 48 | This means, that not every qubit in the set must be in a superposition state individually, but the full set must be in a superposition state. 49 | 50 | **QASM Syntax**: 51 | 52 | .. code-block:: 53 | 54 | assert-sup <target_qubit_1> [, <target_qubit_2> ...]*; 55 | 56 | *Example*: 57 | 58 | .. code-block:: 59 | 60 | assert-sup q[0], q[1], q[2]; 61 | 62 | This assertion checks for superposition of qubits ``q[0]``, ``q[1]``, and ``q[2]``. 63 | 64 | An example for a quantum state that would pass this assertion is the state :math:`\frac{1}{\sqrt{2}}(|0\rangle + |1\rangle) \otimes |0\rangle`. 65 | As two basis states have non-zero amplitudes (:math:`|00\rangle` and :math:`|10\rangle`), this state is in a superposition. 66 | 67 | An example for a quantum state that would fail this assertion is the state :math:`|00\rangle`. 68 | In this case, only a single state (:math:`|00\rangle`) has a non-zero amplitude, so the state is not in a superposition. 69 | 70 | 71 | Equality Assertion 72 | ################## 73 | 74 | Equality assertions compare the state of a set of qubits to a given state and fail if the states are not equal. 75 | Furthermore, a similarity threshold can be passed to the assertion, allowing for approximate comparisons. The similarity is computed through the 76 | cosine similarity of two functions and can be set between 0 and 1. If no similarity threshold is passed, the default value of 1 is used. 77 | 78 | The target state to compare to can be expressed as a state vector or as a new quantum circuit. 79 | **QASM Syntax**: 80 | 81 | .. code-block:: 82 | 83 | assert-eq [similarity], <target_qubit_1> [, <target_qubit_2> ...]* { STATE_REPRESENTATION } 84 | 85 | .. code-block:: 86 | 87 | STATE_REPRESENTATION = 88 | | <STATEVECTOR> 89 | | <CIRCUIT> 90 | 91 | STATEVECTOR = <amplitude_1> [, <amplitude_2> ...]* 92 | 93 | CIRCUIT = <QASM_CODE> 94 | 95 | If the selected state representation is a state vector, it must represent a system of the same number of qubits as the assertion compares to 96 | and use :math:`2^n` amplitudes. Amplitudes can be real or complex numbers using :math:`i` or :math:`j` for the imaginary unit. 97 | 98 | If the selected state representation is a circuit, it must be defined as a new quantum program in QASM format. The circuit must not contain any further assertions and 99 | must use the same number of qubits as the assertion compares to. The current system state will then be compared to the state after running the circuit. 100 | 101 | *Example*: 102 | 103 | .. code-block:: 104 | 105 | assert-eq 0.9, q[0], q[1] { 0.5, 0.5, 0.5, 0.5 }; 106 | 107 | This assertion checks whether the state of qubits ``q[0]`` and ``q[1]`` is equal to the state :math:`\frac{1}{2}(|00\rangle + |01\rangle + |10\rangle + |11\rangle)` with a similarity threshold of 0.9. 108 | 109 | .. code-block:: 110 | 111 | assert-eq q[0], q[1] { 112 | h q[0]; 113 | cx q[0], q[1]; 114 | }; 115 | 116 | This assertion checks whether the state of qubits ``q[0]`` and ``q[1]`` is equal to the bell state :math:`\frac{1}{\sqrt{2}}(|00\rangle + |11\rangle)`. 117 | -------------------------------------------------------------------------------- /docs/Contributing.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../.github/contributing.rst 2 | -------------------------------------------------------------------------------- /docs/DevelopmentGuide.rst: -------------------------------------------------------------------------------- 1 | Development Guide 2 | ================= 3 | 4 | 5 | Installation 6 | ############ 7 | 8 | In order to start developing, clone the MQT Debugger repository using 9 | 10 | .. code-block:: console 11 | 12 | $ git clone https://github.com/munich-quantum-toolkit/debugger 13 | 14 | A C++ compiler supporting C++17 and a minimum CMake version of 3.19 are required to build the project. 15 | 16 | Working on the core C++ library 17 | ############################### 18 | 19 | Our projects use CMake as the main build configuration tool. 20 | Building a project using CMake is a two-stage process. 21 | First, CMake needs to be configured by calling 22 | 23 | .. code-block:: console 24 | 25 | $ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release 26 | 27 | This tells CMake to search the current directory :code:`.` (passed via :code:`-S` for source) for a :code:`CMakeLists.txt` file and process it into a directory :code:`build` (passed via :code:`-B`). 28 | The flag :code:`-DCMAKE_BUILD_TYPE=Release` tells CMake to configure a *Release* build (as opposed to, e.g., a *Debug* build). We also recommend setting the flags 29 | :code:`-DEIGEN_BUILD_TESTING=OFF` and :code:`-DEIGEN_BUILD_DOC=OFF` to prevent the documentation and tests of the ``Eigen3`` library included in the project from being built. 30 | 31 | After configuring with CMake, the project can be built by calling 32 | 33 | .. code-block:: console 34 | 35 | $ cmake --build build --config Release 36 | 37 | This tries to build the project in the :code:`build` directory (passed via :code:`--build`). 38 | Some operating systems and developer environments explicitly require a configuration to be set, which is why the :code:`--config` flag is also passed to the build command. The flag :code:`--parallel <NUMBER_OF_THREADS>` may be added to trigger a parallel build. 39 | 40 | Building the project this way generates 41 | 42 | - the main library :code:`libmqt_debugger.a` (Unix) / :code:`mqt_debugger.lib` (Windows) in the :code:`build/src` directory 43 | - a test executable :code:`mqt_debugger_test` containing unit tests in the :code:`build/test` directory (this requires passing :code:`-DBUILD_MQT_DEBUGGER_TESTS=ON` to CMake during configuration) 44 | - the Python bindings library :code:`pydebugger.<...>` in the :code:`build/src/python` directory (this requires passing :code:`-DBUILD_MQT_DEBUGGER_BINDINGS=ON` to CMake during configuration) 45 | - the CLI App :code:`mqt_debugger_app` in the :code:`build/app` directory (this requires passing :code:`-DBUILD_MQT_DEBUGGER_APP=ON` to CMake during configuration) 46 | 47 | Working on the Python module 48 | ############################ 49 | 50 | The :code:`mqt.debugger` Python module can be conveniently built locally by calling 51 | 52 | .. code-block:: console 53 | 54 | (venv) $ pip install --editable . 55 | 56 | The :code:`--editable` flag ensures that changes in the Python code are instantly available without re-running the command. 57 | 58 | `Pybind11 <https://pybind11.readthedocs.io/>`_ is used for providing bindings of the C++ core library to Python (see `bindings.cpp <https://github.com/munich-quantum-toolkit/debugger/tree/main/src/python/bindings.cpp>`_). 59 | If parts of the C++ code have been changed, the above command has to be run again to make the changes visible in Python. 60 | -------------------------------------------------------------------------------- /docs/Diagnosis.rst: -------------------------------------------------------------------------------- 1 | Diagnosis Methods 2 | ================= 3 | 4 | This document describes the diagnostics methods available in MQT Debugger. 5 | The diagnostics methods can be used to analyze the state of the system, in particular to find potential error causes when an :doc:`assertion <Assertions>` fails. 6 | 7 | All of these methods require a :py:class:`Diagnostics <mqt.debugger.Diagnostics>` object to be executed, which can be obtained from the :py:class:`SimulationState <mqt.debugger.SimulationState>` object using 8 | :cpp:member:`SimulationState::getDiagnostics <SimulationStateStruct::getDiagnostics>`/:py:meth:`SimulationState.get_diagnostics <mqt.debugger.SimulationState.get_diagnostics>`. 9 | 10 | Error Cause Analysis 11 | ##################### 12 | 13 | When an assertion fails, several methods can be used to find potential error clauses. 14 | 15 | For further information, please refer to :cite:labelpar:`rovara2024debugging`. 16 | 17 | Cone of Influence Analysis 18 | -------------------------- 19 | 20 | Cone of Influence Analysis partitions a given quantum program into two parts: the part that influences the failed assertion and the part that does not. 21 | This analysis method can be used to reduce the number of instructions that need to be checked for potential error causes. If the Cone of Influence analysis manages to 22 | narrow down just a small subset of the program, the error cause can be found more quickly. Furthermore, other analysis methods use the Cone of Influence as input 23 | to find potential error causes more efficiently. 24 | 25 | The cone of influence can be obtained using :cpp:member:`Diagnostics::getDataDependencies <DiagnosticsStruct::getDataDependencies>`/:py:meth:`Diagnostics.get_data_dependencies <mqt.debugger.Diagnostics.get_data_dependencies>`. 26 | In the Python version, it requires a single instruction to be passed as an argument, and returns the indices of all instructions that influence it. 27 | In the C++ version, in addition to the desired instruction, a pointer to a boolean array must be passed. Each element of the array corresponds to an instruction in the program and is set to ``true`` if the instruction is part of the cone of influence. 28 | Finally, a boolean Flag (``includeCallers``) can be passed to also include instructions outside the current scope in the cone of influence. Otherwise, if the function is called 29 | inside a custom gate definition, it will only include data dependencies within the gate. 30 | 31 | The cone of influence is computed by recursively iterating over the instructions in the current cone of influence and adding all instructions that influence them, until it reaches a fixed point. 32 | 33 | Interaction Analysis 34 | -------------------- 35 | 36 | Interaction Analysis is a method to find potential error causes by analyzing the interactions between qubits in the system. 37 | It is automatically called when using :cpp:member:`Diagnostics::potentialErrorCauses <DiagnosticsStruct::potentialErrorCauses>`/:py:meth:`Diagnostics.potential_error_causes <mqt.debugger.Diagnostics.potential_error_causes>`. 38 | 39 | This analysis method can be used to find reasons for failing entanglement assertions: 40 | Whenever a failed entanglement assertion is encountered, the Interaction Analysis checks, whether the target qubits of the assertion interact with each other. 41 | 42 | If this is not the case, then clearly, no entanglement can be prepared, so it is treated as a potential error cause. The following code shows an example situation, 43 | in which Interaction Analysis would find a potential error cause: 44 | 45 | .. code-block:: 46 | 47 | qreg q[3]; 48 | 49 | h q[0]; 50 | cx q[0], q[1]; 51 | 52 | assert-ent q; 53 | 54 | Here, calling ``potential_error_causes()`` yields two errors: ``Missing interaction between q[0] and q[2]`` and ``Missing interaction between q[1] and q[2]``. 55 | 56 | .. note:: 57 | Interaction Analysis is generally a static analysis method. However, when it is performed at runtime for the `potential_error_causes()` method, 58 | it further uses dynamically obtained information to improve its results. During execution, the diagnostics tool keeps track of all actual qubits that 59 | were involved in instructions, even inside custom gate definitions, where static analysis would not be able to determine the exact qubits involved. 60 | This way, it can extend the interaction analysis throughout the entire program, even in other scopes. This is not always possible when performing interaction analysis statically. 61 | 62 | Control-Value Analysis 63 | ---------------------- 64 | 65 | Control-Value Analysis is a method that dynamically analyzes the program during execution to find incorrectly defined controlled gates. 66 | In particular, it looks for controlled gates for which the control is always purely in the state :math:`|0\rangle`. In these cases, 67 | the controlled gate will never affect the full state, which could be a sign for an error. 68 | 69 | This analysis also similarly checks for inverse-controlled gates (i.e., controlled gates that tirgger when the control value is :math:`|1\rangle`) that always 70 | have the state :math:`|0\rangle` as control. 71 | 72 | It is automatically called when using :cpp:member:`Diagnostics::potentialErrorCauses <DiagnosticsStruct::potentialErrorCauses>`/:py:meth:`Diagnostics.potential_error_causes <mqt.debugger.Diagnostics.potential_error_causes>`. 73 | 74 | The following code shows an example situation, in which Control-Value Analysis would find a potential error cause: 75 | 76 | .. code-block:: 77 | 78 | qreg q[3]; 79 | 80 | h q[0]; 81 | cx q[0], q[1]; 82 | cx q[2], q[0]; 83 | 84 | assert-ent q; 85 | 86 | Here, calling ``potential_error_causes()`` yields the error ``Controlled gate with constant control value`` for instruction ``cx q[2], q[0]``. 87 | -------------------------------------------------------------------------------- /docs/Installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | MQT Debugger is mainly developed as a C++ library with many of its interfaces for important modules being defined in C. 5 | Parts of the implementation build upon `MQT Core <https://github.com/munich-quantum-toolkit/core>`_, which forms the backbone of the `MQT <https://mqt.readthedocs.io>`_. 6 | In order to make the tool as accessible as possible, it comes with an easy-to-use Python interface. 7 | 8 | We encourage installing MQT Debugger via pip (preferably in a `virtual environment <https://docs.python.org/3/library/venv.html>`_): 9 | 10 | .. code-block:: console 11 | 12 | (venv) & pip install mqt.debugger 13 | 14 | In most practical cases (under 64-bit Linux, MacOS incl. Apple Silicon, and Windows), this requires no compilation and merely downloads and installs a platform-specific pre-built wheel. 15 | 16 | .. note:: 17 | In order to set up a virtual environment, you can use the following commands: 18 | 19 | .. code-block:: console 20 | 21 | $ python3 -m venv venv 22 | $ source venv/bin/activate 23 | 24 | If you are using Windows, you can use the following commands instead: 25 | 26 | .. code-block:: console 27 | 28 | $ python3 -m venv venv 29 | $ venv\Scripts\activate.bat 30 | 31 | It is recommended to make sure that you are using the latest version of pip, setuptools, and wheel before trying to install the project: 32 | 33 | .. code-block:: console 34 | 35 | (venv) $ pip install --upgrade pip setuptools wheel 36 | 37 | A Detailed Walk Through 38 | ####################### 39 | 40 | First, save the following lines as :code:`ghz.qasm` in a folder where you want to install MQT Debugger: 41 | 42 | .. code-block:: 43 | 44 | qreg q[3]; 45 | 46 | h q[0]; 47 | cx q[0], q[1]; 48 | cx q[2], q[0]; 49 | 50 | assert-ent q; 51 | 52 | Then, create the following Python script as :code:`debug_ghz.py` in the same folder: 53 | 54 | .. code-block:: python 55 | 56 | import mqt.debugger as dbg 57 | 58 | state = dbg.create_ddsim_simulation_state() 59 | with open("ghz.qasm") as f: 60 | state.load_code(f.read()) 61 | 62 | state.run_simulation() 63 | 64 | if state.did_assertion_fail(): 65 | print(f"Assertion failed at instruction {state.get_current_instruction() + 1}") 66 | problems = state.get_diagnostics().potential_error_causes() 67 | print("Potential Errors:") 68 | for problem in problems: 69 | print(f"{problem.type} at instruction {problem.instruction + 1}") 70 | 71 | The following snippet shows the installation process from setting up the virtual environment to running a small example program. 72 | 73 | .. code-block:: console 74 | 75 | $ python3 -m venv venv 76 | $ . venv/bin/activate 77 | (venv) $ pip install -U pip setuptools wheel 78 | (venv) $ pip install mqt.debugger 79 | (venv) $ python3 debug_ghz.py 80 | 81 | Building from Source for Performance 82 | #################################### 83 | 84 | In order to get the best performance out of MQT Debugger and enable platform-specific compiler optimizations that cannot be enabled on portable wheels, it is recommended to build the package from source via: 85 | 86 | .. code-block:: console 87 | 88 | (venv) $ pip install mqt.debugger --no-binary mqt.debugger 89 | 90 | This requires a `C++ compiler <https://en.wikipedia.org/wiki/List_of_compilers#C++_compilers>`_ compiler supporting *C++17* and a minimum `CMake <https://cmake.org/>`_ version of *3.19*. 91 | 92 | The library is continuously tested under Linux, MacOS, and Windows using the `latest available system versions for GitHub Actions <https://github.com/actions/virtual-environments>`_. 93 | In order to access the latest build logs, visit the `GitHub Actions page <https://github.com/munich-quantum-toolkit/debugger/actions/workflows/ci.yml>`_. 94 | 95 | .. note:: 96 | We noticed some issues when compiling with Microsoft's *MSCV* compiler toolchain. If you want to start development on this project under Windows, consider using the *clang* compiler toolchain. A detailed description of how to set this up can be found `here <https://docs.microsoft.com/en-us/cpp/build/clang-support-msbuild?view=msvc-160>`_. 97 | -------------------------------------------------------------------------------- /docs/Publications.rst: -------------------------------------------------------------------------------- 1 | Publications 2 | ============ 3 | 4 | .. *MQT Debugger* is academic software. Thus, many of its built-in algorithms have been published as scientific papers. 5 | 6 | .. *MQT Debugger* is academic software. Thus, its built-in algorithms have been published as a scientific paper. 7 | 8 | *MQT Debugger* is academic software. Thus, many of its built-in algorithms are being published as scientific papers. 9 | 10 | If you use *MQT Debugger* in your work, we would appreciate if you cited: :cite:labelpar:`rovara2024debugging`. 11 | 12 | Furthermore, if you use any of the particular algorithms such as 13 | 14 | - diagnostics and error cause analysis :cite:labelpar:`rovara2024debugging` 15 | - assertion refinement (moving or creating assertions) :cite:labelpar:`rovara2024assertionrefinement` 16 | - runtime verification on real quantum devices :cite:labelpar:`rovara2025runtimeverification` 17 | 18 | please consider citing their respective papers as well. A full list of related papers is given below. 19 | 20 | .. bibliography:: 21 | -------------------------------------------------------------------------------- /docs/Quickstart.rst: -------------------------------------------------------------------------------- 1 | Quickstart 2 | ========== 3 | 4 | This documentation gives a quick overview on how to get started with MQT Debugger using: 5 | - The Python library 6 | - The DAP server 7 | - The CLI app 8 | 9 | Python Library 10 | ############## 11 | 12 | The Python bindings are offered to give an easy start into using MQT Debugger. 13 | 14 | Working with the Python library of MQT Debugger requires several preparation steps: 15 | 16 | .. code-block:: python 17 | 18 | import mqt.debugger as dbg 19 | 20 | All main functionalities are included in the module ``mqt.debugger``, no other modules need to be imported. 21 | 22 | .. code-block:: python 23 | 24 | state = dbg.create_ddsim_simulation_state() 25 | 26 | The first step is to create a simulation state. The current implementation of MQT Debugger implements a single simulation backend based on 27 | decision diagrams from `MQT Core <https://github.com/munich-quantum-toolkit/core>`_. This backend is instantiated by calling :py:func:`create_ddsim_simulation_state <mqt.debugger.create_ddsim_simulation_state>`. 28 | 29 | .. code-block:: python 30 | 31 | state.load_code(your_code) 32 | 33 | Before running the debugger, a quantum program must be loaded into the state. This is done by calling :py:meth:`SimulationState.load_code <mqt.debugger.SimulationState.load_code>` with the quantum program as a string argument. 34 | Currently, the supported quantum program format is `QASM 2.0 <https://arxiv.org/abs/1707.03429>`_. 35 | 36 | After this setup is done, the debugging process can be started by stepping through the code one instruction at a time: 37 | 38 | .. code-block:: python 39 | 40 | state.step_forward() 41 | 42 | or by running the full simulation: 43 | 44 | .. code-block:: python 45 | 46 | state.run_simulation() 47 | 48 | In the second case, simulation will be run until the program ends, a failing assertion is encountered, or a breakpoint is reached. 49 | Further details on how to step through the program can be found in the :doc:`reference documentation <library/Library>`. 50 | 51 | Breakpoints can be set using 52 | 53 | .. code-block:: python 54 | 55 | state.set_breakpoint(character_index) 56 | 57 | where ``character_index`` is the index of the character in the original code's string, at which the breakpoint should be set. 58 | 59 | Assertions can be added to the code following the :doc:`assertion syntax <Assertions>` and are automatically evaluated. 60 | 61 | When an assertion fails, the diagnostics methods can be used to get more information about the failure. To this end, first access the diagnostics interface: 62 | 63 | .. code-block:: python 64 | 65 | diagnostics = state.get_diagnostics() 66 | 67 | Then, the potential error causes can be retrieved: 68 | 69 | .. code-block:: python 70 | 71 | problems = diagnostics.potential_error_causes() 72 | print(problems) 73 | 74 | DAP Server 75 | ########## 76 | 77 | This library provides a DAP Server that can be connected to from existing IDEs like Visual Studio Code or CLion. 78 | 79 | It can be started by calling 80 | 81 | .. code-block:: console 82 | 83 | python3 -m mqt.debugger.dap.adapter 84 | 85 | The server will then start on port 4711 and can accept one single connection from debugging clients. 86 | 87 | .. note:: 88 | Connecting to the server requires a client compatible with the `Debug Adapter Protocol <https://microsoft.github.io/debug-adapter-protocol//>`_. 89 | While most common IDEs already support it by default, some additional setup or extensions may be required to allow communication with arbitrary clients. 90 | 91 | The DAP Server provides all simulation methods that are accessible via the Python library. 92 | On assertion failures, the server will automatically pause the simulation and send a message to the client containing possible error causes. 93 | 94 | CLI App 95 | ####### 96 | 97 | The CLI app is a standalone application that can be used to debug quantum programs from the command line. It is mainly supposed to be used as a testing tool 98 | and thus does not provide all the features of the framework or full accessibility through CLI parameters. 99 | 100 | Instead, the CLI app will open a OpenQASM file with the name :code:`program.qasm` in the current working directory and start the debugging process for it. 101 | -------------------------------------------------------------------------------- /docs/Support.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../.github/support.rst 2 | -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | .acknowledgements { 2 | margin-top: 1rem; 3 | padding-bottom: 1rem; 4 | padding-top: 1rem; 5 | border-top: 1px solid var(--color-background-border); 6 | font-size: var(--font-size--small); 7 | color: var(--color-foreground-secondary); 8 | } 9 | 10 | .acknowledgements-logos { 11 | display: grid; 12 | grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); 13 | grid-gap: 1em; 14 | align-items: center; 15 | margin-top: 0.5rem; 16 | } 17 | .acknowledgement { 18 | display: flex; 19 | flex-direction: column; 20 | align-items: center; 21 | justify-content: center; 22 | } 23 | 24 | /* override the default background color for literal strings */ 25 | body:not([data-theme="light"]) .highlight .sa, 26 | .highlight .sb, 27 | .highlight .sc, 28 | .highlight .dl, 29 | .highlight .sd, 30 | .highlight .s2, 31 | .highlight .se, 32 | .highlight .sh, 33 | .highlight .si, 34 | .highlight .sx, 35 | .highlight .sr, 36 | .highlight .s1, 37 | .highlight .ss, 38 | .highlight .s1 { 39 | background-color: #00000001; 40 | } 41 | -------------------------------------------------------------------------------- /docs/_static/mqt_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munich-quantum-toolkit/debugger/1bd3311a4e3d163f788dcb0071903c9e35c03333/docs/_static/mqt_dark.png -------------------------------------------------------------------------------- /docs/_static/mqt_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munich-quantum-toolkit/debugger/1bd3311a4e3d163f788dcb0071903c9e35c03333/docs/_static/mqt_light.png -------------------------------------------------------------------------------- /docs/_templates/page.html: -------------------------------------------------------------------------------- 1 | {% extends "furo/page.html" %} {% block footer %} 2 | <div class="related-pages"> 3 | {% if next -%} 4 | <a class="next-page" href="{{ next.link }}"> 5 | <div class="page-info"> 6 | <div class="context"> 7 | <span>{{ _("Next") }}</span> 8 | </div> 9 | <div class="title">{{ next.title }}</div> 10 | </div> 11 | <svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg> 12 | </a> 13 | {%- endif %} {% if prev -%} 14 | <a class="prev-page" href="{{ prev.link }}"> 15 | <svg class="furo-related-icon"><use href="#svg-arrow-right"></use></svg> 16 | <div class="page-info"> 17 | <div class="context"> 18 | <span>{{ _("Previous") }}</span> 19 | </div> 20 | {% if prev.link == pathto(master_doc) %} 21 | <div class="title">{{ _("Home") }}</div> 22 | {% else %} 23 | <div class="title">{{ prev.title }}</div> 24 | {% endif %} 25 | </div> 26 | </a> 27 | {%- endif %} 28 | </div> 29 | <div class="acknowledgements"> 30 | The Munich Quantum Toolkit has been supported by the European Research Council 31 | (ERC) under the European Union's Horizon 2020 research and innovation program 32 | (grant agreement No. 101001318), the Bavarian State Ministry for Science and 33 | Arts through the Distinguished Professorship Program, as well as the Munich 34 | Quantum Valley, which is supported by the Bavarian state government with funds 35 | from the Hightech Agenda Bayern Plus. 36 | 37 | <div style="margin-top: 0.5em"> 38 | <div class="only-light" align="center"> 39 | <img 40 | src="https://raw.githubusercontent.com/munich-quantum-toolkit/.github/refs/heads/main/docs/_static/mqt-funding-footer-light.svg" 41 | width="90%" 42 | alt="MQT Funding Footer" 43 | /> 44 | </div> 45 | <div class="only-dark" align="center"> 46 | <img 47 | src="https://raw.githubusercontent.com/munich-quantum-toolkit/.github/refs/heads/main/docs/_static/mqt-funding-footer-dark.svg" 48 | width="90%" 49 | alt="MQT Funding Footer" 50 | /> 51 | </div> 52 | </div> 53 | </div> 54 | {% endblock footer %} 55 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Sphinx configuration file.""" 10 | 11 | from __future__ import annotations 12 | 13 | import os 14 | import subprocess 15 | import warnings 16 | from importlib import metadata 17 | from pathlib import Path 18 | from typing import TYPE_CHECKING 19 | 20 | import pybtex.plugin 21 | from pybtex.style.formatting.unsrt import Style as UnsrtStyle 22 | from pybtex.style.template import field, href 23 | 24 | if TYPE_CHECKING: 25 | from pybtex.database import Entry 26 | from pybtex.richtext import HRef 27 | 28 | ROOT = Path(__file__).parent.parent.resolve() 29 | 30 | 31 | try: 32 | from mqt.debugger import __version__ as version 33 | except ModuleNotFoundError: 34 | try: 35 | version = metadata.version("mqt.debugger") 36 | except ModuleNotFoundError: 37 | msg = ( 38 | "Package should be installed to produce documentation! " 39 | "Assuming a modern git archive was used for version discovery." 40 | ) 41 | warnings.warn(msg, stacklevel=1) 42 | 43 | from setuptools_scm import get_version 44 | 45 | version = get_version(root=str(ROOT), fallback_root=ROOT) 46 | 47 | # Filter git details from version 48 | release = version.split("+")[0] 49 | 50 | project = "MQT Debugger" 51 | author = "Chair for Design Automation, Technical University of Munich" 52 | language = "en" 53 | project_copyright = "Chair for Design Automation, Technical University of Munich" 54 | 55 | master_doc = "index" 56 | 57 | templates_path = ["_templates"] 58 | html_css_files = ["custom.css"] 59 | 60 | extensions = [ 61 | "myst_nb", 62 | "sphinx.ext.autodoc", 63 | "sphinx.ext.intersphinx", 64 | "sphinx.ext.napoleon", 65 | "sphinx_copybutton", 66 | "sphinx_design", 67 | "sphinxext.opengraph", 68 | "sphinx.ext.viewcode", 69 | "sphinxcontrib.inkscapeconverter", 70 | "sphinxcontrib.bibtex", 71 | "breathe", 72 | ] 73 | 74 | source_suffix = [".rst", ".md"] 75 | 76 | exclude_patterns = [ 77 | "_build", 78 | "**.ipynb_checkpoints", 79 | "**.jupyter_cache", 80 | "**jupyter_execute", 81 | "Thumbs.db", 82 | ".DS_Store", 83 | ".env", 84 | ".venv", 85 | ] 86 | 87 | pygments_style = "colorful" 88 | 89 | intersphinx_mapping = { 90 | "python": ("https://docs.python.org/3", None), 91 | "numpy": ("https://numpy.org/doc/stable/", None), 92 | "qiskit": ("https://docs.quantum.ibm.com/api/qiskit", None), 93 | "mqt": ("https://mqt.readthedocs.io/en/latest", None), 94 | "core": ("https://mqt.readthedocs.io/projects/core/en/latest", None), 95 | "ddsim": ("https://mqt.readthedocs.io/projects/ddsim/en/latest", None), 96 | "qmap": ("https://mqt.readthedocs.io/projects/qmap/en/latest", None), 97 | "qcec": ("https://mqt.readthedocs.io/projects/qcec/en/latest", None), 98 | "qecc": ("https://mqt.readthedocs.io/projects/qecc/en/latest", None), 99 | "syrec": ("https://mqt.readthedocs.io/projects/syrec/en/latest", None), 100 | } 101 | 102 | myst_enable_extensions = [ 103 | "amsmath", 104 | "colon_fence", 105 | "substitution", 106 | "deflist", 107 | "dollarmath", 108 | ] 109 | myst_substitutions = { 110 | "version": version, 111 | } 112 | myst_heading_anchors = 3 113 | 114 | # -- Options for {MyST}NB ---------------------------------------------------- 115 | 116 | nb_execution_mode = "cache" 117 | nb_mime_priority_overrides = [ 118 | # builder name, mime type, priority 119 | ("latex", "image/svg+xml", 15), 120 | ] 121 | 122 | 123 | class CDAStyle(UnsrtStyle): 124 | """Custom style for including PDF links.""" 125 | 126 | def format_url(self, _e: Entry) -> HRef: # noqa: PLR6301 127 | """Format URL field as a link to the PDF. 128 | 129 | Returns: 130 | The formatted URL field. 131 | """ 132 | url = field("url", raw=True) 133 | return href()[url, "[PDF]"] 134 | 135 | 136 | pybtex.plugin.register_plugin("pybtex.style.formatting", "cda_style", CDAStyle) 137 | 138 | bibtex_bibfiles = ["lit_header.bib", "refs.bib"] 139 | bibtex_default_style = "cda_style" 140 | 141 | copybutton_prompt_text = r"(?:\(\.?venv\) )?(?:\[.*\] )?\$ " 142 | copybutton_prompt_is_regexp = True 143 | copybutton_line_continuation_character = "\\" 144 | 145 | modindex_common_prefix = ["mqt.debugger."] 146 | 147 | add_module_names = False 148 | toc_object_entries_show_parents = "hide" 149 | python_use_unqualified_type_names = True 150 | napoleon_google_docstring = True 151 | napoleon_numpy_docstring = False 152 | 153 | 154 | breathe_projects = {"mqt-debugger": "doxygen/xml"} 155 | breathe_default_project = "mqt-debugger" 156 | 157 | read_the_docs_build = os.environ.get("READTHEDOCS", None) == "True" 158 | if read_the_docs_build: 159 | subprocess.call("mkdir -p _build/doxygen && doxygen", shell=True) # noqa: S602, S607 160 | subprocess.call( # noqa: S602 161 | "mkdir -p api/cpp && breathe-apidoc -o api/cpp -m -f -g namespace _build/doxygen/xml/", # noqa: S607 162 | shell=True, 163 | ) 164 | 165 | # -- Options for HTML output ------------------------------------------------- 166 | html_theme = "furo" 167 | html_static_path = ["_static"] 168 | html_theme_options = { 169 | "light_logo": "mqt_dark.png", 170 | "dark_logo": "mqt_light.png", 171 | "source_repository": "https://github.com/munich-quantum-toolkit/debugger/", 172 | "source_branch": "main", 173 | "source_directory": "docs/", 174 | "navigation_with_keys": True, 175 | } 176 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | Welcome to MQT Debugger's documentation! 2 | ======================================== 3 | 4 | MQT Debugger is a comprehensive framework for debugging and analyzing the behaviour of quantum programs. It is developed by the `Chair for Design Automation <https://www.cda.cit.tum.de/>`_ at the `Technical University of Munich <https://www.tum.de>`_ as part of the :doc:`Munich Quantum Toolkit <mqt:index>` (*MQT*). 5 | 6 | The framework provides tools for running quantum programs in a simulated environment, stepping through the code instruction-by-instruction, and displaying the quantum state at each step. 7 | It also allows developers to include assertions in their code, testing the correctness of provided programs, and, when an assertion fails, suggesting possible causes of the failure. 8 | 9 | MQT Debugger is accessible as a stand-alone application, as a C++ and Python library to include in custom programs, and as a DAP server that can be accessed by popular IDEs such as Visual Studio Code and CLion. 10 | 11 | We recommend you to start with the :doc:`installation instructions <Installation>`. 12 | Then proceed to the :doc:`quickstart guide <Quickstart>` and read the :doc:`reference documentation <library/Library>`. 13 | If you are interested in the theory behind MQT Debugger, have a look at the publications in the :doc:`publication list <Publications>`. 14 | 15 | We appreciate any feedback and contributions to the project. If you want to contribute, you can find more information in the :doc:`Contribution <Contributing>` guide. If you are having trouble with the installation or the usage of MQT Debugger, please let us know at our :doc:`Support <Support>` page. 16 | 17 | ---- 18 | 19 | .. toctree:: 20 | :hidden: 21 | 22 | self 23 | 24 | .. toctree:: 25 | :maxdepth: 2 26 | :caption: User Guide 27 | :glob: 28 | 29 | Installation 30 | Quickstart 31 | Debugging 32 | Assertions 33 | Diagnosis 34 | AssertionRefinement 35 | RuntimeVerification 36 | Publications 37 | 38 | .. toctree:: 39 | :maxdepth: 2 40 | :caption: Developers 41 | :glob: 42 | 43 | Contributing 44 | DevelopmentGuide 45 | Support 46 | 47 | .. toctree:: 48 | :maxdepth: 6 49 | :caption: API Reference 50 | :glob: 51 | 52 | library/Library 53 | -------------------------------------------------------------------------------- /docs/library/Library.rst: -------------------------------------------------------------------------------- 1 | Library 2 | ======= 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | interface/Interface 8 | dd/Dd 9 | parsing/Parsing 10 | python/Python 11 | -------------------------------------------------------------------------------- /docs/library/dd/DDSimDebug.rst: -------------------------------------------------------------------------------- 1 | DDSimDebug.hpp 2 | ============== 3 | 4 | .. doxygenfile:: DDSimDebug.hpp 5 | :project: mqt-debugger 6 | -------------------------------------------------------------------------------- /docs/library/dd/DDSimDiagnostics.rst: -------------------------------------------------------------------------------- 1 | DDSimDiagnostics.hpp 2 | ==================== 3 | 4 | .. doxygenfile:: DDSimDiagnostics.hpp 5 | :project: mqt-debugger 6 | -------------------------------------------------------------------------------- /docs/library/dd/Dd.rst: -------------------------------------------------------------------------------- 1 | DD Implementation 2 | ================= 3 | 4 | This section documents the implementation of the C interfaces specifically using the DD package from `MQT Core <https://github.com/munich-quantum-toolkit/core>`_ 5 | as a simulation backend and OpenQASM as the input language. 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | DDSimDebug 11 | DDSimDiagnostics 12 | -------------------------------------------------------------------------------- /docs/library/interface/Common.rst: -------------------------------------------------------------------------------- 1 | common.h 2 | ======== 3 | 4 | .. doxygenfile:: common.h 5 | :project: mqt-debugger 6 | -------------------------------------------------------------------------------- /docs/library/interface/Debug.rst: -------------------------------------------------------------------------------- 1 | debug.h 2 | ======= 3 | 4 | .. note:: 5 | 6 | As the `SimulationState` interface is defined in C, "member functions" are declared as function pointers. 7 | When using the interface, these function pointers can be accessed like normal C++ methods. 8 | 9 | .. doxygenfile:: debug.h 10 | :project: mqt-debugger 11 | -------------------------------------------------------------------------------- /docs/library/interface/Diagnostics.rst: -------------------------------------------------------------------------------- 1 | diagnostics.h 2 | ============= 3 | 4 | .. note:: 5 | 6 | As the `Diagnostics` interface is defined in C, "member functions" are declared as function pointers. 7 | When using the interface, these function pointers can be accessed like normal C++ methods. 8 | 9 | .. doxygenfile:: diagnostics.h 10 | :project: mqt-debugger 11 | -------------------------------------------------------------------------------- /docs/library/interface/Interface.rst: -------------------------------------------------------------------------------- 1 | C Interface 2 | =========== 3 | 4 | This section documents the interface that must be implemented for a debugger to be compatible with this framework. 5 | 6 | The interface is written in C and can be implemented with any compatible language. We provide an :doc:`example implementation <../dd/Dd>` in C++ 7 | based on the DD simulation backend from `MQT Core <https://github.com/munich-quantum-toolkit/core>`_. 8 | 9 | .. toctree:: 10 | :maxdepth: 4 11 | :caption: C-Interfaces 12 | :glob: 13 | 14 | Common 15 | Debug 16 | Diagnostics 17 | -------------------------------------------------------------------------------- /docs/library/parsing/AssertionParsing.rst: -------------------------------------------------------------------------------- 1 | AssertionParsing.hpp 2 | ===================== 3 | 4 | .. doxygenfile:: AssertionParsing.hpp 5 | :project: mqt-debugger 6 | -------------------------------------------------------------------------------- /docs/library/parsing/CodePreprocessing.rst: -------------------------------------------------------------------------------- 1 | CodePreprocessing.hpp 2 | ===================== 3 | 4 | .. doxygenfile:: CodePreprocessing.hpp 5 | :project: mqt-debugger 6 | -------------------------------------------------------------------------------- /docs/library/parsing/Parsing.rst: -------------------------------------------------------------------------------- 1 | Parsing 2 | ======= 3 | 4 | This section documents the parsing and preprocessing functionalities developed for this framework, partially for the :doc:`DD implementation <../dd/Dd>` of the debugger, 5 | and partially for general-purpose use. 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | AssertionParsing 11 | CodePreprocessing 12 | ParsingError 13 | Utils 14 | -------------------------------------------------------------------------------- /docs/library/parsing/ParsingError.rst: -------------------------------------------------------------------------------- 1 | ParsingError.hpp 2 | ===================== 3 | 4 | .. doxygenfile:: ParsingError.hpp 5 | :project: mqt-debugger 6 | -------------------------------------------------------------------------------- /docs/library/parsing/Utils.rst: -------------------------------------------------------------------------------- 1 | Utils.hpp 2 | ===================== 3 | 4 | .. doxygenfile:: Utils.hpp 5 | :project: mqt-debugger 6 | -------------------------------------------------------------------------------- /docs/library/python/Debug.rst: -------------------------------------------------------------------------------- 1 | mqt.debugger 2 | ============ 3 | 4 | .. automodule:: mqt.debugger 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/library/python/Python.rst: -------------------------------------------------------------------------------- 1 | Python 2 | ====== 3 | 4 | This section documents the Python modules that are provided by this framework. 5 | 6 | .. toctree:: 7 | :maxdepth: 4 8 | 9 | Debug 10 | dap/DAP 11 | -------------------------------------------------------------------------------- /docs/library/python/dap/Adapter.rst: -------------------------------------------------------------------------------- 1 | mqt.debugger.dap.adapter 2 | ============================ 3 | 4 | .. automodule:: mqt.debugger.dap.adapter 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | 9 | 10 | Starting this module directly using 11 | 12 | .. code-block:: console 13 | 14 | python -m mqt.debugger.dap.adapter 15 | 16 | will start the Adapter server on the default port 4711. 17 | -------------------------------------------------------------------------------- /docs/library/python/dap/DAP.rst: -------------------------------------------------------------------------------- 1 | mqt.debugger.dap 2 | ================= 3 | 4 | .. automodule:: mqt.debugger.dap 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | 9 | .. toctree:: 10 | :maxdepth: 4 11 | 12 | Adapter 13 | Messages 14 | -------------------------------------------------------------------------------- /docs/library/python/dap/Messages.rst: -------------------------------------------------------------------------------- 1 | mqt.debugger.dap.messages 2 | ========================= 3 | 4 | .. automodule:: mqt.debugger.dap.messages 5 | :members: 6 | :undoc-members: 7 | :show-inheritance: 8 | -------------------------------------------------------------------------------- /docs/refs.bib: -------------------------------------------------------------------------------- 1 | @MISC{rovara2024debugging, 2 | AUTHOR = {Damian Rovara and Lukas Burgholzer and Robert Wille}, 3 | TITLE = {A Framework for Debugging Quantum Programs}, 4 | YEAR = {2024}, 5 | EPRINT = {2412.12269}, 6 | EPRINTTYPE = {arxiv}, 7 | } 8 | 9 | @MISC{rovara2024assertionrefinement, 10 | AUTHOR = {Damian Rovara and Lukas Burgholzer and Robert Wille}, 11 | TITLE = {Automatically Refining Assertions for Efficient Debugging of Quantum Programs}, 12 | YEAR = {2024}, 13 | EPRINT = {2412.14252}, 14 | EPRINTTYPE = {arxiv}, 15 | } 16 | 17 | @MISC{rovara2025runtimeverification, 18 | AUTHOR = {Damian Rovara and Lukas Burgholzer and Robert Wille}, 19 | TITLE = {A Framework for the Efficient Evaluation of Runtime Assertions on Quantum Computers}, 20 | YEAR = {2025}, 21 | EPRINT = {2505.03885}, 22 | EPRINTTYPE = {arxiv}, 23 | } 24 | -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file common.h 13 | * @brief Common types and functions used by the debugger. 14 | * 15 | * This file contains declarations for several common types required for 16 | * quantum computation, such as complex numbers, state vectors, etc. 17 | */ 18 | 19 | #pragma once 20 | // NOLINTBEGIN(modernize-use-using, performance-enum-size) 21 | 22 | #ifndef __cplusplus 23 | #else 24 | #include <cstddef> 25 | #include <cstdint> 26 | extern "C" { 27 | #endif 28 | 29 | /** 30 | * @brief The result of an operation. 31 | * 32 | * Can be either `OK` or `ERROR`. 33 | */ 34 | typedef enum { 35 | /** 36 | * @brief Indicates that the operation was successful. 37 | */ 38 | OK, 39 | /** 40 | * @brief Indicates that an error occurred during the operation. 41 | */ 42 | ERROR, 43 | } Result; 44 | 45 | /** 46 | * @brief The type of classical variables. 47 | * 48 | * Supports Bool, Int, and Float. 49 | */ 50 | typedef enum { VarBool, VarInt, VarFloat } VariableType; 51 | 52 | /** 53 | * @biref Represents the value of a classical variable. 54 | */ 55 | typedef union { 56 | /** 57 | * @brief The value represented as a boolean. 58 | */ 59 | bool boolValue; 60 | /** 61 | * @brief The value represented as a integer. 62 | */ 63 | int intValue; 64 | /** 65 | * @brief The value represented as a floating point number. 66 | */ 67 | double floatValue; 68 | } VariableValue; 69 | 70 | /** 71 | * @brief Represents a classical variable. 72 | */ 73 | typedef struct { 74 | const char* name; 75 | VariableType type; 76 | VariableValue value; 77 | } Variable; 78 | 79 | /** 80 | * @brief Represents a complex number. 81 | */ 82 | typedef struct { 83 | /** 84 | * @brief The real component of the complex number. 85 | */ 86 | double real; 87 | /** 88 | * @brief The imaginary component of the complex number. 89 | */ 90 | double imaginary; 91 | } Complex; 92 | 93 | /** 94 | * @brief Represents a quantum statevector. 95 | * 96 | * The number of qubits is fixed and the number of states is 2^`numQubits`. 97 | */ 98 | typedef struct { 99 | /** 100 | * @brief The number of qubits in the statevector. 101 | */ 102 | size_t numQubits; 103 | /** 104 | * @brief The number of states in the statevector. 105 | * 106 | * This is equal to 2^`numQubits`. 107 | */ 108 | size_t numStates; 109 | /** 110 | * @brief An area of memory containing the amplitudes of the statevector. 111 | * 112 | * The allocated memory must be enough to store `numStates` Complex numbers. 113 | */ 114 | Complex* amplitudes; 115 | } Statevector; 116 | 117 | /** 118 | * @brief The settings that should be used to compile an assertion program. 119 | */ 120 | typedef struct { 121 | /** 122 | * @brief The optimization level that should be used. Exact meaning depends on 123 | * the implementation, but typically 0 means no optimization. 124 | */ 125 | uint8_t opt; 126 | /** 127 | * @brief The index of the slice that should be compiled. 128 | */ 129 | size_t sliceIndex; 130 | } CompilationSettings; 131 | 132 | #ifdef __cplusplus 133 | } 134 | #endif 135 | 136 | // NOLINTEND(modernize-use-using, performance-enum-size) 137 | -------------------------------------------------------------------------------- /include/common/ComplexMathematics.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file ComplexMathematics.hpp 13 | * @brief Provides maths methods for complex numbers. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include "common.h" 19 | 20 | #include <Eigen/Dense> 21 | #include <cstddef> 22 | #include <string> 23 | #include <vector> 24 | 25 | namespace mqt::debugger { 26 | 27 | /** 28 | * @brief Compute the magnitude of a complex number. 29 | * @param c The complex number. 30 | * @return The computed magnitude. 31 | */ 32 | double complexMagnitude(Complex& c); 33 | 34 | /** 35 | * @brief Add two complex numbers. 36 | * @param c1 The first complex number. 37 | * @param c2 The second complex number. 38 | * @return The sum of the two complex numbers. 39 | */ 40 | Complex complexAddition(const Complex& c1, const Complex& c2); 41 | 42 | /** 43 | * @brief Multiply two complex numbers. 44 | * @param c1 The first complex number. 45 | * @param c2 The second complex number. 46 | * @return The product of the two complex numbers. 47 | */ 48 | Complex complexMultiplication(const Complex& c1, const Complex& c2); 49 | 50 | /** 51 | * @brief Compute the complex conjugate of a complex number. 52 | * @param c The complex number. 53 | * @return The complex conjugate of the input complex number. 54 | */ 55 | Complex complexConjugate(const Complex& c); 56 | 57 | /** 58 | * @brief Check if two qubits are entangled in a given density matrix. 59 | * 60 | * This is done by tracing out all other qubits from a density matrix and then 61 | * checking whether the shared information is greater than 0. 62 | * @param densityMatrix The density matrix to check for entanglement. 63 | * @param qubit1 The first qubit to check. 64 | * @param qubit2 The second qubit to check. 65 | * @return True if the qubits are entangled, false otherwise. 66 | */ 67 | bool areQubitsEntangled(std::vector<std::vector<Complex>>& densityMatrix, 68 | size_t qubit1, size_t qubit2); 69 | 70 | /** 71 | * @brief Translate a given statevector to a density matrix. 72 | * 73 | * @param sv The statevector to translate. 74 | * @return The computed density matrix. 75 | */ 76 | std::vector<std::vector<Complex>> toDensityMatrix(const Statevector& sv); 77 | 78 | /** 79 | * @brief Check if the partial trace of a given state vector is pure. 80 | * 81 | * This is true if and only if the trace of the square of the traced out density 82 | * matrix is equal to 1. 83 | * @param sv The state vector to check. 84 | * @param traceOut The indices of the qubits to trace out. 85 | * @return True if the partial trace is pure, false otherwise. 86 | */ 87 | bool partialTraceIsPure(const Statevector& sv, 88 | const std::vector<size_t>& traceOut); 89 | 90 | /** 91 | * @brief Gets the partial state vector by tracing out individual qubits from 92 | * the full state vector. 93 | * @param sv The full state vector. 94 | * @param traceOut The indices of the qubits to trace out. 95 | * @return The partial state vector. 96 | */ 97 | std::vector<std::vector<Complex>> 98 | getPartialTraceFromStateVector(const Statevector& sv, 99 | const std::vector<size_t>& traceOut); 100 | 101 | /** 102 | * @brief Convert a given vector-of-vectors matrix to an Eigen3 matrix. 103 | * @param matrix The vector-of-vectors matrix to convert. 104 | * @return A new Eigen3 matrix. 105 | */ 106 | Eigen::MatrixXcd // NOLINT 107 | toEigenMatrix(const std::vector<std::vector<Complex>>& matrix); 108 | 109 | /** 110 | * @brief Compute the amplitudes of a given state vector's sub-state. 111 | * @param sv The state vector to compute the sub-state from. 112 | * @param qubits The indices of the qubits to include in the sub-state. 113 | * @return The computed sub-state vector amplitudes. 114 | */ 115 | std::vector<Complex> 116 | getSubStateVectorAmplitudes(const Statevector& sv, 117 | const std::vector<size_t>& qubits); 118 | 119 | /** 120 | * @brief Generate a string representation of a complex number. 121 | * @param c The complex number. 122 | * @return The string representation of the complex number. 123 | */ 124 | std::string complexToString(const Complex& c); 125 | 126 | /** 127 | * @brief Compute the dot product of two state vectors. 128 | * @param sv1 The first state vector. 129 | * @param sv2 The second state vector. 130 | * @return The computed dot product. 131 | */ 132 | double dotProduct(const Statevector& sv1, const Statevector& sv2); 133 | 134 | } // namespace mqt::debugger 135 | -------------------------------------------------------------------------------- /include/common/Span.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file Span.hpp 13 | * @brief Provides a Span class for representing a contiguous range of elements. 14 | * 15 | * Accessing pointers from the C-interface like arrays is not compliant with the 16 | * C++ Core Guidelines. This class provides a safer way to access contiguous 17 | * ranges of elements. 18 | */ 19 | 20 | #pragma once 21 | 22 | #include <cstddef> 23 | 24 | namespace mqt::debugger { 25 | 26 | /** 27 | * @brief Represents a contiguous range of elements that can be accessed as an 28 | * array. 29 | * @tparam T The type of the elements in the span. 30 | */ 31 | template <typename T> class Span { 32 | public: 33 | /** 34 | * @brief Constructs a new span from a pointer and a size. 35 | * @param ptr The pointer to the first element in the span. 36 | * @param count The number of elements in the span. 37 | */ 38 | Span(T* ptr, size_t count) : pointer(ptr), spanSize(count) {} 39 | 40 | /** 41 | * @brief Accesses the element at the given index. 42 | * @param index The index of the element to access. 43 | * @return The element at the given index. 44 | */ 45 | T& operator[](size_t index) const { 46 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 47 | return pointer[index]; 48 | } 49 | 50 | /** 51 | * @brief Returns a pointer to the first element in the span. 52 | * @return A pointer to the first element in the span. 53 | */ 54 | T* data() const { return pointer; } 55 | 56 | /** 57 | * @brief Returns the number of elements in the span. 58 | * @return The number of elements in the span. 59 | */ 60 | [[nodiscard]] size_t size() const { return spanSize; } 61 | 62 | private: 63 | /** 64 | * @brief The pointer on which the span is based. 65 | */ 66 | T* pointer; 67 | 68 | /** 69 | * @brief The number of elements in the span, given by the user. 70 | */ 71 | size_t spanSize; 72 | }; 73 | 74 | } // namespace mqt::debugger 75 | -------------------------------------------------------------------------------- /include/common/parsing/AssertionTools.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "AssertionParsing.hpp" 14 | #include "CodePreprocessing.hpp" 15 | 16 | #include <cstdint> 17 | #include <memory> 18 | 19 | namespace mqt::debugger { 20 | 21 | /** 22 | * @brief Define a general commutation rule. 23 | * 24 | * @param name The name of the commutation rule. 25 | * @param expression The expression that performs the check. 26 | */ 27 | #define COMMUTATION_RULE_GENERAL(name, expression) \ 28 | COMMUTATION_RULE_TEMPLATE(Assertion, name, expression) 29 | 30 | /** 31 | * @brief Define a commutation rule for entanglement assertions. 32 | * 33 | * @param name The name of the commutation rule. 34 | * @param expression The expression that performs the check. 35 | */ 36 | #define COMMUTATION_RULE_ENT(name, expression) \ 37 | COMMUTATION_RULE_TEMPLATE(EntanglementAssertion, name, expression) 38 | 39 | /** 40 | * @brief Define a commutation rule for superposition assertions. 41 | * 42 | * @param name The name of the commutation rule. 43 | * @param expression The expression that performs the check. 44 | */ 45 | #define COMMUTATION_RULE_SUP(name, expression) \ 46 | COMMUTATION_RULE_TEMPLATE(SuperpositionAssertion, name, expression) 47 | 48 | /** 49 | * @brief Define a template for commutation rules. 50 | * 51 | * @param type The type of assertion to check. 52 | * @param name The name of the commutation rule. 53 | * @param expression The expression that performs the check. 54 | */ 55 | #define COMMUTATION_RULE_TEMPLATE(type, name, expression) \ 56 | const auto name = [](const type* assertion, \ 57 | const std::string& instructionName, \ 58 | const std::vector<std::string>& arguments) { \ 59 | (void)assertion; \ 60 | (void)instructionName; \ 61 | (void)arguments; \ 62 | return expression; \ 63 | } 64 | 65 | /** 66 | * @brief The possible results of a commutation check. 67 | * 68 | * Can be either `Commutes`, `DoesNotCommute`, or `Unknown`. 69 | */ 70 | enum class CommutationResult : uint8_t { 71 | /** 72 | * @brief Indicates that the instructions commute with certainty. 73 | */ 74 | Commutes, 75 | /** 76 | * @brief Indicates that the instructions do not commute with certainty. 77 | */ 78 | DoesNotCommute, 79 | /** 80 | * @brief Indicates that it cannot be said with certainty whether the 81 | * instructions commute or not. 82 | */ 83 | Unknown, 84 | }; 85 | 86 | /** 87 | * @brief Check if an assertion commutes with an instruction. 88 | * @param assertion The assertion to check. 89 | * @param instruction The instruction to check. 90 | * @return True if the assertion commutes with the instruction, false otherwise. 91 | */ 92 | bool doesCommute(const std::unique_ptr<Assertion>& assertion, 93 | const Instruction& instruction); 94 | 95 | } // namespace mqt::debugger 96 | -------------------------------------------------------------------------------- /include/common/parsing/ParsingError.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file ParsingError.hpp 13 | * @brief Header file for the ParsingError class 14 | */ 15 | 16 | #pragma once 17 | 18 | #include <stdexcept> 19 | #include <string> 20 | 21 | namespace mqt::debugger { 22 | 23 | /** 24 | * @brief Represents an error that occurred during parsing. 25 | */ 26 | class ParsingError : public std::runtime_error { 27 | public: 28 | /** 29 | * @brief Constructs a new ParsingError with the given message. 30 | * @param msg The error message. 31 | */ 32 | explicit ParsingError(const std::string& msg); 33 | }; 34 | 35 | } // namespace mqt::debugger 36 | -------------------------------------------------------------------------------- /include/common/parsing/Utils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file Utils.hpp 13 | * @brief Contains utility functions for parsing and string manipulation. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include <string> 19 | #include <vector> 20 | 21 | namespace mqt::debugger { 22 | 23 | /** 24 | * @brief Removes leading and trailing whitespace from a string. 25 | * @param str The string to trim. 26 | * @return The trimmed string. 27 | */ 28 | std::string trim(const std::string& str); 29 | 30 | /** 31 | * @brief Checks if a string starts with a given prefix. 32 | * @param str The string to check. 33 | * @param prefix The prefix to check for. 34 | * @return True if the string starts with the prefix, false otherwise. 35 | */ 36 | bool startsWith(const std::string& str, const std::string& prefix); 37 | 38 | /** 39 | * @brief Splits a string into a vector of strings based on a delimiter. 40 | * @param text The text to split. 41 | * @param delimiter The delimiter to split on. 42 | * @param includeEmpty Whether to include empty strings in the result. 43 | * @return The vector of strings. 44 | */ 45 | std::vector<std::string> splitString(const std::string& text, char delimiter, 46 | bool includeEmpty = true); 47 | 48 | /** 49 | * @brief Splits a string into a vector of strings based on multiple delimiters. 50 | * 51 | * A split occurs when any of the delimiters are encountered. 52 | * 53 | * @param text The text to split. 54 | * @param delimiters The delimiters to split on. 55 | * @param includeEmpty Whether to include empty strings in the result. 56 | * @return The vector of strings. 57 | */ 58 | std::vector<std::string> splitString(const std::string& text, 59 | const std::vector<char>& delimiters, 60 | bool includeEmpty = true); 61 | 62 | /** 63 | * @brief Replaces all occurrences of a substring in a string with another 64 | * string. 65 | * @param str The string to modify. 66 | * @param from The substring to replace. 67 | * @param to The string to replace the substring with. 68 | * @return The modified string. 69 | */ 70 | std::string replaceString(std::string str, const std::string& from, 71 | const std::string& to); 72 | 73 | /** 74 | * @brief Removes all whitespace from a string. 75 | * 76 | * This includes spaces, tabs, and newlines. 77 | * 78 | * @param str The string to remove whitespace from. 79 | * @return The string with whitespace removed. 80 | */ 81 | std::string removeWhitespace(std::string str); 82 | 83 | /** 84 | * @brief Checks if two strings approximately refer to the same variable. 85 | * 86 | * If both strings refer to a given index of a register, they are equal if the 87 | * strings are exactly equal. If one string refers to a full register and the 88 | * other only to a single index, they are equal if they both refer to the same 89 | * register. 90 | * 91 | * @param v1 The first variable. 92 | * @param v2 The second variable. 93 | * @return True if the strings refer to the same variable, false otherwise. 94 | */ 95 | bool variablesEqual(const std::string& v1, const std::string& v2); 96 | 97 | /** 98 | * @brief Extracts the base name of a register. 99 | * 100 | * @param variable The variable to extract the base name from. 101 | * @return The base name of the register. 102 | */ 103 | std::string variableBaseName(const std::string& variable); 104 | 105 | } // namespace mqt::debugger 106 | -------------------------------------------------------------------------------- /include/frontend/cli/CliFrontEnd.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file CliFrontEnd.hpp 13 | * @brief Provides a CLI frontend for the debugger. 14 | * 15 | * This file contains the declaration of the CliFrontEnd class, which provides 16 | * a command-line interface for the debugger. 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "backend/debug.h" 22 | 23 | #include <cstddef> 24 | #include <string> 25 | 26 | namespace mqt::debugger { 27 | 28 | #define ANSI_BG_YELLOW "\x1b[43m" 29 | #define ANSI_BG_RESET "\x1b[0m" 30 | #define ANSI_COL_GRAY "\x1b[90m" 31 | 32 | /** 33 | * @brief A command-line interface for the debugger. 34 | * 35 | * By creating an instance of this class and calling the `run` method, the user 36 | * can interact with the debugger using a command-line interface. 37 | */ 38 | class CliFrontEnd { 39 | public: 40 | /** 41 | * @brief Runs the debugger with the given code and state. 42 | * @param code The code to run (compatible with the provided 43 | * `SimulationState`) 44 | * @param state The state to run the code on 45 | */ 46 | void run(const char* code, SimulationState* state); 47 | 48 | private: 49 | /** 50 | * @brief The current code being executed. Used to display the code in the 51 | * CLI. 52 | */ 53 | std::string currentCode; 54 | 55 | /** 56 | * @brief Print the current state of the simulation. 57 | * @param state The simulation state. 58 | * @param inspecting The instruction that is currently inspected (or -1ULL if 59 | * nothing is being inspected). 60 | * @param codeOnly If true, only the code is displayed, not the state. 61 | */ 62 | void printState(SimulationState* state, size_t inspecting, 63 | bool codeOnly = false); 64 | 65 | /** 66 | * @brief Initialize the code for running it at a later time. 67 | */ 68 | void initCode(const char* code); 69 | 70 | /** 71 | * @brief Output a new code with updated assertions based on the assertion 72 | * refinement rules. 73 | * @param state The simulation state. 74 | */ 75 | void suggestUpdatedAssertions(SimulationState* state); 76 | }; 77 | 78 | } // namespace mqt::debugger 79 | -------------------------------------------------------------------------------- /include/python/InterfaceBindings.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file InterfaceBindings.hpp 13 | * @brief This file defines methods to be used for defining Python bindings for 14 | * the debugging and diagnostics backends. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "pybind11/pybind11.h" 20 | 21 | namespace mqt::debugger { 22 | 23 | /** 24 | * @brief Binds the main debugging framework to Python. 25 | * @param m The `pybind11` module. 26 | */ 27 | void bindFramework(pybind11::module& m); 28 | 29 | /** 30 | * @brief Binds the diagnostics backend to Python. 31 | * @param m The `pybind11` module. 32 | */ 33 | void bindDiagnostics(pybind11::module& m); 34 | 35 | } // namespace mqt::debugger 36 | -------------------------------------------------------------------------------- /include/python/dd/DDSimDebugBindings.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file DDSimDebugBindings.hpp 13 | * @brief This file defines methods to be used for defining Python bindings for 14 | * the DD Debugger. 15 | */ 16 | 17 | #pragma once 18 | 19 | #include "pybind11/pybind11.h" 20 | 21 | namespace mqt::debugger { 22 | 23 | /** 24 | * @brief Binds the dd debugging backend to Python. 25 | * @param m The `pybind11` module. 26 | */ 27 | void bindBackend(pybind11::module& m); 28 | 29 | } // namespace mqt::debugger 30 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM Copyright (c) 2025 Munich Quantum 2 | # Software Company GmbH All rights reserved. 3 | # 4 | # SPDX-License-Identifier: MIT 5 | # 6 | # Licensed under the MIT License 7 | 8 | add_library( 9 | ${PROJECT_NAME} 10 | backend/dd/DDSimDebug.cpp 11 | backend/dd/DDSimDiagnostics.cpp 12 | common/ComplexMathematics.cpp 13 | common/ComplexMathematics.cpp 14 | common/parsing/AssertionParsing.cpp 15 | common/parsing/AssertionTools.cpp 16 | common/parsing/CodePreprocessing.cpp 17 | common/parsing/ParsingError.cpp 18 | common/parsing/Utils.cpp 19 | frontend/cli/CliFrontEnd.cpp) 20 | 21 | # set include directories 22 | target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/include 23 | ${PROJECT_BINARY_DIR}/include) 24 | target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${Eigen3_Includes}) 25 | 26 | # link to the MQT::Core libraries 27 | target_link_libraries(${PROJECT_NAME} PUBLIC MQT::CoreDD MQT::CoreIR MQT::CoreCircuitOptimizer) 28 | target_link_libraries(${PROJECT_NAME} PRIVATE MQT::ProjectWarnings MQT::ProjectOptions 29 | MQT::CoreQASM) 30 | target_link_libraries(${PROJECT_NAME} PRIVATE Eigen3::Eigen) 31 | 32 | # add MQT alias 33 | add_library(MQT::Debugger ALIAS ${PROJECT_NAME}) 34 | 35 | if(BUILD_MQT_DEBUGGER_BINDINGS) 36 | add_subdirectory(python) 37 | endif() 38 | -------------------------------------------------------------------------------- /src/common/parsing/ParsingError.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file ParsingError.cpp 13 | * @brief Implementation of the ParsingError class. 14 | */ 15 | 16 | #include "common/parsing/ParsingError.hpp" 17 | 18 | #include <stdexcept> 19 | #include <string> 20 | 21 | namespace mqt::debugger { 22 | 23 | ParsingError::ParsingError(const std::string& msg) : std::runtime_error(msg) {} 24 | 25 | } // namespace mqt::debugger 26 | -------------------------------------------------------------------------------- /src/common/parsing/Utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file Utils.cpp 13 | * @brief Implementation of utility functions used by the debugger. 14 | */ 15 | 16 | #include "common/parsing/Utils.hpp" 17 | 18 | #include <algorithm> 19 | #include <cctype> 20 | #include <cstddef> 21 | #include <string> 22 | #include <vector> 23 | 24 | namespace mqt::debugger { 25 | 26 | std::string trim(const std::string& str) { 27 | auto start = std::find_if_not(str.begin(), str.end(), ::isspace); 28 | auto end = std::find_if_not(str.rbegin(), str.rend(), ::isspace).base(); 29 | return (start < end) ? std::string(start, end) : std::string(); 30 | } 31 | 32 | bool startsWith(const std::string& str, const std::string& prefix) { 33 | return str.compare(0, prefix.size(), prefix) == 0; 34 | } 35 | 36 | std::vector<std::string> splitString(const std::string& text, char delimiter, 37 | bool includeEmpty) { 38 | const std::vector<char> delimiters{delimiter}; 39 | return splitString(text, delimiters, includeEmpty); 40 | } 41 | 42 | std::vector<std::string> splitString(const std::string& text, 43 | const std::vector<char>& delimiters, 44 | bool includeEmpty) { 45 | std::vector<std::string> result; 46 | size_t pos = 0; 47 | while (true) { 48 | size_t min = std::string ::npos; 49 | for (const auto del : delimiters) { 50 | const size_t newPos = text.find(del, pos); 51 | min = newPos < min ? newPos : min; 52 | } 53 | if (min == std::string::npos) { 54 | break; 55 | } 56 | if (min > pos || includeEmpty) { 57 | result.push_back(text.substr(pos, min - pos)); 58 | } 59 | pos = min + 1; 60 | } 61 | if (text.length() > pos || includeEmpty) { 62 | result.push_back(text.substr(pos, text.length() - pos)); 63 | } 64 | return result; 65 | } 66 | 67 | std::string replaceString(std::string str, const std::string& from, 68 | const std::string& to) { 69 | size_t startPos = 0; 70 | while ((startPos = str.find(from, startPos)) != std::string::npos) { 71 | str.replace(startPos, from.length(), to); 72 | startPos += to.length(); // Handles case where 'to' is a substring of 'from' 73 | } 74 | return str; 75 | } 76 | 77 | std::string removeWhitespace(std::string str) { 78 | str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end()); 79 | return str; 80 | } 81 | 82 | bool variablesEqual(const std::string& v1, const std::string& v2) { 83 | if (v1.find('[') != std::string::npos && v2.find('[') != std::string::npos) { 84 | return v1 == v2; 85 | } 86 | if (v1.find('[') != std::string::npos) { 87 | return variablesEqual(splitString(v1, '[')[0], v2); 88 | } 89 | if (v2.find('[') != std::string::npos) { 90 | return variablesEqual(splitString(v2, '[')[0], v1); 91 | } 92 | return v1 == v2; 93 | } 94 | 95 | std::string variableBaseName(const std::string& v) { 96 | return splitString(v, '[')[0]; 97 | } 98 | 99 | } // namespace mqt::debugger 100 | -------------------------------------------------------------------------------- /src/mqt/debugger/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """A module for interfacing with the mqt-debugger library.""" 10 | 11 | from __future__ import annotations 12 | 13 | from . import check, dap 14 | from ._version import version as __version__ 15 | from .pydebugger import ( 16 | CompilationSettings, 17 | Complex, 18 | Diagnostics, 19 | ErrorCause, 20 | ErrorCauseType, 21 | SimulationState, 22 | Statevector, 23 | Variable, 24 | VariableType, 25 | VariableValue, 26 | create_ddsim_simulation_state, 27 | destroy_ddsim_simulation_state, 28 | ) 29 | 30 | __all__ = [ 31 | "CompilationSettings", 32 | "Complex", 33 | "Diagnostics", 34 | "ErrorCause", 35 | "ErrorCauseType", 36 | "SimulationState", 37 | "Statevector", 38 | "Variable", 39 | "VariableType", 40 | "VariableValue", 41 | "__version__", 42 | "check", 43 | "create_ddsim_simulation_state", 44 | "create_ddsim_simulation_state", 45 | "dap", 46 | "destroy_ddsim_simulation_state", 47 | ] 48 | -------------------------------------------------------------------------------- /src/mqt/debugger/_version.pyi: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | __version__: str 10 | version: str 11 | __version_tuple__: tuple[int, int, int, str, str] | tuple[int, int, int] 12 | version_tuple: tuple[int, int, int, str, str] | tuple[int, int, int] 13 | -------------------------------------------------------------------------------- /src/mqt/debugger/check/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """This module handles assertion checks for runs on real devices.""" 10 | 11 | from __future__ import annotations 12 | 13 | from .calibration import Calibration 14 | from .result_checker import Result, check_result 15 | from .run_preparation import estimate_required_shots, estimate_required_shots_from_path, start_compilation 16 | 17 | __all__ = [ 18 | "Calibration", 19 | "Result", 20 | "check_result", 21 | "estimate_required_shots", 22 | "estimate_required_shots_from_path", 23 | "start_compilation", 24 | ] 25 | -------------------------------------------------------------------------------- /src/mqt/debugger/check/__main__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Allows to run the runtime check as a module.""" 10 | 11 | from __future__ import annotations 12 | 13 | from . import runtime_check 14 | 15 | if __name__ == "__main__": 16 | runtime_check.main() 17 | -------------------------------------------------------------------------------- /src/mqt/debugger/check/calibration.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Provides device calibration information for the runtime check.""" 10 | 11 | from __future__ import annotations 12 | 13 | from dataclasses import dataclass, field 14 | 15 | missing_optionals: list[str] = [] 16 | 17 | try: 18 | import numpy as np 19 | except ImportError: 20 | missing_optionals.append("numpy") 21 | try: 22 | from qiskit import QuantumCircuit 23 | except ImportError: 24 | missing_optionals.append("qiskit") 25 | 26 | 27 | @dataclass 28 | class Calibration: 29 | """Represents the calibration data for a quantum device.""" 30 | 31 | error_rate_1q: float 32 | error_rate_2q: float 33 | error_rate_measurement: float 34 | specific_gate_errors: dict[str, float] = field(default_factory=dict) 35 | t: float = 0.0 36 | 37 | def get_expected_success_probability(self, code: str) -> float: 38 | """Get the expected success probability of the device for some program. 39 | 40 | ... (rest of the code remains the same) 41 | 42 | Currently, expected success probability is computed in terms of measurement and gate fidelities. 43 | 44 | Args: 45 | code (str): The program to check. 46 | 47 | Returns: 48 | float: The expected success probability. 49 | """ 50 | if missing_optionals: 51 | raise ImportError( 52 | "The following optional dependencies are required to use this feature: " + ", ".join(missing_optionals) 53 | ) 54 | fidelity_measurement = 1 - self.error_rate_measurement 55 | fidelity_1q = 1 - self.error_rate_1q 56 | fidelity_2q = 1 - self.error_rate_2q 57 | 58 | gate_fidelity = 1.0 59 | qc = QuantumCircuit.from_qasm_str(code) 60 | for instruction in qc.data: 61 | gate_type = instruction.name 62 | if gate_type in self.specific_gate_errors: 63 | gate_fidelity *= 1 - self.specific_gate_errors[gate_type] 64 | continue 65 | if gate_type == "barrier": 66 | continue 67 | if len(instruction.qubits) == 1: 68 | if gate_type == "measure": 69 | gate_fidelity *= fidelity_measurement 70 | else: 71 | gate_fidelity *= fidelity_1q 72 | else: 73 | gate_fidelity *= fidelity_2q 74 | 75 | qubit_times = dict.fromkeys(qc.qubits, 0) 76 | 77 | for instruction in qc.data: 78 | max_time = max(qubit_times[qubit] for qubit in instruction.qubits) 79 | for qubit in instruction.qubits: 80 | qubit_times[qubit] = max_time + 1 81 | qubit_fidelity = 1 82 | for qubit in qubit_times: 83 | qubit_fidelity *= np.exp(-qubit_times[qubit] * self.t) 84 | return gate_fidelity * qubit_fidelity 85 | 86 | @classmethod 87 | def example(cls) -> Calibration: 88 | """Get an example calibration.""" 89 | return cls(0.01, 0.01, 0.01) 90 | -------------------------------------------------------------------------------- /src/mqt/debugger/check/runtime_check.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """This module handles running assertion programs on real hardware and using statistical tests to check the results.""" 10 | 11 | from __future__ import annotations 12 | 13 | import argparse 14 | import json 15 | import locale 16 | from pathlib import Path 17 | 18 | from . import result_checker, run_preparation 19 | from .calibration import Calibration 20 | 21 | 22 | def main() -> None: 23 | """The main function.""" 24 | parser = argparse.ArgumentParser(description="Compile assertion programs for real hardware.") 25 | subparsers = parser.add_subparsers(dest="mode", required=True, help="The mode to run the program in.") 26 | parser.add_argument( 27 | "--calibration", type=Path, help="The path to a calibration file containing device information.", default=None 28 | ) 29 | 30 | # Add the subparser for the preparation mode. 31 | sub_preparation = subparsers.add_parser( 32 | "prepare", help="Prepare the assertion program for running on real hardware." 33 | ) 34 | sub_preparation.add_argument( 35 | "code", type=Path, help="The path to the assertion program in extended OpenQASM format." 36 | ) 37 | sub_preparation.add_argument( 38 | "--output-dir", "-o", type=Path, help="The directory to store the compiled slices.", default="." 39 | ) 40 | 41 | # Add the subparser for the checking mode. 42 | sub_checker = subparsers.add_parser("check", help="Check the results of the assertion program.") 43 | sub_checker.add_argument("results", type=Path, help="The path to a JSON file containing all results.") 44 | sub_checker.add_argument("--dir", "-d", type=Path, help="The path to the compiled program.", default=".") 45 | sub_checker.add_argument("--slice", "-s", type=int, help="The slice index to check.", default=1) 46 | sub_checker.add_argument("-p", type=float, help="The minimal desired p-value to accept an assertion.", default=0.05) 47 | 48 | # Add the subparser for shot estimation. 49 | sub_checker = subparsers.add_parser( 50 | "shots", help="Estimate the number of shots required to evaluate the assertion." 51 | ) 52 | sub_checker.add_argument("slice", type=Path, help="The path to a compiled assertion program slice.") 53 | sub_checker.add_argument("-p", type=float, help="The minimal desired p-value to accept an assertion.", default=0.05) 54 | sub_checker.add_argument( 55 | "--trials", type=int, help="The number of trials for probabilistic estimation.", default=1000 56 | ) 57 | sub_checker.add_argument( 58 | "--accuracy", type=float, help="The desired accuracy to report a sample count.", default=0.95 59 | ) 60 | 61 | args = parser.parse_args() 62 | 63 | if args.calibration is not None: 64 | with args.calibration.open("r") as f: 65 | calibration_data = Calibration(**json.load(f)) 66 | else: 67 | calibration_data = Calibration.example() 68 | 69 | if args.mode == "prepare": 70 | run_preparation.start_compilation(args.code, args.output_dir) 71 | elif args.mode == "check": 72 | with (args.dir / f"slice_{args.slice}.qasm").open("r", encoding=locale.getpreferredencoding(False)) as f: 73 | compiled_code = f.read() 74 | result_checker.check_result(compiled_code, args.results, calibration_data, p_value=args.p) 75 | elif args.mode == "shots": 76 | result = run_preparation.estimate_required_shots_from_path( 77 | args.slice, calibration_data, args.p, args.trials, args.accuracy 78 | ) 79 | print(f"Estimated required shots: {result}") # noqa: T201 80 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """This module handles DAP capabilities of the debugger.""" 10 | 11 | from __future__ import annotations 12 | 13 | from . import messages 14 | from .dap_server import DAPServer 15 | 16 | __all__ = ["DAPServer", "messages"] 17 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/adapter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """The main adapter module that can be run from outside the package to start the server.""" 10 | 11 | from __future__ import annotations 12 | 13 | from .dap_server import DAPServer 14 | 15 | if __name__ == "__main__": 16 | server = DAPServer() 17 | server.start() 18 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """A module for DAP message types.""" 10 | 11 | from __future__ import annotations 12 | 13 | from .capabilities_dap_event import CapabilitiesDAPEvent 14 | from .configuration_done_dap_message import ConfigurationDoneDAPMessage 15 | from .continue_dap_message import ContinueDAPMessage 16 | from .dap_event import DAPEvent 17 | from .dap_message import DAPMessage 18 | from .disconnect_dap_message import DisconnectDAPMessage 19 | from .exception_info_message import ExceptionInfoDAPMessage 20 | from .exited_dap_event import ExitedDAPEvent 21 | from .gray_out_event import GrayOutDAPEvent 22 | from .initialize_dap_message import InitializeDAPMessage 23 | from .initialized_dap_event import InitializedDAPEvent 24 | from .launch_dap_message import LaunchDAPMessage 25 | from .next_dap_message import NextDAPMessage 26 | from .output_dap_event import OutputDAPEvent 27 | from .pause_dap_message import PauseDAPMessage 28 | from .restart_dap_message import RestartDAPMessage 29 | from .restart_frame_dap_message import RestartFrameDAPMessage 30 | from .reverse_continue_dap_message import ReverseContinueDAPMessage 31 | from .scopes_dap_message import ScopesDAPMessage 32 | from .set_breakpoints_dap_message import SetBreakpointsDAPMessage 33 | from .set_exception_breakpoints_dap_message import SetExceptionBreakpointsDAPMessage 34 | from .stack_trace_dap_message import StackTraceDAPMessage 35 | from .step_back_dap_message import StepBackDAPMessage 36 | from .step_in_dap_message import StepInDAPMessage 37 | from .step_out_dap_message import StepOutDAPMessage 38 | from .stopped_dap_event import StoppedDAPEvent, StopReason 39 | from .terminate_dap_message import TerminateDAPMessage 40 | from .terminated_dap_event import TerminatedDAPEvent 41 | from .threads_dap_message import ThreadsDAPMessage 42 | from .variables_dap_message import VariablesDAPMessage 43 | 44 | Request = DAPMessage 45 | 46 | __all__ = [ 47 | "CapabilitiesDAPEvent", 48 | "ConfigurationDoneDAPMessage", 49 | "ContinueDAPMessage", 50 | "DAPEvent", 51 | "DAPMessage", 52 | "DisconnectDAPMessage", 53 | "ExceptionInfoDAPMessage", 54 | "ExitedDAPEvent", 55 | "GrayOutDAPEvent", 56 | "InitializeDAPMessage", 57 | "InitializedDAPEvent", 58 | "LaunchDAPMessage", 59 | "NextDAPMessage", 60 | "OutputDAPEvent", 61 | "PauseDAPMessage", 62 | "Request", 63 | "RestartDAPMessage", 64 | "RestartFrameDAPMessage", 65 | "ReverseContinueDAPMessage", 66 | "ScopesDAPMessage", 67 | "SetBreakpointsDAPMessage", 68 | "SetExceptionBreakpointsDAPMessage", 69 | "StackTraceDAPMessage", 70 | "StepBackDAPMessage", 71 | "StepInDAPMessage", 72 | "StepOutDAPMessage", 73 | "StopReason", 74 | "StoppedDAPEvent", 75 | "TerminateDAPMessage", 76 | "TerminatedDAPEvent", 77 | "ThreadsDAPMessage", 78 | "VariablesDAPMessage", 79 | ] 80 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/capabilities_dap_event.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'capabilities' DAP event.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import Any 14 | 15 | from .dap_event import DAPEvent 16 | 17 | 18 | class CapabilitiesDAPEvent(DAPEvent): 19 | """Represents the 'capabilities' DAP event.""" 20 | 21 | event_name = "capabilities" 22 | 23 | changes: dict[str, Any] 24 | 25 | def __init__(self, changes: dict[str, Any]) -> None: 26 | """Create a new 'capabilities' DAP event message. 27 | 28 | Args: 29 | changes (dict[str, Any]): The changes in the capabilities. 30 | """ 31 | super().__init__() 32 | self.changes = changes 33 | 34 | def validate(self) -> None: 35 | """Validate the 'capabilities' DAP event message after creation.""" 36 | 37 | def encode(self) -> dict[str, int]: 38 | """Encode the 'capabilities' DAP event message as a dictionary. 39 | 40 | Returns: 41 | dict[str, int]: The encoded 'capabilities' DAP event message. 42 | """ 43 | d = super().encode() 44 | d["body"] = { 45 | "capabilities": self.changes, 46 | } 47 | return d 48 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/configuration_done_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'configurationDone' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class ConfigurationDoneDAPMessage(DAPMessage): 22 | """Represents the 'configurationDone' DAP request.""" 23 | 24 | message_type_name: str = "configurationDone" 25 | 26 | def __init__(self, message: dict[str, Any]) -> None: 27 | """Initializes the 'ConfigurationDoneDAPMessage' instance. 28 | 29 | Args: 30 | message (dict[str, Any]): The object representing the 'ConfigurationDone' request. 31 | """ 32 | super().__init__(message) 33 | 34 | def validate(self) -> None: 35 | """Validates the 'ConfigurationDoneDAPMessage' instance.""" 36 | 37 | def handle(self, server: DAPServer) -> dict[str, Any]: 38 | """Performs the action requested by the 'ConfigurationDone' DAP request. 39 | 40 | Args: 41 | server (DAPServer): The DAP server that received the request. 42 | 43 | Returns: 44 | dict[str, Any]: The response to the request. 45 | """ 46 | return super().handle(server) 47 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/continue_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'continue' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class ContinueDAPMessage(DAPMessage): 22 | """Represents the 'continue' DAP request.""" 23 | 24 | message_type_name: str = "continue" 25 | 26 | def __init__(self, message: dict[str, Any]) -> None: 27 | """Initializes the 'ContinueDAPMessage' instance. 28 | 29 | Args: 30 | message (dict[str, Any]): The object representing the 'continue' request. 31 | """ 32 | super().__init__(message) 33 | 34 | def validate(self) -> None: 35 | """Validates the 'ContinueDAPMessage' instance.""" 36 | 37 | def handle(self, server: DAPServer) -> dict[str, Any]: 38 | """Performs the action requested by the 'continue' DAP request. 39 | 40 | Args: 41 | server (DAPServer): The DAP server that received the request. 42 | 43 | Returns: 44 | dict[str, Any]: The response to the request. 45 | """ 46 | server.simulation_state.run_simulation() 47 | d = super().handle(server) 48 | d["body"] = {} 49 | return d 50 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/dap_event.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents a DAP event message.""" 10 | 11 | from __future__ import annotations 12 | 13 | from abc import ABC, abstractmethod 14 | from typing import Any 15 | 16 | 17 | class DAPEvent(ABC): 18 | """Represents a generic DAP event message.""" 19 | 20 | event_name: str = "None" 21 | 22 | def __init__(self) -> None: 23 | """Create a new DAP event message.""" 24 | super().__init__() 25 | self.validate() 26 | 27 | @abstractmethod 28 | def validate(self) -> None: 29 | """Validate the DAP event message after creation. 30 | 31 | Raises an exception if the message is invalid. 32 | """ 33 | ... 34 | 35 | def encode(self) -> dict[str, Any]: 36 | """Encode the DAP event message as a dictionary. 37 | 38 | Returns: 39 | dict[str, Any]: The encoded DAP event message. 40 | """ 41 | return {"type": "event", "event": self.event_name} 42 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents a DAP request message.""" 10 | 11 | from __future__ import annotations 12 | 13 | from abc import ABC, abstractmethod 14 | from typing import TYPE_CHECKING, Any 15 | 16 | if TYPE_CHECKING: 17 | from .. import DAPServer 18 | 19 | 20 | class DAPMessage(ABC): 21 | """Represents a generic DAP request message.""" 22 | 23 | message_type_name: str = "None" 24 | 25 | sequence_number: int 26 | 27 | def __init__(self, message: dict[str, Any]) -> None: 28 | """Creates a new DAPMessage. 29 | 30 | Args: 31 | message (dict[str, Any]): The object containing the message data. 32 | """ 33 | super().__init__() 34 | self.sequence_number = message["seq"] 35 | self.validate() 36 | 37 | @abstractmethod 38 | def validate(self) -> None: 39 | """Validate the DAP request message after creation. 40 | 41 | Raises an exception if the message is invalid. 42 | """ 43 | ... 44 | 45 | def handle(self, _server: DAPServer) -> dict[str, Any]: 46 | """Performs the action requested by the DAP request message and returns the response. 47 | 48 | Args: 49 | server (DAPServer): The DAP server that received the request. 50 | 51 | Returns: 52 | dict[str, Any]: The response to the request. 53 | """ 54 | return { 55 | "type": "response", 56 | "request_seq": self.sequence_number, 57 | "success": True, 58 | "command": self.message_type_name, 59 | } 60 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/disconnect_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'disconnect' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | import mqt.debugger 16 | 17 | from .dap_message import DAPMessage 18 | 19 | if TYPE_CHECKING: 20 | from .. import DAPServer 21 | 22 | 23 | class DisconnectDAPMessage(DAPMessage): 24 | """Represents the 'disconnect' DAP request.""" 25 | 26 | message_type_name: str = "disconnect" 27 | 28 | def __init__(self, message: dict[str, Any]) -> None: 29 | """Initializes the 'DisconnectDAPMessage' instance. 30 | 31 | Args: 32 | message (dict[str, Any]): The object representing the 'disconnect' request. 33 | """ 34 | super().__init__(message) 35 | 36 | def validate(self) -> None: 37 | """Validates the 'DisconnectDAPMessage' instance.""" 38 | 39 | def handle(self, server: DAPServer) -> dict[str, Any]: 40 | """Performs the action requested by the 'disconnect' DAP request. 41 | 42 | Args: 43 | server (DAPServer): The DAP server that received the request. 44 | 45 | Returns: 46 | dict[str, Any]: The response to the request. 47 | """ 48 | mqt.debugger.destroy_ddsim_simulation_state(server.simulation_state) 49 | return super().handle(server) 50 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/exception_info_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'exceptionInfo' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | ASSERTION_DESCRIPTIONS = { 21 | "assert-ent": "The given qubits are not in an entangled state.", 22 | "assert-sup": "The given qubits are not in a superposition.", 23 | "assert-eq": "The given quantum states are not within the given tolerance.", 24 | } 25 | 26 | 27 | class ExceptionInfoDAPMessage(DAPMessage): 28 | """Represents the 'exceptionInfo' DAP request.""" 29 | 30 | message_type_name: str = "exceptionInfo" 31 | 32 | def __init__(self, message: dict[str, Any]) -> None: 33 | """Initializes the 'ExceptionInfoDAPMessage' instance. 34 | 35 | Args: 36 | message (dict[str, Any]): The object representing the 'exceptionInfo' request. 37 | """ 38 | super().__init__(message) 39 | 40 | def validate(self) -> None: 41 | """Validates the 'ExceptionInfoDAPMessage' instance.""" 42 | 43 | def handle(self, server: DAPServer) -> dict[str, Any]: 44 | """Performs the action requested by the 'exceptionInfo' DAP request. 45 | 46 | Args: 47 | server (DAPServer): The DAP server that received the request. 48 | 49 | Returns: 50 | dict[str, Any]: The response to the request. 51 | """ 52 | previous_instruction = server.simulation_state.get_current_instruction() 53 | (start, end) = server.simulation_state.get_instruction_position(previous_instruction) 54 | instruction = server.source_code[start:end] 55 | assertion_type = next(x for x in ("assert-ent", "assert-sup", "assert-eq") if x in instruction) 56 | d = super().handle(server) 57 | d["body"] = { 58 | "exceptionId": instruction.strip(), 59 | "breakMode": "always", 60 | "description": ASSERTION_DESCRIPTIONS[assertion_type], 61 | "details": { 62 | "typeName": assertion_type, 63 | }, 64 | } 65 | return d 66 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/exited_dap_event.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'exited' DAP event.""" 10 | 11 | from __future__ import annotations 12 | 13 | from .dap_event import DAPEvent 14 | 15 | 16 | class ExitedDAPEvent(DAPEvent): 17 | """Represents the 'exited' DAP event.""" 18 | 19 | event_name = "exited" 20 | 21 | exit_code: int 22 | 23 | def __init__(self, exit_code: int) -> None: 24 | """Initializes the 'ExitedDAPEvent' instance. 25 | 26 | Args: 27 | exit_code (int): The exit code of the process. 28 | """ 29 | super().__init__() 30 | self.exit_code = exit_code 31 | 32 | def validate(self) -> None: 33 | """Validates the 'ExitedDAPEvent' instance.""" 34 | 35 | def encode(self) -> dict[str, int]: 36 | """Encodes the 'ExitedDAPEvent' instance as a dictionary. 37 | 38 | Returns: 39 | dict[str, int]: The encoded 'ExitedDAPEvent' instance. 40 | """ 41 | d = super().encode() 42 | d["body"] = {"exitCode": self.exit_code} 43 | return d 44 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/gray_out_event.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'grayOut' DAP custom event.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import Any 14 | 15 | from .dap_event import DAPEvent 16 | 17 | 18 | class GrayOutDAPEvent(DAPEvent): 19 | """Represents the 'grayOut' DAP event.""" 20 | 21 | event_name = "grayOut" 22 | 23 | ranges: list[tuple[int, int]] 24 | source: dict[str, Any] 25 | 26 | def __init__(self, ranges: list[tuple[int, int]], source: dict[str, Any]) -> None: 27 | """Create a new 'grayOut' DAP event message. 28 | 29 | Args: 30 | ranges (list[tuple[int, int]]): The ranges to gray out. 31 | source (dict[str, Any]): The source of the ranges. 32 | """ 33 | super().__init__() 34 | self.ranges = ranges 35 | self.source = source 36 | 37 | def validate(self) -> None: 38 | """Validate the 'grayOut' DAP event message after creation.""" 39 | 40 | def encode(self) -> dict[str, str]: 41 | """Encode the 'grayOut' DAP event message as a dictionary. 42 | 43 | Returns: 44 | dict[str, str]: The encoded 'grayOut' DAP event message. 45 | """ 46 | d = super().encode() 47 | d["body"] = { 48 | "ranges": self.ranges, 49 | "source": self.source, 50 | } 51 | return d 52 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/initialize_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'initialize' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | import mqt.debugger 16 | 17 | from .dap_message import DAPMessage 18 | from .utils import get_default_capabilities 19 | 20 | if TYPE_CHECKING: 21 | from .. import DAPServer 22 | 23 | 24 | class InitializeDAPMessage(DAPMessage): 25 | """Represents the 'initialize' DAP request.""" 26 | 27 | message_type_name: str = "initialize" 28 | 29 | client_id: str 30 | client_name: str 31 | adapter_id: str 32 | path_format: str 33 | lines_start_at1: bool 34 | columns_start_at1: bool 35 | 36 | def __init__(self, message: dict[str, Any]) -> None: 37 | """Initializes the 'InitializeDAPMessage' instance. 38 | 39 | Args: 40 | message (dict[str, Any]): The object representing the 'initialize' request. 41 | """ 42 | self.client_id = message["arguments"].get("clientID", "") 43 | self.client_name = message["arguments"].get("clientName", "") 44 | self.adapter_id = message["arguments"].get("adapterID", "") 45 | self.path_format = message["arguments"].get("pathFormat", "") 46 | self.lines_start_at1 = message["arguments"].get("linesStartAt1", True) 47 | self.columns_start_at1 = message["arguments"].get("columnsStartAt1", True) 48 | super().__init__(message) 49 | 50 | def validate(self) -> None: 51 | """Validates the 'InitializeDAPMessage' instance. 52 | 53 | Raises: 54 | ValueError: If the adapter ID is not `mqtqasm`. 55 | """ 56 | if self.adapter_id != "mqtqasm": 57 | msg = f"Adapter ID must be `mqtqasm`, was {self.adapter_id}" 58 | raise ValueError(msg) 59 | 60 | def handle(self, server: DAPServer) -> dict[str, Any]: 61 | """Performs the action requested by the 'initialize' DAP request. 62 | 63 | Args: 64 | server (DAPServer): The DAP server that received the request. 65 | 66 | Returns: 67 | dict[str, Any]: The response to the request. 68 | """ 69 | server.columns_start_at_one = self.columns_start_at1 70 | server.lines_start_at_one = self.lines_start_at1 71 | server.simulation_state = mqt.debugger.create_ddsim_simulation_state() 72 | return { 73 | "type": "response", 74 | "request_seq": self.sequence_number, 75 | "success": True, 76 | "command": "initialize", 77 | "body": get_default_capabilities(), 78 | } 79 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/initialized_dap_event.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'initialized' DAP event.""" 10 | 11 | from __future__ import annotations 12 | 13 | from .dap_event import DAPEvent 14 | 15 | 16 | class InitializedDAPEvent(DAPEvent): 17 | """Represents the 'initialized' DAP event.""" 18 | 19 | event_name = "initialized" 20 | 21 | def __init__(self) -> None: 22 | """Create a new 'initialized' DAP event message.""" 23 | super().__init__() 24 | 25 | def validate(self) -> None: 26 | """Validate the 'initialized' DAP event message after creation.""" 27 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/launch_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'launch' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | import locale 14 | from pathlib import Path 15 | from typing import TYPE_CHECKING, Any 16 | 17 | from .dap_message import DAPMessage 18 | 19 | if TYPE_CHECKING: 20 | from .. import DAPServer 21 | 22 | 23 | class LaunchDAPMessage(DAPMessage): 24 | """Represents the 'launch' DAP request.""" 25 | 26 | message_type_name: str = "launch" 27 | 28 | no_debug: bool 29 | stop_on_entry: bool 30 | program: str 31 | 32 | def __init__(self, message: dict[str, Any]) -> None: 33 | """Initializes the 'LaunchDAPMessage' instance. 34 | 35 | Args: 36 | message (dict[str, Any]): The object representing the 'launch' request. 37 | """ 38 | self.no_debug = message["arguments"].get("noDebug", False) 39 | self.program = message["arguments"].get("program", "") 40 | self.stop_on_entry = message["arguments"].get("stopOnEntry", "") 41 | super().__init__(message) 42 | 43 | def validate(self) -> None: 44 | """Validates the 'LaunchDAPMessage' instance. 45 | 46 | Raises: 47 | ValueError: If the 'program' field is missing or the file does not exist. 48 | """ 49 | if not self.program: 50 | msg = "The 'program' field is required." 51 | raise ValueError(msg) 52 | if not Path(self.program).exists(): 53 | msg = f"The file '{self.program}' does not exist." 54 | raise ValueError(msg) 55 | 56 | def handle(self, server: DAPServer) -> dict[str, Any]: 57 | """Performs the action requested by the 'launch' DAP request. 58 | 59 | Args: 60 | server (DAPServer): The DAP server that received the request. 61 | 62 | Returns: 63 | dict[str, Any]: The response to the request. 64 | """ 65 | program_path = Path(self.program) 66 | with program_path.open("r", encoding=locale.getpreferredencoding(False)) as f: 67 | code = f.read() 68 | server.source_code = code 69 | try: 70 | server.simulation_state.load_code(code) 71 | except RuntimeError: 72 | return { 73 | "type": "response", 74 | "request_seq": self.sequence_number, 75 | "success": False, 76 | "command": "launch", 77 | "message": "An error occurred while parsing the code.", 78 | } 79 | if not self.stop_on_entry: 80 | server.simulation_state.run_simulation() 81 | server.source_file = {"name": program_path.name, "path": self.program} 82 | return { 83 | "type": "response", 84 | "request_seq": self.sequence_number, 85 | "success": True, 86 | "command": "launch", 87 | } 88 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/next_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'next' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class NextDAPMessage(DAPMessage): 22 | """Represents the 'next' DAP request.""" 23 | 24 | message_type_name: str = "next" 25 | 26 | def __init__(self, message: dict[str, Any]) -> None: 27 | """Initializes the 'NextDAPMessage' instance. 28 | 29 | Args: 30 | message (dict[str, Any]): The object representing the 'next' request. 31 | """ 32 | super().__init__(message) 33 | 34 | def validate(self) -> None: 35 | """Validates the 'NextDAPMessage' instance.""" 36 | 37 | def handle(self, server: DAPServer) -> dict[str, Any]: 38 | """Performs the action requested by the 'next' DAP request. 39 | 40 | Args: 41 | server (DAPServer): The DAP server that received the request. 42 | 43 | Returns: 44 | dict[str, Any]: The response to the request. 45 | """ 46 | server.simulation_state.step_over_forward() 47 | return super().handle(server) 48 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/output_dap_event.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'output' DAP event.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import Any 14 | 15 | from .dap_event import DAPEvent 16 | 17 | 18 | class OutputDAPEvent(DAPEvent): 19 | """Represents the 'output' DAP event.""" 20 | 21 | event_name = "output" 22 | 23 | category: str 24 | output: str | None 25 | group: str | None 26 | line: int 27 | column: int 28 | source: dict[str, Any] 29 | 30 | def __init__( 31 | self, category: str, output: str | None, group: str | None, line: int, column: int, source: dict[str, Any] 32 | ) -> None: 33 | """Create a new 'output' DAP event message. 34 | 35 | Args: 36 | category (str): The output category. 37 | output (str): The output text. 38 | group (str): The output group. 39 | line (int): The line number. 40 | column (int): The column number. 41 | source (dict[str, Any]): The source of the output. 42 | """ 43 | super().__init__() 44 | self.category = category 45 | self.output = output 46 | self.group = group 47 | self.line = line 48 | self.column = column 49 | self.source = source 50 | 51 | def validate(self) -> None: 52 | """Validate the 'output' DAP event message after creation.""" 53 | 54 | def encode(self) -> dict[str, str]: 55 | """Encode the 'output' DAP event message as a dictionary. 56 | 57 | Returns: 58 | dict[str, str]: The encoded 'output' DAP event message. 59 | """ 60 | d = super().encode() 61 | d["body"] = { 62 | "category": self.category, 63 | "output": self.output, 64 | "group": self.group, 65 | "line": self.line, 66 | "column": self.column, 67 | "source": self.source, 68 | } 69 | return d 70 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/pause_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'pause' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class PauseDAPMessage(DAPMessage): 22 | """Represents the 'pause' DAP request.""" 23 | 24 | message_type_name: str = "pause" 25 | 26 | def __init__(self, message: dict[str, Any]) -> None: 27 | """Initializes the 'PauseDAPMessage' instance. 28 | 29 | Args: 30 | message (dict[str, Any]): The object representing the 'pause' request. 31 | """ 32 | super().__init__(message) 33 | 34 | def validate(self) -> None: 35 | """Validates the 'PauseDAPMessage' instance.""" 36 | 37 | def handle(self, server: DAPServer) -> dict[str, Any]: 38 | """Performs the action requested by the 'pause' DAP request. 39 | 40 | Args: 41 | server (DAPServer): The DAP server that received the request. 42 | 43 | Returns: 44 | dict[str, Any]: The response to the request. 45 | """ 46 | server.simulation_state.pause_simulation() 47 | return super().handle(server) 48 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/restart_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'restart' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | import locale 14 | from pathlib import Path 15 | from typing import TYPE_CHECKING, Any 16 | 17 | from .dap_message import DAPMessage 18 | 19 | if TYPE_CHECKING: 20 | from .. import DAPServer 21 | 22 | 23 | class RestartDAPMessage(DAPMessage): 24 | """Represents the 'restart' DAP request.""" 25 | 26 | message_type_name: str = "restart" 27 | 28 | no_debug: bool 29 | stop_on_entry: bool 30 | program: str 31 | 32 | def __init__(self, message: dict[str, Any]) -> None: 33 | """Initializes the 'RestartDAPMessage' instance. 34 | 35 | Args: 36 | message (dict[str, Any]): The object representing the 'restart' request. 37 | """ 38 | self.no_debug = message["arguments"]["arguments"].get("noDebug", False) 39 | self.program = message["arguments"]["arguments"].get("program", "") 40 | self.stop_on_entry = message["arguments"]["arguments"].get("stopOnEntry", "") 41 | super().__init__(message) 42 | 43 | def validate(self) -> None: 44 | """Validates the 'LaunchDAPMessage' instance. 45 | 46 | Raises: 47 | ValueError: If the 'program' field is missing or the file does not exist. 48 | """ 49 | if not self.program: 50 | msg = "The 'program' field is required." 51 | raise ValueError(msg) 52 | if not Path(self.program).exists(): 53 | msg = f"The file '{self.program}' does not exist." 54 | raise ValueError(msg) 55 | 56 | def handle(self, server: DAPServer) -> dict[str, Any]: 57 | """Performs the action requested by the 'restart' DAP request. 58 | 59 | Args: 60 | server (DAPServer): The DAP server that received the request. 61 | 62 | Returns: 63 | dict[str, Any]: The response to the request. 64 | """ 65 | server.simulation_state.reset_simulation() 66 | program_path = Path(self.program) 67 | with program_path.open("r", encoding=locale.getpreferredencoding(False)) as f: 68 | code = f.read() 69 | server.source_code = code 70 | server.simulation_state.load_code(code) 71 | if not self.stop_on_entry: 72 | server.simulation_state.run_simulation() 73 | server.source_file = {"name": program_path.name, "path": self.program} 74 | return { 75 | "type": "response", 76 | "request_seq": self.sequence_number, 77 | "success": True, 78 | "command": "launch", 79 | } 80 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/restart_frame_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'restartFrame' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class RestartFrameDAPMessage(DAPMessage): 22 | """Represents the 'restartFrame' DAP request.""" 23 | 24 | message_type_name: str = "restartFrame" 25 | frame: int 26 | 27 | def __init__(self, message: dict[str, Any]) -> None: 28 | """Initializes the 'RestartFrameDAPMessage' instance. 29 | 30 | Args: 31 | message (dict[str, Any]): The object representing the 'restartFrame' request. 32 | """ 33 | super().__init__(message) 34 | self.frame = message["arguments"]["frameId"] 35 | 36 | def validate(self) -> None: 37 | """Validates the 'RestartFrameDAPMessage' instance.""" 38 | 39 | def handle(self, server: DAPServer) -> dict[str, Any]: 40 | """Performs the action requested by the 'restartFrame' DAP request. 41 | 42 | Args: 43 | server (DAPServer): The DAP server that received the request. 44 | 45 | Returns: 46 | dict[str, Any]: The response to the request. 47 | """ 48 | while server.simulation_state.get_stack_depth() >= self.frame: 49 | server.simulation_state.step_out_backward() 50 | server.simulation_state.step_forward() 51 | d = super().handle(server) 52 | d["body"] = {} 53 | return d 54 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/reverse_continue_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'reverseContinue' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class ReverseContinueDAPMessage(DAPMessage): 22 | """Represents the 'reverseContinue' DAP request.""" 23 | 24 | message_type_name: str = "reverseContinue" 25 | 26 | def __init__(self, message: dict[str, Any]) -> None: 27 | """Initializes the 'ReverseContinueDAPMessage' instance. 28 | 29 | Args: 30 | message (dict[str, Any]): The object representing the 'reverseContinue' request. 31 | """ 32 | super().__init__(message) 33 | 34 | def validate(self) -> None: 35 | """Validates the 'ReverseContinueDAPMessage' instance.""" 36 | 37 | def handle(self, server: DAPServer) -> dict[str, Any]: 38 | """Performs the action requested by the 'reverseContinue' DAP request. 39 | 40 | Args: 41 | server (DAPServer): The DAP server that received the request. 42 | 43 | Returns: 44 | dict[str, Any]: The response to the request. 45 | """ 46 | server.simulation_state.run_simulation_backward() 47 | d = super().handle(server) 48 | d["body"] = {} 49 | return d 50 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/scopes_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'scopes' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class ScopesDAPMessage(DAPMessage): 22 | """Represents the 'scopes' DAP request.""" 23 | 24 | message_type_name: str = "scopes" 25 | 26 | frame_id: int 27 | 28 | def __init__(self, message: dict[str, Any]) -> None: 29 | """Initializes the 'ScopesDAPMessage' instance. 30 | 31 | Args: 32 | message (dict[str, Any]): The object representing the 'scopes' request. 33 | """ 34 | super().__init__(message) 35 | self.frame_id = message["arguments"]["frameId"] 36 | 37 | def validate(self) -> None: 38 | """Validates the 'ScopesDAPMessage' instance.""" 39 | 40 | def handle(self, server: DAPServer) -> dict[str, Any]: 41 | """Performs the action requested by the 'scopes' DAP request. 42 | 43 | Args: 44 | server (DAPServer): The DAP server that received the request. 45 | 46 | Returns: 47 | dict[str, Any]: The response to the request. 48 | """ 49 | d = super().handle(server) 50 | d["body"] = {"scopes": [_get_classical_scope(server), _get_quantum_state_scope(server)]} 51 | return d 52 | 53 | 54 | def _get_classical_scope(server: DAPServer) -> dict[str, Any]: 55 | start_pos = 0 56 | end_pos = len(server.source_code) - 1 57 | start_line, start_col = server.code_pos_to_coordinates(start_pos) 58 | end_line, end_col = server.code_pos_to_coordinates(end_pos) 59 | return { 60 | "name": "Classical Registers", 61 | "presentationHint": "locals", 62 | "variablesReference": 1, # Classical Registers have reference 1 63 | "namedVariables": server.simulation_state.get_num_classical_variables(), 64 | "indexedVariables": 0, 65 | "expensive": False, 66 | "source": server.source_file, 67 | "line": start_line, 68 | "column": start_col, 69 | "endLine": end_line, 70 | "endColumn": end_col, 71 | } 72 | 73 | 74 | def _get_quantum_state_scope(server: DAPServer) -> dict[str, Any]: 75 | start_pos = 0 76 | end_pos = len(server.source_code) - 1 77 | start_line, start_col = server.code_pos_to_coordinates(start_pos) 78 | end_line, end_col = server.code_pos_to_coordinates(end_pos) 79 | return { 80 | "name": "Quantum State", 81 | "presentationHint": "registers", 82 | "variablesReference": 2, # Quantum state has reference 1 83 | "namedVariables": 2 ** server.simulation_state.get_num_qubits(), 84 | "indexedVariables": 0, 85 | "expensive": server.simulation_state.get_num_qubits() > 5, 86 | "source": server.source_file, 87 | "line": start_line, 88 | "column": start_col, 89 | "endLine": end_line, 90 | "endColumn": end_col, 91 | } 92 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/set_breakpoints_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'setBreakpoints' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class SetBreakpointsDAPMessage(DAPMessage): 22 | """Represents the 'setBreakpoints' DAP request.""" 23 | 24 | message_type_name: str = "setBreakpoints" 25 | 26 | breakpoints: list[tuple[int, int]] 27 | source: dict[str, Any] 28 | 29 | def __init__(self, message: dict[str, Any]) -> None: 30 | """Initializes the 'SetBreakpointsDAPMessage' instance. 31 | 32 | Args: 33 | message (dict[str, Any]): The object representing the 'setBreakpoints' request. 34 | """ 35 | self.breakpoints = [(int(x["line"]), int(x.get("column", -1))) for x in message["arguments"]["breakpoints"]] 36 | self.source = message["arguments"]["source"] 37 | super().__init__(message) 38 | 39 | def validate(self) -> None: 40 | """Validates the 'SetBreakpointsDAPMessage' instance.""" 41 | 42 | def handle(self, server: DAPServer) -> dict[str, Any]: 43 | """Performs the action requested by the 'setBreakpoints' DAP request. 44 | 45 | Args: 46 | server (DAPServer): The DAP server that received the request. 47 | 48 | Returns: 49 | dict[str, Any]: The response to the request. 50 | """ 51 | if self.source["name"] != server.source_file["name"] or self.source["path"] != server.source_file["path"]: 52 | return self.handle_wrong_file(server) 53 | 54 | d = super().handle(server) 55 | 56 | server.simulation_state.clear_breakpoints() 57 | bpts = [] 58 | for i, breakpoint_position in enumerate(self.breakpoints): 59 | position = server.code_coordinates_to_pos( 60 | breakpoint_position[0], 61 | breakpoint_position[1] if breakpoint_position[1] != -1 else 1 if server.columns_start_at_one else 0, 62 | ) 63 | try: 64 | breakpoint_instruction = server.simulation_state.set_breakpoint(position) 65 | start, end = server.simulation_state.get_instruction_position(breakpoint_instruction) 66 | start_line, start_col = server.code_pos_to_coordinates(start) 67 | end_line, end_col = server.code_pos_to_coordinates(end) 68 | bpts.append({ 69 | "id": i, 70 | "verified": True, 71 | "source": self.source, 72 | "line": start_line, 73 | "column": start_col, 74 | "endLine": end_line, 75 | "endColumn": end_col, 76 | }) 77 | except RuntimeError: 78 | bpts.append({"id": i, "verified": False, "message": "Breakpoint could not be set", "reason": "failed"}) 79 | d["body"] = {"breakpoints": bpts} 80 | return d 81 | 82 | def handle_wrong_file(self, server: DAPServer) -> dict[str, Any]: 83 | """Performs the action requested by the 'setBreakpoints' DAP request when the file is wrong. 84 | 85 | Args: 86 | server (DAPServer): The DAP server that received the request. 87 | 88 | Returns: 89 | dict[str, Any]: The response to the request. 90 | """ 91 | d = super().handle(server) 92 | bpts = [] 93 | for i, _breakpoint in enumerate(self.breakpoints): 94 | bpts.append({ 95 | "id": i, 96 | "verified": False, 97 | "message": "Breakpoints only supported in the main file", 98 | "reason": "failed", 99 | }) 100 | d["body"] = {"breakpoints": bpts} 101 | return d 102 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/set_exception_breakpoints_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'setExceptionBreakpoints' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class SetExceptionBreakpointsDAPMessage(DAPMessage): 22 | """Represents the 'setBreakpoints' DAP request.""" 23 | 24 | message_type_name: str = "setExceptionBreakpoints" 25 | 26 | filters: list[str] 27 | 28 | def __init__(self, message: dict[str, Any]) -> None: 29 | """Initializes the 'SetExceptionBreakpointsDAPMessage' instance. 30 | 31 | Args: 32 | message (dict[str, Any]): The object representing the 'setExceptionBreakpoints' request. 33 | """ 34 | self.filters = message["arguments"].get("filters", []) + [ 35 | x["filterId"] for x in message["arguments"].get("filterOptions", []) 36 | ] 37 | super().__init__(message) 38 | 39 | def validate(self) -> None: 40 | """Validates the 'SetExceptionBreakpointsDAPMessage' instance.""" 41 | 42 | def handle(self, server: DAPServer) -> dict[str, Any]: 43 | """Performs the action requested by the 'setExceptionBreakpoints' DAP request. 44 | 45 | Args: 46 | server (DAPServer): The DAP server that received the request. 47 | 48 | Returns: 49 | dict[str, Any]: The response to the request. 50 | """ 51 | server.exception_breakpoints = self.filters 52 | d = super().handle(server) 53 | d["body"] = {"breakpoints": [{"verified": True} for x in self.filters]} 54 | return d 55 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/stack_trace_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'stackTrace' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class StackTraceDAPMessage(DAPMessage): 22 | """Represents the 'stackTrace' DAP request.""" 23 | 24 | message_type_name: str = "stackTrace" 25 | 26 | def __init__(self, message: dict[str, Any]) -> None: 27 | """Initializes the 'StackTraceDAPMessage' instance. 28 | 29 | Args: 30 | message (dict[str, Any]): The object representing the 'stackTrace' request. 31 | """ 32 | super().__init__(message) 33 | 34 | def validate(self) -> None: 35 | """Validates the 'StackTraceDAPMessage' instance.""" 36 | 37 | def handle(self, server: DAPServer) -> dict[str, Any]: 38 | """Performs the action requested by the 'stackTrace' DAP request. 39 | 40 | Args: 41 | server (DAPServer): The DAP server that received the request. 42 | 43 | Returns: 44 | dict[str, Any]: The response to the request. 45 | """ 46 | d = super().handle(server) 47 | 48 | stack_frames = [] 49 | depth = server.simulation_state.get_stack_depth() 50 | stack_trace = server.simulation_state.get_stack_trace(depth) 51 | for i, frame in enumerate(stack_trace): 52 | (start, end) = server.simulation_state.get_instruction_position(frame) 53 | start_line, start_col = server.code_pos_to_coordinates(start) 54 | end_line, end_col = server.code_pos_to_coordinates(end) 55 | if i == len(stack_trace) - 1: 56 | name = "main" 57 | else: 58 | parent_instr = stack_trace[i + 1] 59 | (parent_start, parent_end) = server.simulation_state.get_instruction_position(parent_instr) 60 | name = server.source_code[parent_start:parent_end].strip().split(" ")[0].strip() 61 | stack_frames.append({ 62 | "id": depth - i, 63 | "name": name, 64 | "line": start_line, 65 | "endLine": end_line, 66 | "column": start_col, 67 | "endColumn": end_col, 68 | "source": server.source_file, 69 | }) 70 | 71 | d["body"] = {"stackFrames": stack_frames, "totalFrames": depth} 72 | return d 73 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/step_back_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'stepBack' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class StepBackDAPMessage(DAPMessage): 22 | """Represents the 'stepBack' DAP request.""" 23 | 24 | message_type_name: str = "stepBack" 25 | 26 | def __init__(self, message: dict[str, Any]) -> None: 27 | """Initializes the 'StepBackDAPMessage' instance. 28 | 29 | Args: 30 | message (dict[str, Any]): The object representing the 'stepBack' request. 31 | """ 32 | super().__init__(message) 33 | 34 | def validate(self) -> None: 35 | """Validates the 'StepBackDAPMessage' instance.""" 36 | 37 | def handle(self, server: DAPServer) -> dict[str, Any]: 38 | """Performs the action requested by the 'stepBack' DAP request. 39 | 40 | Args: 41 | server (DAPServer): The DAP server that received the request. 42 | 43 | Returns: 44 | dict[str, Any]: The response to the request. 45 | """ 46 | if server.simulation_state.can_step_backward(): 47 | server.simulation_state.step_over_backward() 48 | return super().handle(server) 49 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/step_in_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'stepIn' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class StepInDAPMessage(DAPMessage): 22 | """Represents the 'stepIn' DAP request.""" 23 | 24 | message_type_name: str = "stepIn" 25 | 26 | def __init__(self, message: dict[str, Any]) -> None: 27 | """Initializes the 'StepInDAPMessage' instance. 28 | 29 | Args: 30 | message (dict[str, Any]): The object representing the 'stepIn' request. 31 | """ 32 | super().__init__(message) 33 | 34 | def validate(self) -> None: 35 | """Validates the 'StepInDAPMessage' instance.""" 36 | 37 | def handle(self, server: DAPServer) -> dict[str, Any]: 38 | """Performs the action requested by the 'stepIn' DAP request. 39 | 40 | Args: 41 | server (DAPServer): The DAP server that received the request. 42 | 43 | Returns: 44 | dict[str, Any]: The response to the request. 45 | """ 46 | server.simulation_state.step_forward() 47 | return super().handle(server) 48 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/step_out_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'stepOut' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class StepOutDAPMessage(DAPMessage): 22 | """Represents the 'stepOut' DAP request.""" 23 | 24 | message_type_name: str = "stepOut" 25 | 26 | def __init__(self, message: dict[str, Any]) -> None: 27 | """Initializes the 'StepOutDAPMessage' instance. 28 | 29 | Args: 30 | message (dict[str, Any]): The object representing the 'stepOut' request. 31 | """ 32 | super().__init__(message) 33 | 34 | def validate(self) -> None: 35 | """Validates the 'StepOutDAPMessage' instance.""" 36 | 37 | def handle(self, server: DAPServer) -> dict[str, Any]: 38 | """Performs the action requested by the 'stepOut' DAP request. 39 | 40 | Args: 41 | server (DAPServer): The DAP server that received the request. 42 | 43 | Returns: 44 | dict[str, Any]: _description_ 45 | """ 46 | server.simulation_state.step_out_forward() 47 | return super().handle(server) 48 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/stopped_dap_event.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'stopped' DAP event.""" 10 | 11 | from __future__ import annotations 12 | 13 | import enum 14 | 15 | from .dap_event import DAPEvent 16 | 17 | 18 | class StopReason(enum.Enum): 19 | """Represents the reason for stopping.""" 20 | 21 | STEP = "step" 22 | BREAKPOINT = "breakpoint" 23 | EXCEPTION = "exception" 24 | PAUSE = "pause" 25 | ENTRY = "entry" 26 | GOTO = "goto" 27 | BREAKPOINT_FUNCTION = "function breakpoint" 28 | BREAKPOINT_DATA = "data breakpoint" 29 | BREAKPOINT_INSTRUCTION = "instruction breakpoint" 30 | 31 | 32 | class StoppedDAPEvent(DAPEvent): 33 | """Represents the 'stopped' DAP event.""" 34 | 35 | event_name = "stopped" 36 | 37 | reason: StopReason 38 | description: str 39 | 40 | def __init__(self, reason: StopReason, description: str) -> None: 41 | """Create a new 'stopped' DAP event message. 42 | 43 | Args: 44 | reason (StopReason): The reason for stopping. 45 | description (str): The description of the stop. 46 | """ 47 | super().__init__() 48 | self.reason = reason 49 | self.description = description 50 | 51 | def validate(self) -> None: 52 | """Validate the 'stopped' DAP event message after creation.""" 53 | 54 | def encode(self) -> dict[str, str]: 55 | """Encode the 'stopped' DAP event message as a dictionary. 56 | 57 | Returns: 58 | dict[str, str]: The encoded 'stopped' DAP event message. 59 | """ 60 | d = super().encode() 61 | d["body"] = {} 62 | d["body"]["reason"] = self.reason.value 63 | d["body"]["description"] = self.description 64 | d["body"]["threadId"] = 1 65 | d["body"]["text"] = self.description 66 | d["body"]["allThreadsStopped"] = True 67 | return d 68 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/terminate_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'terminate' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | import mqt.debugger 16 | 17 | from .dap_message import DAPMessage 18 | 19 | if TYPE_CHECKING: 20 | from .. import DAPServer 21 | 22 | 23 | class TerminateDAPMessage(DAPMessage): 24 | """Represents the 'terminate' DAP request.""" 25 | 26 | message_type_name: str = "terminate" 27 | 28 | def __init__(self, message: dict[str, Any]) -> None: 29 | """Initializes the 'TerminateDAPMessage' instance. 30 | 31 | Args: 32 | message (dict[str, Any]): The object representing the 'terminate' request. 33 | """ 34 | super().__init__(message) 35 | 36 | def validate(self) -> None: 37 | """Validates the 'TerminateDAPMessage' instance.""" 38 | 39 | def handle(self, server: DAPServer) -> dict[str, Any]: 40 | """Performs the action requested by the 'terminate' DAP request. 41 | 42 | Args: 43 | server (DAPServer): The DAP server that received the request. 44 | 45 | Returns: 46 | dict[str, Any]: The response to the request. 47 | """ 48 | mqt.debugger.destroy_ddsim_simulation_state(server.simulation_state) 49 | return super().handle(server) 50 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/terminated_dap_event.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'terminated' DAP event.""" 10 | 11 | from __future__ import annotations 12 | 13 | from .dap_event import DAPEvent 14 | 15 | 16 | class TerminatedDAPEvent(DAPEvent): 17 | """Represents the 'terminated' DAP event.""" 18 | 19 | event_name = "terminated" 20 | 21 | def __init__(self) -> None: 22 | """Initializes the 'TerminatedDAPEvent' instance.""" 23 | super().__init__() 24 | 25 | def validate(self) -> None: 26 | """Validates the 'TerminatedDAPEvent' instance.""" 27 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/threads_dap_message.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Represents the 'threads' DAP request.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import TYPE_CHECKING, Any 14 | 15 | from .dap_message import DAPMessage 16 | 17 | if TYPE_CHECKING: 18 | from .. import DAPServer 19 | 20 | 21 | class ThreadsDAPMessage(DAPMessage): 22 | """Represents the 'threads' DAP request.""" 23 | 24 | message_type_name: str = "threads" 25 | 26 | def __init__(self, message: dict[str, Any]) -> None: 27 | """Initializes the 'ThreadsDAPMessage' instance. 28 | 29 | Args: 30 | message (dict[str, Any]): The object representing the 'threads' request. 31 | """ 32 | super().__init__(message) 33 | 34 | def validate(self) -> None: 35 | """Validates the 'ThreadsDAPMessage' instance.""" 36 | 37 | def handle(self, server: DAPServer) -> dict[str, Any]: 38 | """Performs the action requested by the 'threads' DAP request. 39 | 40 | Args: 41 | server (DAPServer): The DAP server that received the request. 42 | 43 | Returns: 44 | dict[str, Any]: The response to the request. 45 | """ 46 | d = super().handle(server) 47 | d["body"] = {"threads": [{"id": 1, "name": "Main Thread"}]} 48 | return d 49 | -------------------------------------------------------------------------------- /src/mqt/debugger/dap/messages/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Provides utility functions for the DAP messages.""" 10 | 11 | from __future__ import annotations 12 | 13 | from typing import Any 14 | 15 | 16 | def get_default_capabilities() -> dict[str, Any]: 17 | """Returns the default capabilities of the DAP server. 18 | 19 | Returns: 20 | dict[str, Any]: The default capabilities of the DAP server. 21 | """ 22 | return { 23 | "supportsConfigurationDoneRequest": True, 24 | "supportsFunctionBreakpoints": False, 25 | "supportsConditionalBreakpoints": False, 26 | "supportsHitConditionalBreakpoints": False, 27 | "supportsEvaluateForHovers": False, 28 | "supportsExceptionInfoRequest": True, 29 | "exceptionBreakpointFilters": [], 30 | "supportsStepBack": True, 31 | "supportsSetVariable": False, 32 | "supportsRestartFrame": True, 33 | "supportsTerminateRequest": True, 34 | "supportsRestartRequest": True, 35 | "supportsVariableType": True, 36 | "supportsDelayedStackTraceLoading": False, 37 | "supportsVariablePaging": True, 38 | } 39 | -------------------------------------------------------------------------------- /src/mqt/debugger/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munich-quantum-toolkit/debugger/1bd3311a4e3d163f788dcb0071903c9e35c03333/src/mqt/debugger/py.typed -------------------------------------------------------------------------------- /src/python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM Copyright (c) 2025 Munich Quantum 2 | # Software Company GmbH All rights reserved. 3 | # 4 | # SPDX-License-Identifier: MIT 5 | # 6 | # Licensed under the MIT License 7 | 8 | pybind11_add_module( 9 | pydebugger 10 | # Prefer thin LTO if available 11 | THIN_LTO 12 | # Optimize the bindings for size 13 | OPT_SIZE 14 | # Source code goes here 15 | bindings.cpp 16 | InterfaceBindings.cpp 17 | dd/DDSimDebugBindings.cpp) 18 | target_link_libraries(pydebugger PRIVATE MQT::Debugger pybind11_json MQT::ProjectOptions 19 | MQT::ProjectWarnings) 20 | message(STATUS "CMAKE_INSTALL_PREFIX is set to ${CMAKE_INSTALL_PREFIX}") 21 | # Install directive for scikit-build-core 22 | install( 23 | TARGETS pydebugger 24 | DESTINATION . 25 | COMPONENT mqt-debugger_Python) 26 | -------------------------------------------------------------------------------- /src/python/bindings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file bindings.cpp 13 | * @brief Python bindings for the debugger module. 14 | * 15 | * Central file for defining the Python bindings for the framework. 16 | */ 17 | 18 | #include "python/InterfaceBindings.hpp" 19 | #include "python/dd/DDSimDebugBindings.hpp" 20 | 21 | #include <pybind11/pybind11.h> 22 | 23 | namespace mqt::debugger { 24 | 25 | PYBIND11_MODULE(pydebugger, m) { 26 | bindDiagnostics(m); 27 | bindFramework(m); 28 | bindBackend(m); 29 | } 30 | 31 | } // namespace mqt::debugger 32 | -------------------------------------------------------------------------------- /src/python/dd/DDSimDebugBindings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file DDSimDebugBindings.cpp 13 | * @brief Implements Python bindings for the DD Debugger. 14 | * 15 | * This includes bindings for creating and destroying DD SimulationStates and 16 | * Diagnostics states. 17 | */ 18 | 19 | #include "python/dd/DDSimDebugBindings.hpp" 20 | 21 | #include "backend/dd/DDSimDebug.hpp" 22 | #include "backend/debug.h" 23 | #include "pybind11/pybind11.h" 24 | 25 | namespace mqt::debugger { 26 | 27 | void bindBackend(pybind11::module& m) { 28 | 29 | m.def( 30 | "create_ddsim_simulation_state", 31 | []() { 32 | // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) 33 | auto* state = new DDSimulationState(); 34 | createDDSimulationState(state); 35 | return &state->interface; 36 | }, 37 | R"(Creates a new `SimulationState` instance using the DD backend for simulation and the OpenQASM language as input format. 38 | 39 | Returns: 40 | SimulationState: The created simulation state.)"); 41 | 42 | m.def( 43 | "destroy_ddsim_simulation_state", 44 | [](SimulationState* state) { 45 | // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast) 46 | destroyDDSimulationState(reinterpret_cast<DDSimulationState*>(state)); 47 | // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) 48 | }, 49 | R"(Delete a given DD-based `SimulationState` instance and free up resources. 50 | 51 | Args: 52 | state (SimulationState): The simulation state to delete.)"); 53 | } 54 | 55 | } // namespace mqt::debugger 56 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM Copyright (c) 2025 Munich Quantum 2 | # Software Company GmbH All rights reserved. 3 | # 4 | # SPDX-License-Identifier: MIT 5 | # 6 | # Licensed under the MIT License 7 | 8 | package_add_test( 9 | mqt_debugger_test 10 | MQT::Debugger 11 | utils_test.cpp 12 | test_compilation_statistical_slices.cpp 13 | test_compilation_projective_measurements.cpp 14 | test_utility.cpp 15 | test_simulation.cpp 16 | test_data_retrieval.cpp 17 | test_diagnostics.cpp 18 | test_custom_code.cpp 19 | test_parsing.cpp 20 | test_assertion_movement.cpp 21 | test_assertion_creation.cpp) 22 | 23 | # set include directories 24 | target_include_directories(mqt_debugger_test PUBLIC ${PROJECT_SOURCE_DIR}/test/utils) 25 | -------------------------------------------------------------------------------- /test/circuits/classical-storage.qasm: -------------------------------------------------------------------------------- 1 | qreg q[3]; // 0 2 | qreg p[1]; // 1 3 | creg c[3]; // 2 4 | creg hello[1]; // 3 5 | 6 | x q[0]; // 4 7 | x q[1]; // 5 8 | 9 | measure q -> c; // 6 10 | 11 | x q[2]; // 7 12 | measure q[2] -> c[2]; // 8 13 | x q[2]; // 9 14 | 15 | h q[0]; // 10 16 | cx q[0], p[0]; // 11 17 | 18 | measure p[0] -> hello[0]; // 12 19 | 20 | y q[2]; // 13 21 | y q[1]; // 14 22 | 23 | measure q[0] -> c[0]; // 15 24 | 25 | barrier; // 16 26 | -------------------------------------------------------------------------------- /test/circuits/complex-jumps.qasm: -------------------------------------------------------------------------------- 1 | qreg q[3]; // 0 2 | 3 | gate my_cx q0, q1 { // 1 4 | cx q0, q1; // 2 5 | } // 3 6 | 7 | gate make_ghz q0, q1, q2 { // 4 8 | h q0; // 5 9 | entangle q0, q1; // 6 10 | entangle q0, q2; // 7 11 | } // 8 12 | 13 | gate entangle q1, q2 { // 9 14 | my_cx q1, q2; // 10 15 | } // 11 16 | 17 | make_ghz q[0], q[1], q[2]; // 12 18 | 19 | entangle q[2], q[0]; // 13 20 | entangle q[1], q[0]; // 14 21 | -------------------------------------------------------------------------------- /test/circuits/diagnose-with-jumps.qasm: -------------------------------------------------------------------------------- 1 | gate level_one q0, q1, q2 { // 0 2 | level_two q0, q1; // 1 3 | level_two q1, q2; // 2 4 | } // 3 5 | 6 | gate level_two q0, q1 { // 4 7 | cx q0, q1; // 5 8 | level_three_a q0; // 6 9 | 10 | level_three_b q1; // 7 11 | } // 8 12 | 13 | gate level_three_a q { // 9 14 | x q; // 10 15 | } // 11 16 | 17 | gate level_three_b q { // 12 18 | z q; // 13 19 | } // 14 20 | 21 | qreg q[4]; // 15 22 | 23 | x q[0]; // 16 24 | level_one q[2], q[1], q[0]; // 17 25 | 26 | assert-eq q[0], q[1], q[2], q[3] { qreg x[4]; } // 18 27 | -------------------------------------------------------------------------------- /test/circuits/failing-assertions-missing-interaction.qasm: -------------------------------------------------------------------------------- 1 | qreg q[5]; // 0 2 | 3 | h q[0]; // 1 4 | h q[3]; // 2 5 | 6 | cx q[0], q[1]; // 3 7 | cx q[1], q[2]; // 4 8 | cx q[3], q[4]; // 5 9 | 10 | assert-ent q[0], q[2]; // 6 11 | assert-ent q[0], q[3]; // 7 (fails) 12 | -------------------------------------------------------------------------------- /test/circuits/failing-assertions-multiple-causes.qasm: -------------------------------------------------------------------------------- 1 | qreg q[5]; // 0 2 | 3 | cx q[0], q[1]; // 1 4 | cx q[0], q[2]; // 2 5 | 6 | assert-ent q[0], q[1], q[2], q[3], q[4]; // 3 (fails) 7 | -------------------------------------------------------------------------------- /test/circuits/failing-assertions-multiple-missing-interaction.qasm: -------------------------------------------------------------------------------- 1 | qreg q[5]; // 0 2 | 3 | h q[0]; // 1 4 | 5 | assert-ent q[0], q[1], q[2], q[3], q[4]; // 2 (fails) 6 | -------------------------------------------------------------------------------- /test/circuits/failing-assertions-multiple-zero-controls.qasm: -------------------------------------------------------------------------------- 1 | qreg q[4]; // 0 2 | 3 | cx q[0], q[1]; // 1 4 | cx q[0], q[2]; // 2 5 | cx q[0], q[3]; // 3 6 | 7 | assert-ent q[0], q[1], q[2], q[3]; // 4 (fails) 8 | -------------------------------------------------------------------------------- /test/circuits/failing-assertions.qasm: -------------------------------------------------------------------------------- 1 | qreg q[4]; // 0 2 | 3 | h q[0]; // 1 4 | 5 | cx q[0], q[1]; // 2 6 | cx q[1], q[2]; // 3 7 | // here the control and target were swapped 8 | cx q[3], q[0]; // 4 9 | 10 | assert-ent q[0], q[2]; // 5 11 | assert-ent q[2], q[3]; // 6 (fails) 12 | 13 | z q[0]; // 7 14 | 15 | h q[0]; // 8 16 | 17 | assert-sup q[3]; // 9 (fails) 18 | assert-sup q[0], q[1], q[2], q[3]; // 10 19 | -------------------------------------------------------------------------------- /test/circuits/runtime-interaction.qasm: -------------------------------------------------------------------------------- 1 | gate gate1 q0, q1 { // 0 2 | x q0; // 1 3 | x q1; // 2 4 | assert-ent q0, q1; // 3 5 | } // 4 6 | 7 | qreg q[2]; // 5 8 | 9 | cx q[0], q[1]; // 6 10 | gate1 q[0], q[1]; // 7 11 | -------------------------------------------------------------------------------- /test/circuits/zero-controls-with-jumps.qasm: -------------------------------------------------------------------------------- 1 | qreg q[3]; 2 | 3 | x q[0]; 4 | 5 | gate test1 q0, q1 { 6 | cx q0, q1; 7 | } 8 | 9 | gate test2 q0, q1 { 10 | cx q0, q1; 11 | } 12 | 13 | test1 q[2], q[1]; 14 | test1 q[1], q[2]; 15 | 16 | test2 q[0], q[2]; 17 | test2 q[1], q[2]; 18 | 19 | cx q[1], q[2]; 20 | -------------------------------------------------------------------------------- /test/python/resources/bindings/classical.qasm: -------------------------------------------------------------------------------- 1 | qreg q[3]; 2 | creg c[3]; 3 | qreg q2[1]; 4 | 5 | h q[0]; 6 | cx q[0], q[1]; 7 | cx q[0], q[2]; 8 | 9 | assert-ent q[0], q[1]; 10 | assert-ent q[1], q[2]; 11 | assert-ent q[0], q[2]; 12 | 13 | measure q[0] -> c[0]; 14 | measure q[1] -> c[1]; 15 | measure q[2] -> c[2]; 16 | -------------------------------------------------------------------------------- /test/python/resources/bindings/ghz-incorrect.qasm: -------------------------------------------------------------------------------- 1 | qreg q[3]; 2 | 3 | h q[0]; 4 | cx q[0], q[1]; 5 | cx q[2], q[0]; 6 | 7 | assert-ent q[0], q[1]; 8 | assert-ent q[1], q[2]; 9 | assert-ent q[0], q[2]; 10 | 11 | assert-sup q[0], q[1], q[2]; 12 | -------------------------------------------------------------------------------- /test/python/resources/bindings/jumps.qasm: -------------------------------------------------------------------------------- 1 | qreg q[3]; 2 | 3 | gate my_cx q0, q1 { 4 | cx q0, q1; 5 | } 6 | 7 | gate my_h q0 { 8 | h q0; 9 | } 10 | 11 | gate superposition q { 12 | my_h q; 13 | } 14 | 15 | gate create_ghz q0, q1, q2 { 16 | superposition q0; 17 | assert-sup q0; 18 | my_cx q0, q1; 19 | my_cx q1, q2; 20 | assert-sup q0; assert-sup q1; assert-sup q2; 21 | assert-ent q0, q1, q2; 22 | } 23 | 24 | create_ghz q[0], q[1], q[2]; 25 | 26 | assert-eq q[0], q[1], q[2] { 27 | qreg q[3]; 28 | h q[0]; 29 | cx q[0], q[1]; 30 | cx q[0], q[2]; 31 | } 32 | 33 | assert-eq 0.9, q[0], q[1], q[2] { 0.7, 0, 0, 0, 0, 0, 0, 0.7 } 34 | -------------------------------------------------------------------------------- /test/python/resources/compilation/calibration.json: -------------------------------------------------------------------------------- 1 | { 2 | "error_rate_1q": 0.001, 3 | "error_rate_2q": 0.01, 4 | "error_rate_measurement": 0.01, 5 | "specific_gate_errors": { 6 | "cx": 0.001 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/python/resources/compilation/original.qasm: -------------------------------------------------------------------------------- 1 | OPENQASM 2.0; 2 | include "qelib1.inc"; 3 | 4 | qreg q[2]; 5 | h q[0]; 6 | cx q[0], q[1]; 7 | assert-eq 0.9, q[0], q[1] { 0.707, 0, 0, 0.707 } 8 | -------------------------------------------------------------------------------- /test/python/resources/compilation/test-shots.qasm: -------------------------------------------------------------------------------- 1 | // ASSERT: (test_q0,test_q1) {0.499849,0,0,0.499849} 0.9 2 | // ASSERT: (test_q0_,) {superposition} 3 | // ASSERT: (test_q0__,test_q1_) {zero} 4 | OPENQASM 2.0; 5 | include "qelib1.inc"; 6 | 7 | qreg q[2]; 8 | h q[0]; 9 | cx q[0], q[1]; 10 | barrier q[0]; 11 | creg test_q0[1]; 12 | creg test_q1[1]; 13 | measure q[0] -> test_q0[0]; 14 | measure q[1] -> test_q1[0]; 15 | // remainder of program is not important for tests 16 | -------------------------------------------------------------------------------- /test/python/resources/compilation/test_program_compiled/slice_1.qasm: -------------------------------------------------------------------------------- 1 | // ASSERT: (test_q0,test_q1) {0.499849,0,0,0.499849} 0.9 2 | OPENQASM 2.0; 3 | include "qelib1.inc"; 4 | 5 | qreg q[2]; 6 | h q[0]; 7 | cx q[0], q[1]; 8 | creg test_q0[1]; 9 | creg test_q1[1]; 10 | measure q[0] -> test_q0[0]; 11 | measure q[1] -> test_q1[0]; 12 | -------------------------------------------------------------------------------- /test/python/resources/compilation/test_program_compiled/slice_2.qasm: -------------------------------------------------------------------------------- 1 | // ASSERT: (test_q0) {superposition} 2 | OPENQASM 2.0; 3 | include "qelib1.inc"; 4 | 5 | creg test_q0[1]; 6 | qreg q[2]; 7 | h q[0]; 8 | cx q[0], q[1]; 9 | measure q[0] -> test_q0[0]; 10 | -------------------------------------------------------------------------------- /test/python/resources/compilation/test_program_compiled/slice_3.qasm: -------------------------------------------------------------------------------- 1 | // ASSERT: (test_q0,test_q1) {zero} 2 | OPENQASM 2.0; 3 | include "qelib1.inc"; 4 | 5 | qreg q[2]; 6 | h q[0]; 7 | cx q[0], q[1]; 8 | creg test_q0[1]; 9 | creg test_q1[1]; 10 | cx q[0], q[1]; 11 | h q[0]; 12 | measure q[0] -> test_q0[0]; 13 | measure q[1] -> test_q1[0]; 14 | h q[0]; 15 | cx q[0], q[1]; 16 | -------------------------------------------------------------------------------- /test/python/resources/diagnosis/control-always-zero.qasm: -------------------------------------------------------------------------------- 1 | qreg q[3]; 2 | 3 | x q[0]; 4 | 5 | gate test1 q0, q1 { 6 | cx q0, q1; 7 | } 8 | 9 | gate test2 q0, q1 { 10 | cx q0, q1; 11 | } 12 | 13 | test1 q[2], q[1]; 14 | test1 q[1], q[2]; 15 | 16 | test2 q[0], q[2]; 17 | test2 q[1], q[2]; 18 | 19 | cx q[1], q[2]; 20 | 21 | assert-eq q[0], q[1], q[2] { 0, 0, 0, 0, 0, 0, 0, 0 } 22 | -------------------------------------------------------------------------------- /test/python/resources/diagnosis/data-dependencies-with-callers.qasm: -------------------------------------------------------------------------------- 1 | qreg q[3]; // 0 2 | 3 | gate test q { // 1 4 | x q; // 2 5 | } // 3 6 | 7 | x q[0]; // 4 8 | x q[1]; // 5 9 | 10 | test q[0]; // 6 11 | 12 | x q[0]; // 7 13 | x q[2]; // 8 14 | 15 | test q[2]; // 9 16 | -------------------------------------------------------------------------------- /test/python/resources/diagnosis/dependencies-with-jumps.qasm: -------------------------------------------------------------------------------- 1 | qreg q[3]; 2 | 3 | gate entangle q0, q1, q2 { 4 | cx q0, q1; 5 | cx q0, q2; 6 | barrier q2; 7 | } 8 | 9 | h q[0]; 10 | 11 | entangle q[0], q[1], q[2]; 12 | 13 | h q[2]; 14 | 15 | barrier q[2]; 16 | -------------------------------------------------------------------------------- /test/python/resources/diagnosis/dependencies.qasm: -------------------------------------------------------------------------------- 1 | qreg q[3]; 2 | 3 | h q[2]; 4 | 5 | cx q[0], q[2]; 6 | cx q[0], q[1]; 7 | 8 | h q[0]; 9 | 10 | cx q[0], q[2]; 11 | 12 | barrier q[1]; 13 | barrier q[2]; 14 | -------------------------------------------------------------------------------- /test/python/resources/diagnosis/missing-interaction.qasm: -------------------------------------------------------------------------------- 1 | qreg q[4]; 2 | 3 | cx q[0], q[1]; 4 | cx q[1], q[2]; 5 | assert-ent q[0], q[2]; // fails without missing interaction 6 | assert-ent q[0], q[3]; // fails with missing interaction 7 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/bell.out: -------------------------------------------------------------------------------- 1 | 0.707,0,0,0.707 2 | --- 3 | 4 | --- 5 | 0 6 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/bell.qasm: -------------------------------------------------------------------------------- 1 | qreg q[2]; 2 | 3 | h q[0]; 4 | cx q[0], q[1]; 5 | 6 | assert-ent q[0], q[1]; 7 | assert-sup q[0]; 8 | assert-sup q[1]; 9 | assert-sup q[0], q[1]; 10 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/bv.out: -------------------------------------------------------------------------------- 1 | 0, 0, 0, 0, 0, 0.707, 0, 0, 0, 0, 0, 0, 0, -0.707, 0, 0 2 | --- 3 | --- 4 | 0 5 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/bv.qasm: -------------------------------------------------------------------------------- 1 | qreg q[3]; 2 | qreg ancilla[1]; 3 | 4 | gate oracle q0, q1, q2, anc { 5 | cx q0, anc; 6 | cx q2, anc; 7 | } 8 | 9 | h q; 10 | x ancilla; 11 | h ancilla; 12 | 13 | oracle q[0], q[1], q[2], ancilla; 14 | 15 | h q; 16 | 17 | assert-eq 0.9, q[0], q[1], q[2], ancilla { 0, 0, 0, 0, 0, 0.707, 0, 0, 0, 0, 0, 0, 0, -0.707, 0, 0 } 18 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/dj_4.out: -------------------------------------------------------------------------------- 1 | 0,0,0,0,0,0,0,0.707,0,0,0,0,0,0,0,-0.707 2 | --- 3 | c[0]=1,c[1]=1,c[2]=1 4 | --- 5 | 0 6 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/dj_4.qasm: -------------------------------------------------------------------------------- 1 | qreg q[4]; 2 | creg c[3]; 3 | u2(0,0) q[0]; 4 | u2(0,0) q[1]; 5 | h q[2]; 6 | u2(-pi,-pi) q[3]; 7 | cx q[0],q[3]; 8 | u2(-pi,-pi) q[0]; 9 | cx q[1],q[3]; 10 | u2(-pi,-pi) q[1]; 11 | cx q[2],q[3]; 12 | h q[2]; 13 | barrier q[0],q[1],q[2],q[3]; 14 | measure q[0] -> c[0]; 15 | measure q[1] -> c[1]; 16 | measure q[2] -> c[2]; 17 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/fail_eq.out: -------------------------------------------------------------------------------- 1 | 0, 0.707, 0, 0.707 2 | --- 3 | 4 | --- 5 | 7 6 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/fail_eq.qasm: -------------------------------------------------------------------------------- 1 | qreg q[2]; 2 | 3 | x q[0]; 4 | h q[1]; 5 | 6 | assert-eq q[0], q[1] { 0, 0.707, 0, 0.707} // should fail 7 | assert-eq 1.0, q[0], q[1] { 0, 0.707, 0, 0.707} // should fail 8 | assert-eq 0.9, q[0], q[1] { 0, 0.707, 0, 0.707} // should pass 9 | 10 | assert-eq q[1], q[0] { 0, 0.707, 0, 0.707} // should fail 11 | assert-eq 1.0, q[1], q[0] { 0, 0.707, 0, 0.707} // should fail 12 | assert-eq 0.9, q[1], q[0] { 0, 0.707, 0, 0.707} // should fail 13 | 14 | assert-eq q[1], q[0] { 0, 0, 0.707, 0.707} // should fail 15 | assert-eq 1.0, q[1], q[0] { 0, 0, 0.707, 0.707} // should fail 16 | assert-eq 0.9, q[1], q[0] { 0, 0, 0.707, 0.707} // should pass 17 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/fail_ghz.out: -------------------------------------------------------------------------------- 1 | 0.707,0,0,0.707,0,0,0,0 2 | --- 3 | 4 | --- 5 | 4 6 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/fail_ghz.qasm: -------------------------------------------------------------------------------- 1 | qreg q[3]; 2 | 3 | h q[0]; 4 | cx q[0], q[1]; 5 | cx q[2], q[0]; 6 | 7 | assert-ent q[0], q[1], q[2]; 8 | assert-ent q[0], q[1]; 9 | assert-ent q[0], q[2]; 10 | assert-ent q[1], q[2]; 11 | assert-sup q[0]; 12 | assert-sup q[1]; 13 | assert-sup q[2]; 14 | assert-sup q[0], q[1]; 15 | assert-sup q[0], q[2]; 16 | assert-sup q[1], q[2]; 17 | assert-sup q[0], q[1], q[2]; 18 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/ghz_3.out: -------------------------------------------------------------------------------- 1 | 0.707,0,0,0,0,0,0,0.707 2 | --- 3 | 4 | --- 5 | 0 6 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/ghz_3.qasm: -------------------------------------------------------------------------------- 1 | qreg q[3]; 2 | 3 | h q[0]; 4 | cx q[0], q[1]; 5 | cx q[0], q[2]; 6 | 7 | assert-ent q[0], q[1], q[2]; 8 | assert-sup q[0]; 9 | assert-sup q[1]; 10 | assert-sup q[0], q[1]; 11 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/ghz_5.out: -------------------------------------------------------------------------------- 1 | 0.707,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.707 2 | --- 3 | 4 | --- 5 | 0 6 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/ghz_5.qasm: -------------------------------------------------------------------------------- 1 | qreg q[5]; 2 | 3 | h q[0]; 4 | cx q[0], q[1]; 5 | cx q[1], q[2]; 6 | cx q[2], q[3]; 7 | cx q[3], q[4]; 8 | 9 | assert-ent q[0], q[1], q[2]; 10 | assert-ent q[0], q[1], q[2], q[3], q[4]; 11 | assert-sup q[0]; 12 | assert-sup q[1]; 13 | assert-sup q[2]; 14 | assert-sup q[3]; 15 | assert-sup q[4]; 16 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/grover_3.out: -------------------------------------------------------------------------------- 1 | 0,0,0,0,0,0,0,-1 2 | --- 3 | c[0] = 1,c[1] = 1 4 | --- 5 | 0 6 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/grover_3.qasm: -------------------------------------------------------------------------------- 1 | qreg q[2]; 2 | qreg flag[1]; 3 | creg c[2]; 4 | 5 | // initialization 6 | h q; 7 | x flag; 8 | barrier q; 9 | 10 | // oracle: mark target state |11> 11 | h flag; 12 | ccx q[0], q[1], flag; 13 | h flag; 14 | barrier q; 15 | 16 | // diffusion 17 | h q; 18 | x q; 19 | h q[1]; 20 | cx q[0], q[1]; 21 | h q[1]; 22 | x q; 23 | h q; 24 | 25 | measure q -> c; 26 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/qpe.out: -------------------------------------------------------------------------------- 1 | 0,0,1,0 2 | --- 3 | c[0] = 1,c[1] = 1,c[2]=0,c[3]=0 4 | --- 5 | 0 6 | -------------------------------------------------------------------------------- /test/python/resources/end-to-end/qpe.qasm: -------------------------------------------------------------------------------- 1 | // iteratively estimating the phase of U=p(3pi/8) with 4-bit precision 2 | // we seek theta=3/16=0.0011_2, which is exactly representable using 4 bits 3 | // thus we expect to measure 0.c_3 c_2 c_1 c_0 = 0011 with certainty 4 | 5 | // counting register 6 | qreg q[1]; 7 | 8 | // eigenstate register 9 | qreg psi[1]; 10 | 11 | // classical registers 12 | creg c[4]; 13 | 14 | // initialize eigenstate psi = |1> 15 | x psi; 16 | barrier psi; 17 | 18 | // start by computing LSB c_0 19 | h q; 20 | cp(8 * (3*pi/8)) psi, q; 21 | h q; 22 | measure q[0] -> c[0]; 23 | 24 | // reset counting qubit and compute next bit c_1 25 | reset q; 26 | h q; 27 | cp(4 * (3*pi/8)) psi, q; 28 | if (c == 1) p(-pi/2) q; 29 | h q; 30 | measure q[0] -> c[1]; 31 | 32 | // reset counting qubit and compute next bit c_2 33 | reset q; 34 | h q; 35 | cp(2 * (3*pi/8)) psi, q; 36 | if (c == 1) p(1 * -pi/4) q; 37 | if (c == 2) p(2 * -pi/4) q; 38 | if (c == 3) p(3 * -pi/4) q; 39 | h q; 40 | measure q[0] -> c[2]; 41 | 42 | // reset counting qubit and compute MSB c_3 43 | reset q; 44 | h q; 45 | cp(1 * (3*pi/8)) psi, q; 46 | if (c == 1) p(1 * -pi/8) q; 47 | if (c == 2) p(2 * -pi/8) q; 48 | if (c == 3) p(3 * -pi/8) q; 49 | if (c == 4) p(4 * -pi/8) q; 50 | if (c == 5) p(5 * -pi/8) q; 51 | if (c == 6) p(6 * -pi/8) q; 52 | if (c == 7) p(7 * -pi/8) q; 53 | h q; 54 | measure q[0] -> c[3]; 55 | -------------------------------------------------------------------------------- /test/python/test_diagnosis.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 2 | # Copyright (c) 2025 Munich Quantum Software Company GmbH 3 | # All rights reserved. 4 | # 5 | # SPDX-License-Identifier: MIT 6 | # 7 | # Licensed under the MIT License 8 | 9 | """Tests diagnosis methods of the debugger.""" 10 | 11 | from __future__ import annotations 12 | 13 | import locale 14 | from pathlib import Path 15 | 16 | from mqt.debugger import ( 17 | ErrorCauseType, 18 | SimulationState, 19 | create_ddsim_simulation_state, 20 | destroy_ddsim_simulation_state, 21 | ) 22 | 23 | 24 | def load_instance(name: str) -> SimulationState: 25 | """Loads a quantum simulation and debugging instance from a file. 26 | 27 | Args: 28 | name (str): The name of the instance to load. 29 | 30 | Returns: 31 | SimulationState: The generated simulation state. 32 | """ 33 | state = create_ddsim_simulation_state() 34 | with Path(f"test/python/resources/diagnosis/{name}.qasm").open(encoding=locale.getpreferredencoding(False)) as f: 35 | state.load_code(f.read()) 36 | return state 37 | 38 | 39 | def test_data_dependencies() -> None: 40 | """Test the data dependency analysis.""" 41 | s = load_instance("dependencies") 42 | dependencies = s.get_diagnostics().get_data_dependencies(6) 43 | assert dependencies == [1, 2, 3, 6] 44 | dependencies = s.get_diagnostics().get_data_dependencies(7) 45 | assert dependencies == [1, 2, 3, 4, 5, 7] 46 | destroy_ddsim_simulation_state(s) 47 | 48 | 49 | def test_data_dependencies_jumps() -> None: 50 | """Test the data dependency analysis in code with jumps.""" 51 | s = load_instance("dependencies-with-jumps") 52 | dependencies = s.get_diagnostics().get_data_dependencies(4) 53 | assert dependencies == [2, 3, 4] 54 | dependencies = s.get_diagnostics().get_data_dependencies(9) 55 | assert dependencies == [2, 3, 4, 6, 7, 8, 9] 56 | destroy_ddsim_simulation_state(s) 57 | 58 | 59 | def test_control_always_zero() -> None: 60 | """Test the control-always-zero error diagnosis.""" 61 | s = load_instance("control-always-zero") 62 | s.run_simulation() 63 | causes = s.get_diagnostics().potential_error_causes() 64 | 65 | assert len(causes) == 2 66 | 67 | assert causes[0].type == ErrorCauseType.ControlAlwaysZero 68 | assert causes[0].instruction == 3 69 | 70 | assert causes[1].type == ErrorCauseType.ControlAlwaysZero 71 | assert causes[1].instruction == 12 72 | 73 | 74 | def test_missing_interaction() -> None: 75 | """Test the missing-interaction error diagnosis.""" 76 | s = load_instance("missing-interaction") 77 | s.run_simulation() 78 | causes = [x for x in s.get_diagnostics().potential_error_causes() if x.type == ErrorCauseType.MissingInteraction] 79 | assert len(causes) == 0 80 | s.run_simulation() 81 | causes = [x for x in s.get_diagnostics().potential_error_causes() if x.type == ErrorCauseType.MissingInteraction] 82 | assert len(causes) == 1 83 | assert causes[0].instruction == 4 84 | 85 | 86 | def test_zero_control_listing() -> None: 87 | """Test the zero-control list.""" 88 | s = load_instance("control-always-zero") 89 | s.run_simulation() 90 | zero_controls = s.get_diagnostics().get_zero_control_instructions() 91 | assert zero_controls == [3, 12] 92 | 93 | 94 | def test_data_dependencies_with_callers() -> None: 95 | """Tests the data dependency analysis with enabling callers.""" 96 | s = load_instance("data-dependencies-with-callers") 97 | s.run_simulation() 98 | dependencies = s.get_diagnostics().get_data_dependencies(2, include_callers=True) 99 | assert dependencies == [2, 4, 6, 8, 9] 100 | 101 | dependencies = s.get_diagnostics().get_data_dependencies(7, include_callers=True) 102 | assert dependencies == [2, 4, 6, 7] 103 | # 8 and 9 are not included `test` doesn't have unknown callers in this case, so the analysis won't include all callers. 104 | -------------------------------------------------------------------------------- /test/test_utility.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file test_utility.cpp 13 | * @brief Test the functionality of utility functions provided by the debugger. 14 | */ 15 | #include "backend/debug.h" 16 | #include "common.h" 17 | #include "common_fixtures.hpp" 18 | 19 | #include <cstddef> 20 | #include <gtest/gtest.h> 21 | #include <map> 22 | #include <string> 23 | #include <utility> 24 | 25 | namespace mqt::debugger::test { 26 | 27 | /** 28 | * @brief Fixture for testing the correctness of utility functions. 29 | * 30 | * This fixture creates a DDSimulationState and allows to load code from files 31 | * in the `circuits` directory. 32 | */ 33 | class UtilityTest : public LoadFromFileFixture {}; 34 | 35 | /** 36 | * @test Test the retrieval of the number of instructions in the loaded code. 37 | */ 38 | TEST_F(UtilityTest, GetInstructionCount) { 39 | loadFromFile("complex-jumps"); 40 | ASSERT_EQ(state->getInstructionCount(state), 15); 41 | } 42 | 43 | /** 44 | * @test Test the retrieval of the position of an instruction in the loaded 45 | * code. 46 | */ 47 | TEST_F(UtilityTest, GetInstructionPosition) { 48 | loadFromFile("complex-jumps"); 49 | 50 | const std::map<size_t, std::pair<size_t, size_t>> expected = { 51 | {0, {0, 9}}, {1, {38, 112}}, {2, {79, 88}}, 52 | {3, {112, 112}}, {4, {150, 298}}, {12, {452, 477}}}; 53 | for (const auto& [instruction, expectedPosition] : expected) { 54 | size_t start = 0; 55 | size_t end = 0; 56 | state->getInstructionPosition(state, instruction, &start, &end); 57 | ASSERT_EQ(start, expectedPosition.first) 58 | << "Failed for instruction " << instruction; 59 | ASSERT_EQ(end, expectedPosition.second) 60 | << "Failed for instruction " << instruction; 61 | } 62 | } 63 | 64 | /** 65 | * @test Test that an error is returned when trying to access the position of an 66 | * instruction with index larger than the total number of instructions. 67 | */ 68 | TEST_F(UtilityTest, BadInstructionPosition) { 69 | loadFromFile("complex-jumps"); 70 | 71 | size_t start = 0; 72 | size_t end = 0; 73 | ASSERT_EQ(state->getInstructionPosition(state, 100, &start, &end), ERROR); 74 | } 75 | 76 | } // namespace mqt::debugger::test 77 | -------------------------------------------------------------------------------- /test/utils_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file utils_test.cpp 13 | * @brief Implementation of utility functions for testing. 14 | * 15 | * This file implements several utility functions that are used in the test 16 | * files. 17 | */ 18 | 19 | #include "utils/utils_test.hpp" 20 | 21 | #include "common.h" 22 | 23 | #include <filesystem> 24 | #include <fstream> 25 | #include <iostream> 26 | #include <iterator> 27 | #include <string> 28 | 29 | namespace mqt::debugger::test { 30 | 31 | bool complexEquality(const Complex& c, double real, double imaginary) { 32 | const double epsilon = 0.001; 33 | if (real - c.real > epsilon || c.real - real > epsilon) { 34 | return false; 35 | } 36 | if (imaginary - c.imaginary > epsilon || c.imaginary - imaginary > epsilon) { 37 | return false; 38 | } 39 | return true; 40 | } 41 | 42 | bool classicalEquals(const Variable& v, bool value) { 43 | return v.type == VarBool && v.value.boolValue == value; 44 | } 45 | 46 | std::string readFromCircuitsPath(const std::string& testName) { 47 | const std::filesystem::path localPath = 48 | std::filesystem::path("circuits") / (testName + std::string(".qasm")); 49 | std::ifstream file(localPath); 50 | if (!file.is_open()) { 51 | file = std::ifstream(std::filesystem::path("../../test/circuits") / 52 | (testName + std::string(".qasm"))); 53 | if (!file.is_open()) { 54 | std::cerr << "Could not open file\n"; 55 | file.close(); 56 | return ""; 57 | } 58 | } 59 | 60 | const std::string code((std::istreambuf_iterator<char>(file)), 61 | std::istreambuf_iterator<char>()); 62 | file.close(); 63 | return code; 64 | } 65 | 66 | /** 67 | * @brief Generate a string representation of a double without trailing zeros. 68 | * 69 | * @param d The double to convert to a string. 70 | * @return The string representation of the double. 71 | */ 72 | std::string doubleToStringTest(const double d) { 73 | auto string = std::to_string(d); 74 | while (string.back() == '0') { 75 | string.pop_back(); 76 | } 77 | if (string.back() == '.') { 78 | string.pop_back(); 79 | } 80 | return string; 81 | } 82 | 83 | std::string complexToStringTest(const Complex& c) { 84 | const double epsilon = 0.0000001; 85 | if (c.imaginary < epsilon && c.imaginary > -epsilon) { 86 | return doubleToStringTest(c.real); 87 | } 88 | if (c.real < epsilon && c.real > -epsilon) { 89 | return doubleToStringTest(c.imaginary) + "i"; 90 | } 91 | return doubleToStringTest(c.real) + " + " + doubleToStringTest(c.imaginary) + 92 | "i"; 93 | } 94 | 95 | } // namespace mqt::debugger::test 96 | --------------------------------------------------------------------------------