├── .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: "✨ "
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 |
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 `_ or `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 `_.
9 | - Search through past `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 `_.
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
20 | #include
21 | #include
22 | #include
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 [, ...]*;
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 [, ...]*;
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], [, ...]* { STATE_REPRESENTATION }
84 |
85 | .. code-block::
86 |
87 | STATE_REPRESENTATION =
88 | |
89 | |
90 |
91 | STATEVECTOR = [, ...]*
92 |
93 | CIRCUIT =
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 ` 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 `_ is used for providing bindings of the C++ core library to Python (see `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 ` fails.
6 |
7 | All of these methods require a :py:class:`Diagnostics ` object to be executed, which can be obtained from the :py:class:`SimulationState ` object using
8 | :cpp:member:`SimulationState::getDiagnostics `/:py:meth:`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 `/:py:meth:`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 `/:py:meth:`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 `/:py:meth:`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 `_, which forms the backbone of the `MQT `_.
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 `_):
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 `_ compiler supporting *C++17* and a minimum `CMake `_ 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 `_.
93 | In order to access the latest build logs, visit the `GitHub Actions page `_.
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 `_.
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 `_. This backend is instantiated by calling :py:func:`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 ` with the quantum program as a string argument.
34 | Currently, the supported quantum program format is `QASM 2.0 `_.
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 `.
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 ` 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 `_.
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 |
29 |
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 |
38 |
39 |

44 |
45 |
46 |

51 |
52 |
53 |
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 `_ at the `Technical University of Munich `_ as part of the :doc:`Munich Quantum Toolkit ` (*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 `.
12 | Then proceed to the :doc:`quickstart guide ` and read the :doc:`reference documentation `.
13 | If you are interested in the theory behind MQT Debugger, have a look at the publications in the :doc:`publication list `.
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 ` guide. If you are having trouble with the installation or the usage of MQT Debugger, please let us know at our :doc:`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 `_
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 `_.
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
25 | #include
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
21 | #include
22 | #include
23 | #include
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>& 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> 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& 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>
98 | getPartialTraceFromStateVector(const Statevector& sv,
99 | const std::vector& 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>& 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
116 | getSubStateVectorAmplitudes(const Statevector& sv,
117 | const std::vector& 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
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 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
17 | #include
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& 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,
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
19 | #include
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
19 | #include
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 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 splitString(const std::string& text,
59 | const std::vector& 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
24 | #include
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
19 | #include
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
19 | #include
20 | #include
21 | #include
22 | #include
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 splitString(const std::string& text, char delimiter,
37 | bool includeEmpty) {
38 | const std::vector delimiters{delimiter};
39 | return splitString(text, delimiters, includeEmpty);
40 | }
41 |
42 | std::vector splitString(const std::string& text,
43 | const std::vector& delimiters,
44 | bool includeEmpty) {
45 | std::vector 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
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(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
20 | #include
21 | #include