├── python └── mqt │ └── debugger │ ├── py.typed │ ├── _version.pyi │ ├── check │ ├── __main__.py │ ├── __init__.py │ ├── calibration.py │ └── runtime_check.py │ ├── dap │ ├── __init__.py │ ├── adapter.py │ └── messages │ │ ├── terminated_dap_event.py │ │ ├── initialized_dap_event.py │ │ ├── dap_event.py │ │ ├── exited_dap_event.py │ │ ├── utils.py │ │ ├── capabilities_dap_event.py │ │ ├── next_dap_message.py │ │ ├── pause_dap_message.py │ │ ├── step_out_dap_message.py │ │ ├── step_in_dap_message.py │ │ ├── configuration_done_dap_message.py │ │ ├── threads_dap_message.py │ │ ├── continue_dap_message.py │ │ ├── step_back_dap_message.py │ │ ├── terminate_dap_message.py │ │ ├── disconnect_dap_message.py │ │ ├── gray_out_event.py │ │ ├── reverse_continue_dap_message.py │ │ ├── restart_frame_dap_message.py │ │ ├── dap_message.py │ │ ├── set_exception_breakpoints_dap_message.py │ │ ├── stopped_dap_event.py │ │ ├── output_dap_event.py │ │ ├── exception_info_message.py │ │ ├── stack_trace_dap_message.py │ │ ├── initialize_dap_message.py │ │ ├── restart_dap_message.py │ │ ├── launch_dap_message.py │ │ ├── scopes_dap_message.py │ │ ├── __init__.py │ │ └── set_breakpoints_dap_message.py │ └── __init__.py ├── docs ├── .gitignore ├── support.md ├── _static │ ├── mqt_dark.png │ ├── mqt_light.png │ └── custom.css ├── library │ ├── python │ │ ├── Debug.rst │ │ ├── dap │ │ │ ├── Messages.rst │ │ │ ├── DAP.rst │ │ │ └── Adapter.rst │ │ └── Python.rst │ ├── interface │ │ ├── Common.rst │ │ ├── Debug.rst │ │ ├── Diagnostics.rst │ │ └── Interface.rst │ ├── parsing │ │ ├── Utils.rst │ │ ├── ParsingError.rst │ │ ├── AssertionParsing.rst │ │ ├── CodePreprocessing.rst │ │ └── Parsing.rst │ ├── dd │ │ ├── DDSimDebug.rst │ │ ├── DDSimDiagnostics.rst │ │ └── Dd.rst │ └── Library.rst ├── refs.bib ├── references.md ├── _templates │ └── page.html ├── quickstart.md ├── index.md ├── assertions.md ├── diagnosis.md └── conf.py ├── .gitattributes ├── test ├── python │ ├── resources │ │ ├── end-to-end │ │ │ ├── bell.out │ │ │ ├── fail_eq.out │ │ │ ├── ghz_3.out │ │ │ ├── fail_ghz.out │ │ │ ├── grover_3.out │ │ │ ├── qpe.out │ │ │ ├── bv.out │ │ │ ├── dj_4.out │ │ │ ├── ghz_5.out │ │ │ ├── bell.qasm │ │ │ ├── ghz_3.qasm │ │ │ ├── ghz_5.qasm │ │ │ ├── dj_4.qasm │ │ │ ├── bv.qasm │ │ │ ├── grover_3.qasm │ │ │ ├── fail_ghz.qasm │ │ │ ├── fail_eq.qasm │ │ │ └── qpe.qasm │ │ ├── compilation │ │ │ ├── original.qasm │ │ │ ├── calibration.json │ │ │ ├── test_program_compiled │ │ │ │ ├── slice_2.qasm │ │ │ │ ├── slice_1.qasm │ │ │ │ └── slice_3.qasm │ │ │ └── test-shots.qasm │ │ ├── diagnosis │ │ │ ├── dependencies.qasm │ │ │ ├── missing-interaction.qasm │ │ │ ├── dependencies-with-jumps.qasm │ │ │ ├── data-dependencies-with-callers.qasm │ │ │ └── control-always-zero.qasm │ │ └── bindings │ │ │ ├── ghz-incorrect.qasm │ │ │ ├── classical.qasm │ │ │ └── jumps.qasm │ └── test_diagnosis.py ├── circuits │ ├── failing-assertions-multiple-missing-interaction.qasm │ ├── failing-assertions-multiple-causes.qasm │ ├── zero-controls-with-jumps.qasm │ ├── failing-assertions-multiple-zero-controls.qasm │ ├── runtime-interaction.qasm │ ├── failing-assertions-missing-interaction.qasm │ ├── complex-jumps.qasm │ ├── failing-assertions.qasm │ ├── classical-storage.qasm │ └── diagnose-with-jumps.qasm ├── CMakeLists.txt ├── test_utility.cpp └── utils_test.cpp ├── .clang-format ├── .git-archival.txt ├── .cmake-format.yaml ├── .editorconfig ├── .github ├── CONTRIBUTING.md ├── workflows │ ├── release-drafter.yml │ ├── update-mqt-core.yml │ ├── templating.yml │ ├── upstream.yml │ └── cd.yml ├── SECURITY.md ├── pull_request_template.md ├── SUPPORT.md ├── codecov.yml ├── release-drafter.yml ├── ISSUE_TEMPLATE │ ├── feature-request.yml │ └── bug-report.yml └── renovate.json5 ├── bindings ├── CMakeLists.txt ├── bindings.cpp └── dd │ └── DDSimDebugBindings.cpp ├── src ├── common │ └── parsing │ │ ├── ParsingError.cpp │ │ └── Utils.cpp └── CMakeLists.txt ├── include ├── python │ ├── dd │ │ └── DDSimDebugBindings.hpp │ └── InterfaceBindings.hpp ├── common │ ├── parsing │ │ ├── ParsingError.hpp │ │ ├── Utils.hpp │ │ └── AssertionTools.hpp │ ├── Span.hpp │ └── ComplexMathematics.hpp ├── frontend │ └── cli │ │ └── CliFrontEnd.hpp └── common.h ├── sitecustomize.py ├── app ├── CMakeLists.txt └── testDDSimDebugger.cpp ├── .license-tools-config.json ├── LICENSE.md ├── cmake ├── cmake_uninstall.cmake.in └── ExternalDependencies.cmake ├── .readthedocs.yaml ├── CMakeLists.txt ├── .clang-tidy ├── .gitignore └── .pre-commit-config.yaml /python/mqt/debugger/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | doxygen/ 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | .git_archival.txt export-subst 2 | -------------------------------------------------------------------------------- /docs/support.md: -------------------------------------------------------------------------------- 1 | ```{include} ../.github/SUPPORT.md 2 | 3 | ``` 4 | -------------------------------------------------------------------------------- /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/fail_eq.out: -------------------------------------------------------------------------------- 1 | 0, 0.707, 0, 0.707 2 | --- 3 | 4 | --- 5 | 7 6 | -------------------------------------------------------------------------------- /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/fail_ghz.out: -------------------------------------------------------------------------------- 1 | 0.707,0,0,0.707,0,0,0,0 2 | --- 3 | 4 | --- 5 | 4 6 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IncludeBlocks: Regroup 3 | PointerAlignment: Left 4 | Standard: c++20 5 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /docs/_static/mqt_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munich-quantum-toolkit/debugger/HEAD/docs/_static/mqt_dark.png -------------------------------------------------------------------------------- /docs/library/python/Debug.rst: -------------------------------------------------------------------------------- 1 | mqt.debugger 2 | ============ 3 | 4 | .. automodule:: mqt.debugger 5 | :members: 6 | -------------------------------------------------------------------------------- /docs/_static/mqt_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/munich-quantum-toolkit/debugger/HEAD/docs/_static/mqt_light.png -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/library/interface/Common.rst: -------------------------------------------------------------------------------- 1 | common.h 2 | ======== 3 | 4 | .. doxygenfile:: common.h 5 | :project: mqt-debugger 6 | -------------------------------------------------------------------------------- /.git-archival.txt: -------------------------------------------------------------------------------- 1 | node: $Format:%H$ 2 | node-date: $Format:%cI$ 3 | describe-name: $Format:%(describe:tags=true,match=*[0-9]*)$ 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/library/parsing/Utils.rst: -------------------------------------------------------------------------------- 1 | Utils.hpp 2 | ===================== 3 | 4 | .. doxygenfile:: Utils.hpp 5 | :project: mqt-debugger 6 | -------------------------------------------------------------------------------- /docs/library/dd/DDSimDebug.rst: -------------------------------------------------------------------------------- 1 | DDSimDebug.hpp 2 | ============== 3 | 4 | .. doxygenfile:: DDSimDebug.hpp 5 | :project: mqt-debugger 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /docs/library/parsing/ParsingError.rst: -------------------------------------------------------------------------------- 1 | ParsingError.hpp 2 | ===================== 3 | 4 | .. doxygenfile:: ParsingError.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/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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Contributing 5 | 6 | Thank you for your interest in contributing to MQT Debugger! 7 | An extensive contribution guide is available in our [documentation](https://mqt.readthedocs.io/projects/debugger/en/latest/contributing.html). 8 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /bindings/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_mqt_python_binding( 9 | DEBUGGER 10 | ${MQT_DEBUGGER_TARGET_NAME}-bindings 11 | bindings.cpp 12 | InterfaceBindings.cpp 13 | dd/DDSimDebugBindings.cpp 14 | MODULE_NAME 15 | pydebugger 16 | INSTALL_DIR 17 | . 18 | LINK_LIBS 19 | MQT::Debugger 20 | pybind11_json) 21 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /.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@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0 22 | env: 23 | GITHUB_TOKEN: ${{ github.token }} 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | /** 22 | * @brief Binds the dd debugging backend to Python. 23 | * @param m The `pybind11` module. 24 | */ 25 | void bindBackend(pybind11::module& m); 26 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /sitecustomize.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 | """Site customization shim to enable multiprocess coverage collection in tests. 10 | 11 | See https://coverage.readthedocs.io/en/latest/subprocess.html. 12 | """ 13 | 14 | from __future__ import annotations 15 | 16 | try: 17 | import coverage 18 | 19 | coverage.process_startup() 20 | except ImportError: 21 | # The 'coverage' module is optional 22 | # If it is not installed, we do not enable multiprocess coverage collection 23 | pass 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /bindings/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 | #include 23 | 24 | namespace py = pybind11; 25 | using namespace pybind11::literals; 26 | 27 | PYBIND11_MODULE(MQT_DEBUGGER_MODULE_NAME, m, py::mod_gil_not_used()) { 28 | bindDiagnostics(m); 29 | bindFramework(m); 30 | bindBackend(m); 31 | } 32 | -------------------------------------------------------------------------------- /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 | /** 22 | * @brief Binds the main debugging framework to Python. 23 | * @param m The `pybind11` module. 24 | */ 25 | void bindFramework(pybind11::module& m); 26 | 27 | /** 28 | * @brief Binds the diagnostics backend to Python. 29 | * @param m The `pybind11` module. 30 | */ 31 | void bindDiagnostics(pybind11::module& m); 32 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Security Policy 5 | 6 | ## Supported Versions 7 | 8 | Security updates are applied only to the most recent releases. 9 | 10 | ## Reporting a Vulnerability 11 | 12 | To report vulnerabilities, you can privately report a potential security issue 13 | via the GitHub security vulnerabilities feature. This can be done here: 14 | 15 | https://github.com/munich-quantum-toolkit/debugger/security/advisories 16 | 17 | Please do **not** open a public issue about a potential security vulnerability. 18 | 19 | You can find more details on the security vulnerability feature in the GitHub 20 | documentation here: 21 | 22 | https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability 23 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Please include a summary of the change and, if applicable, which issue is fixed. 4 | Please also include relevant motivation and context. 5 | List any dependencies that are required for this change. 6 | 7 | Fixes #(issue) 8 | 9 | ## Checklist: 10 | 11 | 14 | 15 | - [ ] The pull request only contains commits that are focused and relevant to this change. 16 | - [ ] I have added appropriate tests that cover the new/changed functionality. 17 | - [ ] I have updated the documentation to reflect these changes. 18 | - [ ] The changes follow the project's style guidelines and introduce no new warnings. 19 | - [ ] The changes are fully tested and pass the CI checks. 20 | - [ ] I have reviewed my own code changes. 21 | -------------------------------------------------------------------------------- /.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@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 25 | with: 26 | update-to-head: ${{ github.event.inputs.update-to-head == 'true' }} 27 | secrets: 28 | APP_ID: ${{ secrets.APP_ID }} 29 | APP_PRIVATE_KEY: ${{ secrets.APP_PRIVATE_KEY }} 30 | -------------------------------------------------------------------------------- /.github/workflows/templating.yml: -------------------------------------------------------------------------------- 1 | name: Templating 2 | on: 3 | pull_request: 4 | paths: 5 | - ".github/workflows/templating.yml" 6 | workflow_dispatch: # Allow manual triggering 7 | 8 | jobs: 9 | render-template: 10 | name: Render template 11 | runs-on: ubuntu-latest 12 | permissions: 13 | contents: write 14 | pull-requests: write 15 | steps: 16 | - id: create-token 17 | uses: actions/create-github-app-token@29824e69f54612133e76f7eaac726eef6c875baf # v2.2.1 18 | with: 19 | app-id: ${{ secrets.APP_ID }} 20 | private-key: ${{ secrets.APP_PRIVATE_KEY }} 21 | - uses: munich-quantum-toolkit/templates@4299ec8bbb21d85c1b32ac6ac6fd6701586dea21 # v1.1.11 22 | with: 23 | token: ${{ steps.create-token.outputs.token }} 24 | name: Debugger 25 | organization: ${{ github.repository_owner }} 26 | project-type: c++-python 27 | repository: ${{ github.event.repository.name }} 28 | has-changelog-and-upgrade-guide: false 29 | -------------------------------------------------------------------------------- /.github/SUPPORT.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Support 5 | 6 | If you are stuck with a problem using MQT Debugger or have questions, please get in touch at our [Issues] or [Discussions]. 7 | We'd love to help. 8 | 9 | You can save time by following this procedure when reporting a problem: 10 | 11 | - Do try to solve the problem on your own first. 12 | - Search through past [Issues] and [Discussions] to see if someone else already had the same problem. 13 | - Before filing a bug report, try to create a minimal reproducible example (MRE) that reproduces the problem. 14 | It is much easier to identify the cause for the problem if a handful of lines suffice to show that something is not working. 15 | 16 | You can also always reach us at [quantum.cda@xcit.tum.de](mailto:quantum.cda@xcit.tum.de). 17 | 18 | [Issues]: https://github.com/munich-quantum-toolkit/debugger/issues 19 | [Discussions]: https://github.com/munich-quantum-toolkit/debugger/discussions 20 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "extern/**/*" 3 | - "bindings/**/*" 4 | - "test/**/*" 5 | - "python/mqt/debugger/dap/**/*" 6 | - "python/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 | - "python/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/workflows/upstream.yml: -------------------------------------------------------------------------------- 1 | name: Qiskit Upstream Tests 2 | on: 3 | schedule: 4 | # Run every Monday at 00:00 UTC 5 | - cron: "0 0 * * 1" 6 | pull_request: 7 | paths: 8 | - ".github/workflows/upstream.yml" 9 | workflow_dispatch: # Allow manual triggering 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | qiskit-upstream-tests: 17 | name: 🐍⚛️ 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | runs-on: [ubuntu-24.04, macos-14, windows-2022] 22 | uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-tests.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 23 | with: 24 | runs-on: ${{ matrix.runs-on }} 25 | setup-z3: true 26 | 27 | create-issue-on-failure: 28 | name: Create issue on failure 29 | needs: qiskit-upstream-tests 30 | if: ${{ always() }} 31 | uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-qiskit-upstream-issue.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 32 | with: 33 | tests-result: ${{ needs.qiskit-upstream-tests.result }} 34 | permissions: 35 | contents: read 36 | issues: write # Needed to create/update issues 37 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /python/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": True, 32 | "supportsRestartFrame": True, 33 | "supportsTerminateRequest": True, 34 | "supportsRestartRequest": True, 35 | "supportsVariableType": True, 36 | "supportsDelayedStackTraceLoading": False, 37 | "supportsVariablePaging": True, 38 | } 39 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 18 | .acknowledgement { 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | 25 | /* override the default background color for literal strings */ 26 | body:not([data-theme="light"]) .highlight .sa, 27 | .highlight .sb, 28 | .highlight .sc, 29 | .highlight .dl, 30 | .highlight .sd, 31 | .highlight .s2, 32 | .highlight .se, 33 | .highlight .sh, 34 | .highlight .si, 35 | .highlight .sx, 36 | .highlight .sr, 37 | .highlight .s1, 38 | .highlight .ss, 39 | .highlight .s1, 40 | .highlight .s { 41 | background-color: #00000001; 42 | } 43 | 44 | /* provide dark mode overrides for mystnb variables */ 45 | body:not([data-theme="light"]) { 46 | --mystnb-source-bg-color: #131416; 47 | --mystnb-stdout-bg-color: #1a1c1e; 48 | --mystnb-stderr-bg-color: #442222; 49 | --mystnb-traceback-bg-color: #202020; 50 | } 51 | 52 | body:not([data-theme="light"]) .highlight .gp { 53 | color: #c65d09; 54 | } 55 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | formats: 4 | - htmlzip 5 | 6 | sphinx: 7 | configuration: docs/conf.py 8 | 9 | build: 10 | os: ubuntu-24.04 11 | tools: 12 | python: "3.12" 13 | apt_packages: 14 | - graphviz 15 | - inkscape 16 | jobs: 17 | post_checkout: 18 | # Skip docs build if the commit message contains "skip ci" 19 | - (git --no-pager log --pretty="tformat:%s -- %b" -1 | grep -viq "skip ci") || exit 183 20 | # Skip docs build if there are no changes related to docs 21 | - | 22 | if [ "$READTHEDOCS_VERSION_TYPE" = "external" ] && git diff --quiet origin/main -- bindings/ include/ docs/ python/ .github/contributing* .github/support* .readthedocs.yaml; 23 | then 24 | exit 183; 25 | fi 26 | # Unshallow the git clone and fetch tags to get proper version information 27 | - git fetch --unshallow --tags 28 | pre_build: 29 | # Set up uv 30 | - asdf plugin add uv 31 | - asdf install uv latest 32 | - asdf global uv latest 33 | build: 34 | html: 35 | - uv run --frozen --no-dev --group docs -m sphinx -T -b html -d docs/_build/doctrees -D language=en docs $READTHEDOCS_OUTPUT/html 36 | htmlzip: 37 | - uv run --frozen --no-dev --group docs -m sphinx -T -b dirhtml -d docs/_build/doctrees -D language=en docs docs/_build/dirhtml 38 | - mkdir -p $READTHEDOCS_OUTPUT/htmlzip 39 | - zip -r $READTHEDOCS_OUTPUT/htmlzip/html.zip docs/_build/dirhtml/* 40 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | # This file has been generated from an external template. Please do not modify it directly. 2 | # Changes should be contributed to https://github.com/munich-quantum-toolkit/templates. 3 | 4 | name-template: "MQT Debugger $RESOLVED_VERSION Release" 5 | tag-template: "v$RESOLVED_VERSION" 6 | categories: 7 | - title: "🚀 Features and Enhancements" 8 | labels: 9 | - "enhancement" 10 | - "feature" 11 | - "refactor" 12 | - "usability" 13 | - title: "🐛 Bug Fixes" 14 | labels: 15 | - "bug" 16 | - "fix" 17 | - title: "📄 Documentation" 18 | labels: 19 | - "documentation" 20 | - title: "🤖 CI" 21 | labels: 22 | - "continuous integration" 23 | - title: "📦 Packaging" 24 | labels: 25 | - "packaging" 26 | - title: "🧹 Code Quality" 27 | labels: 28 | - "code quality" 29 | - title: "⬆️ Dependencies" 30 | collapse-after: 5 31 | labels: 32 | - "dependencies" 33 | - "github-actions" 34 | - "pre-commit" 35 | - "submodules" 36 | change-template: "- $TITLE ([#$NUMBER]($URL)) ([**@$AUTHOR**](https://github.com/$AUTHOR))" 37 | change-title-escapes: '\<*_&' 38 | version-resolver: 39 | major: 40 | labels: 41 | - "major" 42 | minor: 43 | labels: 44 | - "minor" 45 | patch: 46 | labels: 47 | - "patch" 48 | default: patch 49 | 50 | template: | 51 | ## 👀 What Changed 52 | 53 | $CHANGES 54 | 55 | **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION 56 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.yml: -------------------------------------------------------------------------------- 1 | # This file has been generated from an external template. Please do not modify it directly. 2 | # Changes should be contributed to https://github.com/munich-quantum-toolkit/templates. 3 | 4 | name: ✨ Feature request 5 | description: Suggest a new feature or improvement 6 | title: "✨ " 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: > 11 | **Thanks for helping improve this project by suggesting a feature!** 12 | 13 | ⚠ Before submitting: 14 | - Search [existing feature requests](https://github.com/munich-quantum-toolkit/debugger/search?q=is%3Aissue&type=issues) to avoid duplicates 15 | - One feature per issue helps us track and implement ideas effectively 16 | 17 | - type: textarea 18 | attributes: 19 | label: Problem Statement 20 | description: >- 21 | Describe the problem you are facing and why it needs to be solved. What limitations are you encountering? 22 | placeholder: >- 23 | Currently, when I try to [action/task], I am unable to [blockers/limitations]. 24 | This creates problems because [impact/consequences]. 25 | validations: 26 | required: true 27 | 28 | - type: textarea 29 | attributes: 30 | label: Proposed Solution 31 | description: > 32 | Describe your ideal solution. What would the feature look like? How would it work? 33 | placeholder: >- 34 | I'd like to see [feature/change] that would: 35 | - [benefit 1] 36 | - [benefit 2] 37 | - [example usage/scenario] 38 | validations: 39 | required: true 40 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /bindings/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 | using namespace mqt::debugger; 26 | 27 | void bindBackend(pybind11::module& m) { 28 | 29 | m.def( 30 | "create_ddsim_simulation_state", 31 | []() { 32 | // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) 33 | auto* state = new DDSimulationState(); 34 | createDDSimulationState(state); 35 | return &state->interface; 36 | }, 37 | R"(Creates a new `SimulationState` instance using the DD backend for simulation and the OpenQASM language as input format. 38 | 39 | Returns: 40 | SimulationState: The created simulation state.)"); 41 | 42 | m.def( 43 | "destroy_ddsim_simulation_state", 44 | [](SimulationState* state) { 45 | // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast) 46 | destroyDDSimulationState(reinterpret_cast<DDSimulationState*>(state)); 47 | // NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast) 48 | }, 49 | R"(Delete a given DD-based `SimulationState` instance and free up resources. 50 | 51 | Args: 52 | state (SimulationState): The simulation state to delete.)"); 53 | } 54 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | $schema: "https://docs.renovatebot.com/renovate-schema.json", 3 | extends: [ 4 | "config:recommended", 5 | ":gitSignOff", 6 | "helpers:pinGitHubActionDigestsToSemver", 7 | ":automergeDigest", 8 | ":automergeStableNonMajor", 9 | ":combinePatchMinorReleases", 10 | ":configMigration", 11 | ":enablePreCommit", 12 | ":prConcurrentLimitNone", 13 | ":prHourlyLimitNone", 14 | ], 15 | enabledManagers: ["cargo", "github-actions", "npm", "pep621", "pre-commit"], 16 | lockFileMaintenance: { 17 | enabled: true, 18 | automerge: true 19 | }, 20 | labels: ["dependencies"], 21 | schedule: ["every weekend"], 22 | packageRules: [ 23 | { 24 | matchManagers: ["cargo"], 25 | addLabels: ["rust"], 26 | commitMessagePrefix: "⬆\uFE0F\uD83E\uDD80" 27 | }, 28 | { 29 | matchManagers: ["github-actions"], 30 | addLabels: ["github-actions"], 31 | commitMessagePrefix: "⬆\uFE0F\uD83D\uDC68\u200D\uD83D\uDCBB" 32 | }, 33 | { 34 | matchManagers: ["npm"], 35 | addLabels: ["javascript"], 36 | commitMessagePrefix: "⬆\uFE0F\uD83D\uDCDC" 37 | }, 38 | { 39 | matchManagers: ["pep621"], 40 | addLabels: ["python"], 41 | commitMessagePrefix: "⬆\uFE0F\uD83D\uDC0D" 42 | }, 43 | { 44 | matchManagers: ["pre-commit"], 45 | addLabels: ["pre-commit"], 46 | commitMessagePrefix: "⬆\uFE0F\uD83E\uDE9D", 47 | versioning: "pep440" 48 | }, 49 | { 50 | matchUpdateTypes: ["lockFileMaintenance"], 51 | commitMessagePrefix: "⬆\uFE0F\uD83D\uDD12\uFE0F", 52 | }, 53 | { 54 | description: "Group and automerge all patch updates", 55 | groupName: "patch updates", 56 | groupSlug: "all-patch", 57 | matchUpdateTypes: ["patch"], 58 | automerge: true 59 | }, 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /docs/refs.bib: -------------------------------------------------------------------------------- 1 | @inproceedings{mqt, 2 | title = {The {{MQT}} Handbook: {{A}} Summary of Design Automation Tools and Software for Quantum Computing}, 3 | shorttitle = {The MQT Handbook}, 4 | author = {Robert Wille and Lucas Berent and Tobias Forster and Jagatheesan Kunasaikaran and Kevin Mato and Tom Peham and Nils Quetschlich and Damian Rovara and Aaron Sander and Ludwig Schmid and Daniel Schoenberger and Yannick Stade and Lukas Burgholzer}, 5 | year = {2024}, 6 | booktitle = {IEEE International Conference on Quantum Software (QSW)}, 7 | doi = {10.1109/QSW62656.2024.00013}, 8 | eprint = {2405.17543}, 9 | eprinttype = {arxiv}, 10 | addendum = {A live version of this document is available at \url{https://mqt.readthedocs.io}}, 11 | } 12 | 13 | @misc{rovara2024assertionrefinement, 14 | title = {Automatically Refining Assertions for Efficient Debugging of Quantum Programs}, 15 | author = {Damian Rovara and Lukas Burgholzer and Robert Wille}, 16 | year = {2024}, 17 | eprint = {2412.14252}, 18 | eprinttype = {arxiv}, 19 | } 20 | 21 | @misc{rovara2024debugging, 22 | title = {A Framework for Debugging Quantum Programs}, 23 | author = {Damian Rovara and Lukas Burgholzer and Robert Wille}, 24 | year = {2024}, 25 | eprint = {2412.12269}, 26 | eprinttype = {arxiv}, 27 | } 28 | 29 | @misc{rovara2025runtimeverification, 30 | title = {A Framework for the Efficient Evaluation of Runtime Assertions on Quantum Computers}, 31 | author = {Damian Rovara and Lukas Burgholzer and Robert Wille}, 32 | year = {2025}, 33 | eprint = {2505.03885}, 34 | eprinttype = {arxiv}, 35 | } 36 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /.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 | build-sdist: 12 | name: 🐍 Packaging 13 | uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-sdist.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 14 | 15 | # Builds wheels on all supported platforms using cibuildwheel. 16 | # The wheels are uploaded as GitHub artifacts `dev-cibw-*` or `cibw-*`, depending on whether 17 | # the workflow is triggered from a PR or a release, respectively. 18 | build-wheel: 19 | name: 🐍 Packaging 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | runs-on: 24 | [ 25 | ubuntu-24.04, 26 | ubuntu-24.04-arm, 27 | macos-15-intel, 28 | macos-14, 29 | windows-2022, 30 | windows-11-arm, 31 | ] 32 | uses: munich-quantum-toolkit/workflows/.github/workflows/reusable-python-packaging-wheel-cibuildwheel.yml@89734354f64f30a80dd16602d4e271df34348987 # v1.17.4 33 | with: 34 | runs-on: ${{ matrix.runs-on }} 35 | 36 | deploy: 37 | if: github.event_name == 'release' && github.event.action == 'published' 38 | name: 🚀 Deploy to PyPI 39 | runs-on: ubuntu-latest 40 | environment: 41 | name: pypi 42 | url: https://pypi.org/p/mqt.debugger 43 | permissions: 44 | id-token: write 45 | attestations: write 46 | contents: read 47 | needs: [build-sdist, build-wheel] 48 | steps: 49 | - uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 50 | with: 51 | pattern: cibw-* 52 | path: dist 53 | merge-multiple: true 54 | - name: Generate artifact attestation for sdist and wheel(s) 55 | uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 # v3.1.0 56 | with: 57 | subject-path: dist/* 58 | - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 59 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /docs/references.md: -------------------------------------------------------------------------------- 1 | # References 2 | 3 | _MQT Debugger_ has a strong foundation in peer‑reviewed research. 4 | Many of its built‑in algorithms are based on methods published in scientific journals and conferences. 5 | For an overview of _MQT Debugger_ and its features, see {cite:p}`rovara2024debugging`. 6 | If you want to cite this article, please use the following BibTeX entry: 7 | 8 | ```bibtex 9 | @misc{rovara2024debugging, 10 | title = {A Framework for Debugging Quantum Programs}, 11 | author = {Rovara, Damian and Burgholzer, Lukas and Wille, Robert}, 12 | year = {2024}, 13 | eprint = {2412.12269}, 14 | eprinttype = {arxiv} 15 | } 16 | ``` 17 | 18 | _MQT Debugger_ is part of the Munich Quantum Toolkit, which is described in {cite:p}`mqt`. 19 | If you want to cite the Munich Quantum Toolkit, please use the following BibTeX entry: 20 | 21 | ```bibtex 22 | @inproceedings{mqt, 23 | title = {The {{MQT}} Handbook: {{A}} Summary of Design Automation Tools and Software for Quantum Computing}, 24 | shorttitle = {{The MQT Handbook}}, 25 | author = {Wille, Robert and Berent, Lucas and Forster, Tobias and Kunasaikaran, Jagatheesan and Mato, Kevin and Peham, Tom and Quetschlich, Nils and Rovara, Damian and Sander, Aaron and Schmid, Ludwig and Schoenberger, Daniel and Stade, Yannick and Burgholzer, Lukas}, 26 | year = 2024, 27 | booktitle = {IEEE International Conference on Quantum Software (QSW)}, 28 | doi = {10.1109/QSW62656.2024.00013}, 29 | eprint = {2405.17543}, 30 | eprinttype = {arxiv}, 31 | addendum = {A live version of this document is available at \url{https://mqt.readthedocs.io}} 32 | } 33 | ``` 34 | 35 | Furthermore, if you use any of the particular algorithms such as 36 | 37 | - diagnostics and error cause analysis {cite:p}`rovara2024debugging`, 38 | - assertion refinement (moving or creating assertions) {cite:p}`rovara2024assertionrefinement`, or 39 | - runtime verification on real quantum devices {cite:p}`rovara2025runtimeverification` 40 | 41 | please consider citing their respective papers as well. 42 | 43 | A full list of references is given below. 44 | 45 | ```{bibliography} 46 | 47 | ``` 48 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /include/common/Span.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file Span.hpp 13 | * @brief Provides a Span class for representing a contiguous range of elements. 14 | * 15 | * Accessing pointers from the C-interface like arrays is not compliant with the 16 | * C++ Core Guidelines. This class provides a safer way to access contiguous 17 | * ranges of elements. 18 | */ 19 | 20 | #pragma once 21 | 22 | #include <cstddef> 23 | 24 | namespace mqt::debugger { 25 | 26 | /** 27 | * @brief Represents a contiguous range of elements that can be accessed as an 28 | * array. 29 | * @tparam T The type of the elements in the span. 30 | */ 31 | template <typename T> class Span { 32 | public: 33 | /** 34 | * @brief Constructs a new span from a pointer and a size. 35 | * @param ptr The pointer to the first element in the span. 36 | * @param count The number of elements in the span. 37 | */ 38 | Span(T* ptr, size_t count) : pointer(ptr), spanSize(count) {} 39 | 40 | /** 41 | * @brief Accesses the element at the given index. 42 | * @param index The index of the element to access. 43 | * @return The element at the given index. 44 | */ 45 | T& operator[](size_t index) const { 46 | // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) 47 | return pointer[index]; 48 | } 49 | 50 | /** 51 | * @brief Returns a pointer to the first element in the span. 52 | * @return A pointer to the first element in the span. 53 | */ 54 | T* data() const { return pointer; } 55 | 56 | /** 57 | * @brief Returns the number of elements in the span. 58 | * @return The number of elements in the span. 59 | */ 60 | [[nodiscard]] size_t size() const { return spanSize; } 61 | 62 | private: 63 | /** 64 | * @brief The pointer on which the span is based. 65 | */ 66 | T* pointer; 67 | 68 | /** 69 | * @brief The number of elements in the span, given by the user. 70 | */ 71 | size_t spanSize; 72 | }; 73 | 74 | } // namespace mqt::debugger 75 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.yml: -------------------------------------------------------------------------------- 1 | # This file has been generated from an external template. Please do not modify it directly. 2 | # Changes should be contributed to https://github.com/munich-quantum-toolkit/templates. 3 | 4 | name: 🐛 Bug Report 5 | description: Report a problem or unexpected behavior 6 | title: "🐛 <brief description of the issue>" 7 | body: 8 | - type: markdown 9 | attributes: 10 | value: >- 11 | **Thanks for taking the time to report an issue!** 12 | 13 | ⚠ Before submitting: 14 | 1. Search [existing issues](https://github.com/munich-quantum-toolkit/debugger/search?q=is%3Aissue&type=issues) to avoid duplicates 15 | 2. For questions or discussions, please use our [discussions forum](https://github.com/munich-quantum-toolkit/debugger/discussions) 16 | 17 | - type: textarea 18 | attributes: 19 | label: System and Environment Information 20 | description: Provide details about your environment. 21 | placeholder: | 22 | - Operating system and version: 23 | - MQT Debugger version: 24 | - Language versions (e.g., C++, Python, Rust, ...): 25 | - Compiler or build tool versions (e.g., Clang, GCC, rustc, ...): 26 | - Any relevant dependencies and their versions: 27 | validations: 28 | required: true 29 | 30 | - type: textarea 31 | attributes: 32 | label: Bug Description 33 | description: Provide a clear and detailed explanation of the issue you are experiencing. 34 | placeholder: | 35 | What happened? 36 | What did you expect to happen instead? 37 | Include any error messages or unexpected behavior you observed. 38 | validations: 39 | required: true 40 | 41 | - type: textarea 42 | attributes: 43 | label: Steps to Reproduce 44 | description: Provide specific steps to reproduce this issue. 45 | placeholder: | 46 | 1. Set up environment with '...' 47 | 2. Configure workflow using '...' 48 | 3. Execute command '...' 49 | 4. Observe error/issue: '...' 50 | 51 | Include any relevant code snippets, workflow configurations, or input files that help demonstrate the problem. 52 | validations: 53 | required: true 54 | -------------------------------------------------------------------------------- /include/frontend/cli/CliFrontEnd.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file CliFrontEnd.hpp 13 | * @brief Provides a CLI frontend for the debugger. 14 | * 15 | * This file contains the declaration of the CliFrontEnd class, which provides 16 | * a command-line interface for the debugger. 17 | */ 18 | 19 | #pragma once 20 | 21 | #include "backend/debug.h" 22 | 23 | #include <cstddef> 24 | #include <string> 25 | 26 | namespace mqt::debugger { 27 | 28 | #define ANSI_BG_YELLOW "\x1b[43m" 29 | #define ANSI_BG_RESET "\x1b[0m" 30 | #define ANSI_COL_GRAY "\x1b[90m" 31 | 32 | /** 33 | * @brief A command-line interface for the debugger. 34 | * 35 | * By creating an instance of this class and calling the `run` method, the user 36 | * can interact with the debugger using a command-line interface. 37 | */ 38 | class CliFrontEnd { 39 | public: 40 | /** 41 | * @brief Runs the debugger with the given code and state. 42 | * @param code The code to run (compatible with the provided 43 | * `SimulationState`) 44 | * @param state The state to run the code on 45 | */ 46 | void run(const char* code, SimulationState* state); 47 | 48 | private: 49 | /** 50 | * @brief The current code being executed. Used to display the code in the 51 | * CLI. 52 | */ 53 | std::string currentCode; 54 | 55 | /** 56 | * @brief Print the current state of the simulation. 57 | * @param state The simulation state. 58 | * @param inspecting The instruction that is currently inspected (or -1ULL if 59 | * nothing is being inspected). 60 | * @param codeOnly If true, only the code is displayed, not the state. 61 | */ 62 | void printState(SimulationState* state, size_t inspecting, 63 | bool codeOnly = false); 64 | 65 | /** 66 | * @brief Initialize the code for running it at a later time. 67 | */ 68 | void initCode(const char* code); 69 | 70 | /** 71 | * @brief Output a new code with updated assertions based on the assertion 72 | * refinement rules. 73 | * @param state The simulation state. 74 | */ 75 | void suggestUpdatedAssertions(SimulationState* state); 76 | }; 77 | 78 | } // namespace mqt::debugger 79 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /test/test_utility.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file test_utility.cpp 13 | * @brief Test the functionality of utility functions provided by the debugger. 14 | */ 15 | #include "backend/debug.h" 16 | #include "common.h" 17 | #include "common_fixtures.hpp" 18 | 19 | #include <cstddef> 20 | #include <gtest/gtest.h> 21 | #include <map> 22 | #include <string> 23 | #include <utility> 24 | 25 | namespace mqt::debugger::test { 26 | 27 | /** 28 | * @brief Fixture for testing the correctness of utility functions. 29 | * 30 | * This fixture creates a DDSimulationState and allows to load code from files 31 | * in the `circuits` directory. 32 | */ 33 | class UtilityTest : public LoadFromFileFixture {}; 34 | 35 | /** 36 | * @test Test the retrieval of the number of instructions in the loaded code. 37 | */ 38 | TEST_F(UtilityTest, GetInstructionCount) { 39 | loadFromFile("complex-jumps"); 40 | ASSERT_EQ(state->getInstructionCount(state), 15); 41 | } 42 | 43 | /** 44 | * @test Test the retrieval of the position of an instruction in the loaded 45 | * code. 46 | */ 47 | TEST_F(UtilityTest, GetInstructionPosition) { 48 | loadFromFile("complex-jumps"); 49 | 50 | const std::map<size_t, std::pair<size_t, size_t>> expected = { 51 | {0, {0, 9}}, {1, {38, 112}}, {2, {79, 88}}, 52 | {3, {112, 112}}, {4, {150, 298}}, {12, {452, 477}}}; 53 | for (const auto& [instruction, expectedPosition] : expected) { 54 | size_t start = 0; 55 | size_t end = 0; 56 | state->getInstructionPosition(state, instruction, &start, &end); 57 | ASSERT_EQ(start, expectedPosition.first) 58 | << "Failed for instruction " << instruction; 59 | ASSERT_EQ(end, expectedPosition.second) 60 | << "Failed for instruction " << instruction; 61 | } 62 | } 63 | 64 | /** 65 | * @test Test that an error is returned when trying to access the position of an 66 | * instruction with index larger than the total number of instructions. 67 | */ 68 | TEST_F(UtilityTest, BadInstructionPosition) { 69 | loadFromFile("complex-jumps"); 70 | 71 | size_t start = 0; 72 | size_t end = 0; 73 | ASSERT_EQ(state->getInstructionPosition(state, 100, &start, &end), ERROR); 74 | } 75 | 76 | } // namespace mqt::debugger::test 77 | -------------------------------------------------------------------------------- /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 20) 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.10 REQUIRED 47 | COMPONENTS Interpreter Development.Module 48 | OPTIONAL_COMPONENTS Development.SABIModule) 49 | endif() 50 | 51 | include(cmake/ExternalDependencies.cmake) 52 | 53 | # set prefix for all MQT Debugger targets 54 | set(MQT_DEBUGGER_TARGET_NAME mqt-debugger) 55 | 56 | # add main library code 57 | add_subdirectory(src) 58 | 59 | if(BUILD_MQT_DEBUGGER_BINDINGS) 60 | add_subdirectory(bindings) 61 | endif() 62 | 63 | # add test app 64 | if(BUILD_MQT_DEBUGGER_APP) 65 | add_subdirectory(app) 66 | endif() 67 | 68 | # add test code 69 | if(BUILD_MQT_DEBUGGER_TESTS) 70 | enable_testing() 71 | include(GoogleTest) 72 | add_subdirectory(test) 73 | endif() 74 | 75 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in 76 | ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake IMMEDIATE @ONLY) 77 | add_custom_target(uninstall-debugger COMMAND ${CMAKE_COMMAND} -P 78 | ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) 79 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /test/utils_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file utils_test.cpp 13 | * @brief Implementation of utility functions for testing. 14 | * 15 | * This file implements several utility functions that are used in the test 16 | * files. 17 | */ 18 | 19 | #include "utils/utils_test.hpp" 20 | 21 | #include "common.h" 22 | 23 | #include <filesystem> 24 | #include <fstream> 25 | #include <iostream> 26 | #include <iterator> 27 | #include <string> 28 | 29 | namespace mqt::debugger::test { 30 | 31 | bool complexEquality(const Complex& c, double real, double imaginary) { 32 | const double epsilon = 0.001; 33 | if (real - c.real > epsilon || c.real - real > epsilon) { 34 | return false; 35 | } 36 | if (imaginary - c.imaginary > epsilon || c.imaginary - imaginary > epsilon) { 37 | return false; 38 | } 39 | return true; 40 | } 41 | 42 | bool classicalEquals(const Variable& v, bool value) { 43 | return v.type == VarBool && v.value.boolValue == value; 44 | } 45 | 46 | std::string readFromCircuitsPath(const std::string& testName) { 47 | const std::filesystem::path localPath = 48 | std::filesystem::path("circuits") / (testName + std::string(".qasm")); 49 | std::ifstream file(localPath); 50 | if (!file.is_open()) { 51 | file = std::ifstream(std::filesystem::path("../../test/circuits") / 52 | (testName + std::string(".qasm"))); 53 | if (!file.is_open()) { 54 | std::cerr << "Could not open file\n"; 55 | file.close(); 56 | return ""; 57 | } 58 | } 59 | 60 | const std::string code((std::istreambuf_iterator<char>(file)), 61 | std::istreambuf_iterator<char>()); 62 | file.close(); 63 | return code; 64 | } 65 | 66 | namespace { 67 | /** 68 | * @brief Generate a string representation of a double without trailing zeros. 69 | * 70 | * @param d The double to convert to a string. 71 | * @return The string representation of the double. 72 | */ 73 | std::string doubleToStringTest(const double d) { 74 | auto string = std::to_string(d); 75 | while (string.back() == '0') { 76 | string.pop_back(); 77 | } 78 | if (string.back() == '.') { 79 | string.pop_back(); 80 | } 81 | return string; 82 | } 83 | } // namespace 84 | 85 | std::string complexToStringTest(const Complex& c) { 86 | const double epsilon = 0.0000001; 87 | if (c.imaginary < epsilon && c.imaginary > -epsilon) { 88 | return doubleToStringTest(c.real); 89 | } 90 | if (c.real < epsilon && c.real > -epsilon) { 91 | return doubleToStringTest(c.imaginary) + "i"; 92 | } 93 | return doubleToStringTest(c.real) + " + " + doubleToStringTest(c.imaginary) + 94 | "i"; 95 | } 96 | 97 | } // namespace mqt::debugger::test 98 | -------------------------------------------------------------------------------- /src/common/parsing/Utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file Utils.cpp 13 | * @brief Implementation of utility functions used by the debugger. 14 | */ 15 | 16 | #include "common/parsing/Utils.hpp" 17 | 18 | #include <algorithm> 19 | #include <cctype> 20 | #include <cstddef> 21 | #include <ranges> 22 | #include <string> 23 | #include <vector> 24 | 25 | namespace mqt::debugger { 26 | 27 | std::string trim(const std::string& str) { 28 | auto start = std::ranges::find_if_not(str, ::isspace); 29 | auto end = std::ranges::find_if_not(std::ranges::reverse_view(str), ::isspace) 30 | .base(); 31 | return (start < end) ? std::string(start, end) : std::string(); 32 | } 33 | 34 | std::vector<std::string> splitString(const std::string& text, char delimiter, 35 | bool includeEmpty) { 36 | const std::vector<char> delimiters{delimiter}; 37 | return splitString(text, delimiters, includeEmpty); 38 | } 39 | 40 | std::vector<std::string> splitString(const std::string& text, 41 | const std::vector<char>& delimiters, 42 | bool includeEmpty) { 43 | std::vector<std::string> result; 44 | size_t pos = 0; 45 | while (true) { 46 | size_t min = std::string ::npos; 47 | for (const auto del : delimiters) { 48 | const size_t newPos = text.find(del, pos); 49 | min = newPos < min ? newPos : min; 50 | } 51 | if (min == std::string::npos) { 52 | break; 53 | } 54 | if (min > pos || includeEmpty) { 55 | result.push_back(text.substr(pos, min - pos)); 56 | } 57 | pos = min + 1; 58 | } 59 | if (text.length() > pos || includeEmpty) { 60 | result.push_back(text.substr(pos, text.length() - pos)); 61 | } 62 | return result; 63 | } 64 | 65 | std::string replaceString(std::string str, const std::string& from, 66 | const std::string& to) { 67 | size_t startPos = 0; 68 | while ((startPos = str.find(from, startPos)) != std::string::npos) { 69 | str.replace(startPos, from.length(), to); 70 | startPos += to.length(); // Handles case where 'to' is a substring of 'from' 71 | } 72 | return str; 73 | } 74 | 75 | std::string removeWhitespace(std::string str) { 76 | std::erase_if(str, ::isspace); 77 | return str; 78 | } 79 | 80 | bool variablesEqual(const std::string& v1, const std::string& v2) { 81 | if (v1.find('[') != std::string::npos && v2.find('[') != std::string::npos) { 82 | return v1 == v2; 83 | } 84 | if (v1.find('[') != std::string::npos) { 85 | return variablesEqual(splitString(v1, '[')[0], v2); 86 | } 87 | if (v2.find('[') != std::string::npos) { 88 | return variablesEqual(splitString(v2, '[')[0], v1); 89 | } 90 | return v1 == v2; 91 | } 92 | 93 | std::string variableBaseName(const std::string& v) { 94 | return splitString(v, '[')[0]; 95 | } 96 | 97 | } // namespace mqt::debugger 98 | -------------------------------------------------------------------------------- /.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 | /python/**/_version.py 135 | 136 | # SKBuild cache dir 137 | _skbuild/ 138 | 139 | # Any build dirs in the tests 140 | test/**/build/ 141 | 142 | # Common editor files 143 | *~ 144 | *.swp 145 | 146 | # RPM spec file 147 | !/distro/*.spec 148 | /distro/*.tar.gz 149 | *.rpm 150 | 151 | # ruff 152 | .ruff_cache/ 153 | 154 | # OS specific stuff 155 | .DS_Store 156 | .DS_Store? 157 | ._* 158 | .Spotlight-V100 159 | .Trashes 160 | ehthumbs.db 161 | Thumbs.db 162 | 163 | .idea/ 164 | .vscode/ 165 | # tmt setup 166 | /distro/main.fmf 167 | /distro/plans/main.fmf 168 | /distro/tests/main.fmf 169 | experiments/ 170 | 171 | /docs/**/build 172 | .vs 173 | out/build 174 | 175 | node_modules/ 176 | wheelhouse/ 177 | 178 | .local-tools 179 | 180 | # test code for the CLIFrontEnd 181 | app/code/*.qasm 182 | 183 | .python-version 184 | 185 | .quick-commit-config.yaml 186 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /python/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 .change_amplitude_dap_message import AmplitudeChangeDAPMessage 15 | from .change_bit_dap_message import BitChangeDAPMessage 16 | from .configuration_done_dap_message import ConfigurationDoneDAPMessage 17 | from .continue_dap_message import ContinueDAPMessage 18 | from .dap_event import DAPEvent 19 | from .dap_message import DAPMessage 20 | from .disconnect_dap_message import DisconnectDAPMessage 21 | from .exception_info_message import ExceptionInfoDAPMessage 22 | from .exited_dap_event import ExitedDAPEvent 23 | from .gray_out_event import GrayOutDAPEvent 24 | from .initialize_dap_message import InitializeDAPMessage 25 | from .initialized_dap_event import InitializedDAPEvent 26 | from .launch_dap_message import LaunchDAPMessage 27 | from .next_dap_message import NextDAPMessage 28 | from .output_dap_event import OutputDAPEvent 29 | from .pause_dap_message import PauseDAPMessage 30 | from .restart_dap_message import RestartDAPMessage 31 | from .restart_frame_dap_message import RestartFrameDAPMessage 32 | from .reverse_continue_dap_message import ReverseContinueDAPMessage 33 | from .scopes_dap_message import ScopesDAPMessage 34 | from .set_breakpoints_dap_message import SetBreakpointsDAPMessage 35 | from .set_exception_breakpoints_dap_message import SetExceptionBreakpointsDAPMessage 36 | from .stack_trace_dap_message import StackTraceDAPMessage 37 | from .step_back_dap_message import StepBackDAPMessage 38 | from .step_in_dap_message import StepInDAPMessage 39 | from .step_out_dap_message import StepOutDAPMessage 40 | from .stopped_dap_event import StoppedDAPEvent, StopReason 41 | from .terminate_dap_message import TerminateDAPMessage 42 | from .terminated_dap_event import TerminatedDAPEvent 43 | from .threads_dap_message import ThreadsDAPMessage 44 | from .variables_dap_message import VariablesDAPMessage 45 | 46 | Request = DAPMessage 47 | 48 | __all__ = [ 49 | "AmplitudeChangeDAPMessage", 50 | "BitChangeDAPMessage", 51 | "CapabilitiesDAPEvent", 52 | "ConfigurationDoneDAPMessage", 53 | "ContinueDAPMessage", 54 | "DAPEvent", 55 | "DAPMessage", 56 | "DisconnectDAPMessage", 57 | "ExceptionInfoDAPMessage", 58 | "ExitedDAPEvent", 59 | "GrayOutDAPEvent", 60 | "InitializeDAPMessage", 61 | "InitializedDAPEvent", 62 | "LaunchDAPMessage", 63 | "NextDAPMessage", 64 | "OutputDAPEvent", 65 | "PauseDAPMessage", 66 | "Request", 67 | "RestartDAPMessage", 68 | "RestartFrameDAPMessage", 69 | "ReverseContinueDAPMessage", 70 | "ScopesDAPMessage", 71 | "SetBreakpointsDAPMessage", 72 | "SetExceptionBreakpointsDAPMessage", 73 | "StackTraceDAPMessage", 74 | "StepBackDAPMessage", 75 | "StepInDAPMessage", 76 | "StepOutDAPMessage", 77 | "StopReason", 78 | "StoppedDAPEvent", 79 | "TerminateDAPMessage", 80 | "TerminatedDAPEvent", 81 | "ThreadsDAPMessage", 82 | "VariablesDAPMessage", 83 | ] 84 | -------------------------------------------------------------------------------- /include/common/parsing/Utils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file Utils.hpp 13 | * @brief Contains utility functions for parsing and string manipulation. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include <string> 19 | #include <vector> 20 | 21 | namespace mqt::debugger { 22 | 23 | /** 24 | * @brief Removes leading and trailing whitespace from a string. 25 | * @param str The string to trim. 26 | * @return The trimmed string. 27 | */ 28 | std::string trim(const std::string& str); 29 | 30 | /** 31 | * @brief Splits a string into a vector of strings based on a delimiter. 32 | * @param text The text to split. 33 | * @param delimiter The delimiter to split on. 34 | * @param includeEmpty Whether to include empty strings in the result. 35 | * @return The vector of strings. 36 | */ 37 | std::vector<std::string> splitString(const std::string& text, char delimiter, 38 | bool includeEmpty = true); 39 | 40 | /** 41 | * @brief Splits a string into a vector of strings based on multiple delimiters. 42 | * 43 | * A split occurs when any of the delimiters are encountered. 44 | * 45 | * @param text The text to split. 46 | * @param delimiters The delimiters to split on. 47 | * @param includeEmpty Whether to include empty strings in the result. 48 | * @return The vector of strings. 49 | */ 50 | std::vector<std::string> splitString(const std::string& text, 51 | const std::vector<char>& delimiters, 52 | bool includeEmpty = true); 53 | 54 | /** 55 | * @brief Replaces all occurrences of a substring in a string with another 56 | * string. 57 | * @param str The string to modify. 58 | * @param from The substring to replace. 59 | * @param to The string to replace the substring with. 60 | * @return The modified string. 61 | */ 62 | std::string replaceString(std::string str, const std::string& from, 63 | const std::string& to); 64 | 65 | /** 66 | * @brief Removes all whitespace from a string. 67 | * 68 | * This includes spaces, tabs, and newlines. 69 | * 70 | * @param str The string to remove whitespace from. 71 | * @return The string with whitespace removed. 72 | */ 73 | std::string removeWhitespace(std::string str); 74 | 75 | /** 76 | * @brief Checks if two strings approximately refer to the same variable. 77 | * 78 | * If both strings refer to a given index of a register, they are equal if the 79 | * strings are exactly equal. If one string refers to a full register and the 80 | * other only to a single index, they are equal if they both refer to the same 81 | * register. 82 | * 83 | * @param v1 The first variable. 84 | * @param v2 The second variable. 85 | * @return True if the strings refer to the same variable, false otherwise. 86 | */ 87 | bool variablesEqual(const std::string& v1, const std::string& v2); 88 | 89 | /** 90 | * @brief Extracts the base name of a register. 91 | * 92 | * @param variable The variable to extract the base name from. 93 | * @return The base name of the register. 94 | */ 95 | std::string variableBaseName(const std::string& variable); 96 | 97 | } // namespace mqt::debugger 98 | -------------------------------------------------------------------------------- /docs/_templates/page.html: -------------------------------------------------------------------------------- 1 | {% extends "furo/page.html" %} {% block footer %} 2 | <div class="related-pages"> 3 | {% if next -%} 4 | <a class="next-page" href="{{ next.link }}"> 5 | <div class="page-info"> 6 | <div class="context"> 7 | <span>{{ _("Next") }}</span> 8 | </div> 9 | <div class="title">{{ next.title }}</div> 10 | </div> 11 | <svg class="furo-related-icon"> 12 | <use href="#svg-arrow-right"></use> 13 | </svg> 14 | </a> 15 | {%- endif %} {% if prev -%} 16 | <a class="prev-page" href="{{ prev.link }}"> 17 | <svg class="furo-related-icon"> 18 | <use href="#svg-arrow-right"></use> 19 | </svg> 20 | <div class="page-info"> 21 | <div class="context"> 22 | <span>{{ _("Previous") }}</span> 23 | </div> 24 | {% if prev.link == pathto(master_doc) %} 25 | <div class="title">{{ _("Home") }}</div> 26 | {% else %} 27 | <div class="title">{{ prev.title }}</div> 28 | {% endif %} 29 | </div> 30 | </a> 31 | {%- endif %} 32 | </div> 33 | <div class="acknowledgements"> 34 | The Munich Quantum Toolkit has been supported by the European Research Council 35 | (ERC) under the European Union's Horizon 2020 research and innovation program 36 | (grant agreement No. 101001318), the Bavarian State Ministry for Science and 37 | Arts through the Distinguished Professorship Program, as well as the Munich 38 | Quantum Valley, which is supported by the Bavarian state government with funds 39 | from the Hightech Agenda Bayern Plus. 40 | <div style="margin-top: 0.5em"> 41 | <div class="only-light" align="center"> 42 | <img 43 | src="https://raw.githubusercontent.com/munich-quantum-toolkit/.github/refs/heads/main/docs/_static/mqt-funding-footer-light.svg" 44 | width="90%" 45 | alt="MQT Funding Footer" 46 | /> 47 | </div> 48 | <div class="only-dark" align="center"> 49 | <img 50 | src="https://raw.githubusercontent.com/munich-quantum-toolkit/.github/refs/heads/main/docs/_static/mqt-funding-footer-dark.svg" 51 | width="90%" 52 | alt="MQT Funding Footer" 53 | /> 54 | </div> 55 | </div> 56 | </div> 57 | <div class="bottom-of-page"> 58 | <div class="left-details"> 59 | {%- if show_copyright %} 60 | <div class="copyright"> 61 | {%- if hasdoc('copyright') %} {% trans path=pathto('copyright'), 62 | copyright=copyright|e -%} 63 | <a href="{{ path }}">Copyright</a> © {{ copyright }} {%- endtrans %} 64 | {%- else %} {% trans copyright=copyright|e -%} Copyright © {{ 65 | copyright }} {%- endtrans %} {%- endif %} 66 | </div> 67 | {%- endif %} {%- if last_updated -%} 68 | <div class="last-updated"> 69 | {% trans last_updated=last_updated|e -%} Last updated on {{ last_updated 70 | }} {%- endtrans -%} 71 | </div> 72 | {%- endif %} 73 | </div> 74 | <div class="right-details"> 75 | {% if theme_footer_icons or READTHEDOCS -%} 76 | <div class="icons"> 77 | {% if theme_footer_icons -%} {% for icon_dict in theme_footer_icons -%} 78 | <a 79 | class="muted-link {{ icon_dict.class }}" 80 | href="{{ icon_dict.url }}" 81 | aria-label="{{ icon_dict.name }}" 82 | > 83 | {{- icon_dict.html -}} 84 | </a> 85 | {% endfor %} {%- endif %} {%- endif %} 86 | </div> 87 | </div> 88 | {% endblock footer %} 89 | </div> 90 | -------------------------------------------------------------------------------- /include/common/parsing/AssertionTools.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "AssertionParsing.hpp" 14 | #include "CodePreprocessing.hpp" 15 | 16 | #include <cstdint> 17 | #include <memory> 18 | 19 | namespace mqt::debugger { 20 | 21 | /** 22 | * @brief Define a general commutation rule. 23 | * 24 | * @param name The name of the commutation rule. 25 | * @param expression The expression that performs the check. 26 | */ 27 | #define COMMUTATION_RULE_GENERAL(name, expression) \ 28 | COMMUTATION_RULE_TEMPLATE(Assertion, name, expression) 29 | 30 | /** 31 | * @brief Define a commutation rule for entanglement assertions. 32 | * 33 | * @param name The name of the commutation rule. 34 | * @param expression The expression that performs the check. 35 | */ 36 | #define COMMUTATION_RULE_ENT(name, expression) \ 37 | COMMUTATION_RULE_TEMPLATE(EntanglementAssertion, name, expression) 38 | 39 | /** 40 | * @brief Define a commutation rule for superposition assertions. 41 | * 42 | * @param name The name of the commutation rule. 43 | * @param expression The expression that performs the check. 44 | */ 45 | #define COMMUTATION_RULE_SUP(name, expression) \ 46 | COMMUTATION_RULE_TEMPLATE(SuperpositionAssertion, name, expression) 47 | 48 | /** 49 | * @brief Define a template for commutation rules. 50 | * 51 | * @param type The type of assertion to check. 52 | * @param name The name of the commutation rule. 53 | * @param expression The expression that performs the check. 54 | */ 55 | #define COMMUTATION_RULE_TEMPLATE(type, name, expression) \ 56 | const auto name = [](const type* assertion, \ 57 | const std::string& instructionName, \ 58 | const std::vector<std::string>& arguments) { \ 59 | (void)assertion; \ 60 | (void)instructionName; \ 61 | (void)arguments; \ 62 | return expression; \ 63 | } 64 | 65 | /** 66 | * @brief The possible results of a commutation check. 67 | * 68 | * Can be either `Commutes`, `DoesNotCommute`, or `Unknown`. 69 | */ 70 | enum class CommutationResult : uint8_t { 71 | /** 72 | * @brief Indicates that the instructions commute with certainty. 73 | */ 74 | Commutes, 75 | /** 76 | * @brief Indicates that the instructions do not commute with certainty. 77 | */ 78 | DoesNotCommute, 79 | /** 80 | * @brief Indicates that it cannot be said with certainty whether the 81 | * instructions commute or not. 82 | */ 83 | Unknown, 84 | }; 85 | 86 | /** 87 | * @brief Check if an assertion commutes with an instruction. 88 | * @param assertion The assertion to check. 89 | * @param instruction The instruction to check. 90 | * @return True if the assertion commutes with the instruction, false otherwise. 91 | */ 92 | bool doesCommute(const std::unique_ptr<Assertion>& assertion, 93 | const Instruction& instruction); 94 | 95 | } // namespace mqt::debugger 96 | -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file common.h 13 | * @brief Common types and functions used by the debugger. 14 | * 15 | * This file contains declarations for several common types required for 16 | * quantum computation, such as complex numbers, state vectors, etc. 17 | */ 18 | 19 | #pragma once 20 | // NOLINTBEGIN(modernize-use-using, performance-enum-size) 21 | 22 | #ifndef __cplusplus 23 | #else 24 | #include <cstddef> 25 | #include <cstdint> 26 | extern "C" { 27 | #endif 28 | 29 | /** 30 | * @brief The result of an operation. 31 | * 32 | * Can be either `OK` or `ERROR`. 33 | */ 34 | typedef enum { 35 | /** 36 | * @brief Indicates that the operation was successful. 37 | */ 38 | OK, 39 | /** 40 | * @brief Indicates that an error occurred during the operation. 41 | */ 42 | ERROR, 43 | } Result; 44 | 45 | /** 46 | * @brief The type of classical variables. 47 | * 48 | * Supports Bool, Int, and Float. 49 | */ 50 | typedef enum { VarBool, VarInt, VarFloat } VariableType; 51 | 52 | /** 53 | * @biref Represents the value of a classical variable. 54 | */ 55 | typedef union { 56 | /** 57 | * @brief The value represented as a boolean. 58 | */ 59 | bool boolValue; 60 | /** 61 | * @brief The value represented as a integer. 62 | */ 63 | int intValue; 64 | /** 65 | * @brief The value represented as a floating point number. 66 | */ 67 | double floatValue; 68 | } VariableValue; 69 | 70 | /** 71 | * @brief Represents a classical variable. 72 | */ 73 | typedef struct { 74 | const char* name; 75 | VariableType type; 76 | VariableValue value; 77 | } Variable; 78 | 79 | /** 80 | * @brief Represents a complex number. 81 | */ 82 | typedef struct { 83 | /** 84 | * @brief The real component of the complex number. 85 | */ 86 | double real; 87 | /** 88 | * @brief The imaginary component of the complex number. 89 | */ 90 | double imaginary; 91 | } Complex; 92 | 93 | /** 94 | * @brief Represents a quantum statevector. 95 | * 96 | * The number of qubits is fixed and the number of states is 2^`numQubits`. 97 | */ 98 | typedef struct { 99 | /** 100 | * @brief The number of qubits in the statevector. 101 | */ 102 | size_t numQubits; 103 | /** 104 | * @brief The number of states in the statevector. 105 | * 106 | * This is equal to 2^`numQubits`. 107 | */ 108 | size_t numStates; 109 | /** 110 | * @brief An area of memory containing the amplitudes of the statevector. 111 | * 112 | * The allocated memory must be enough to store `numStates` Complex numbers. 113 | */ 114 | Complex* amplitudes; 115 | } Statevector; 116 | 117 | /** 118 | * @brief The settings that should be used to compile an assertion program. 119 | */ 120 | typedef struct { 121 | /** 122 | * @brief The optimization level that should be used. Exact meaning depends on 123 | * the implementation, but typically 0 means no optimization. 124 | */ 125 | uint8_t opt; 126 | /** 127 | * @brief The index of the slice that should be compiled. 128 | */ 129 | size_t sliceIndex; 130 | } CompilationSettings; 131 | 132 | #ifdef __cplusplus 133 | } 134 | #endif 135 | 136 | // NOLINTEND(modernize-use-using, performance-enum-size) 137 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /docs/quickstart.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | 3 | This documentation gives a quick overview on how to get started with MQT Debugger using: 4 | 5 | - The Python library 6 | - The DAP server 7 | - The CLI app 8 | 9 | ## Python Library 10 | 11 | The Python bindings are offered to give an easy start into using MQT Debugger. 12 | 13 | Working with the Python library of MQT Debugger requires several preparation steps: 14 | 15 | ```python 16 | import mqt.debugger as dbg 17 | ``` 18 | 19 | All main functionalities are included in the module {code}`mqt.debugger`, no other modules need to be imported. 20 | 21 | ```python 22 | state = dbg.create_ddsim_simulation_state() 23 | ``` 24 | 25 | The first step is to create a simulation state. The current implementation of MQT Debugger implements a single simulation backend based on 26 | decision diagrams from [MQT Core](https://github.com/munich-quantum-toolkit/core). This backend is instantiated by calling {code}`create_ddsim_simulation_state()`. 27 | 28 | ```python 29 | state.load_code(your_code) 30 | ``` 31 | 32 | Before running the debugger, a quantum program must be loaded into the state. This is done by calling {code}`SimulationState.load_code()` with the quantum program as a string argument. 33 | Currently, the supported quantum program format is [QASM 2.0](https://arxiv.org/abs/1707.03429). 34 | 35 | After this setup is done, the debugging process can be started by stepping through the code one instruction at a time: 36 | 37 | ```python 38 | state.step_forward() 39 | ``` 40 | 41 | or by running the full simulation: 42 | 43 | ```python 44 | state.run_simulation() 45 | ``` 46 | 47 | In the second case, simulation will be run until the program ends, a failing assertion is encountered, or a breakpoint is reached. 48 | Further details on how to step through the program can be found in the [reference documentation](library/Library.md). 49 | 50 | Breakpoints can be set using 51 | 52 | ```python 53 | state.set_breakpoint(character_index) 54 | ``` 55 | 56 | where {code}`character_index` is the index of the character in the original code's string, at which the breakpoint should be set. 57 | 58 | Assertions can be added to the code following the {doc}`assertion syntax <assertions>` and are automatically evaluated. 59 | 60 | 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: 61 | 62 | ```python 63 | diagnostics = state.get_diagnostics() 64 | ``` 65 | 66 | Then, the potential error causes can be retrieved: 67 | 68 | ```python 69 | problems = diagnostics.potential_error_causes() 70 | print(problems) 71 | ``` 72 | 73 | ## DAP Server 74 | 75 | This library provides a DAP Server that can be connected to from existing IDEs like Visual Studio Code or CLion. 76 | 77 | It can be started by calling 78 | 79 | ```console 80 | $ python3 -m mqt.debugger.dap.adapter 81 | ``` 82 | 83 | The server will then start on port 4711 and can accept one single connection from debugging clients. 84 | 85 | ```{note} 86 | Connecting to the server requires a client compatible with the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/). 87 | While most common IDEs already support it by default, some additional setup or extensions may be required to allow communication with arbitrary clients. 88 | ``` 89 | 90 | The DAP Server provides all simulation methods that are accessible via the Python library. 91 | On assertion failures, the server will automatically pause the simulation and send a message to the client containing possible error causes. 92 | 93 | ## CLI App 94 | 95 | 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 96 | and thus does not provide all the features of the framework or full accessibility through CLI parameters. 97 | 98 | Instead, the CLI app will open an OpenQASM file with the name {code}`program.qasm` in the current working directory and start the debugging process for it. 99 | -------------------------------------------------------------------------------- /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 | from typing import TYPE_CHECKING 16 | 17 | from mqt.debugger import ( 18 | ErrorCauseType, 19 | create_ddsim_simulation_state, 20 | destroy_ddsim_simulation_state, 21 | ) 22 | 23 | if TYPE_CHECKING: 24 | from mqt.debugger import SimulationState 25 | 26 | 27 | def load_instance(name: str) -> SimulationState: 28 | """Loads a quantum simulation and debugging instance from a file. 29 | 30 | Args: 31 | name (str): The name of the instance to load. 32 | 33 | Returns: 34 | SimulationState: The generated simulation state. 35 | """ 36 | state = create_ddsim_simulation_state() 37 | with Path(f"test/python/resources/diagnosis/{name}.qasm").open(encoding=locale.getpreferredencoding(False)) as f: 38 | state.load_code(f.read()) 39 | return state 40 | 41 | 42 | def test_data_dependencies() -> None: 43 | """Test the data dependency analysis.""" 44 | s = load_instance("dependencies") 45 | dependencies = s.get_diagnostics().get_data_dependencies(6) 46 | assert dependencies == [1, 2, 3, 6] 47 | dependencies = s.get_diagnostics().get_data_dependencies(7) 48 | assert dependencies == [1, 2, 3, 4, 5, 7] 49 | destroy_ddsim_simulation_state(s) 50 | 51 | 52 | def test_data_dependencies_jumps() -> None: 53 | """Test the data dependency analysis in code with jumps.""" 54 | s = load_instance("dependencies-with-jumps") 55 | dependencies = s.get_diagnostics().get_data_dependencies(4) 56 | assert dependencies == [2, 3, 4] 57 | dependencies = s.get_diagnostics().get_data_dependencies(9) 58 | assert dependencies == [2, 3, 4, 6, 7, 8, 9] 59 | destroy_ddsim_simulation_state(s) 60 | 61 | 62 | def test_control_always_zero() -> None: 63 | """Test the control-always-zero error diagnosis.""" 64 | s = load_instance("control-always-zero") 65 | s.run_simulation() 66 | causes = s.get_diagnostics().potential_error_causes() 67 | 68 | assert len(causes) == 2 69 | 70 | assert causes[0].type == ErrorCauseType.ControlAlwaysZero 71 | assert causes[0].instruction == 3 72 | 73 | assert causes[1].type == ErrorCauseType.ControlAlwaysZero 74 | assert causes[1].instruction == 12 75 | 76 | 77 | def test_missing_interaction() -> None: 78 | """Test the missing-interaction error diagnosis.""" 79 | s = load_instance("missing-interaction") 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) == 0 83 | s.run_simulation() 84 | causes = [x for x in s.get_diagnostics().potential_error_causes() if x.type == ErrorCauseType.MissingInteraction] 85 | assert len(causes) == 1 86 | assert causes[0].instruction == 4 87 | 88 | 89 | def test_zero_control_listing() -> None: 90 | """Test the zero-control list.""" 91 | s = load_instance("control-always-zero") 92 | s.run_simulation() 93 | zero_controls = s.get_diagnostics().get_zero_control_instructions() 94 | assert zero_controls == [3, 12] 95 | 96 | 97 | def test_data_dependencies_with_callers() -> None: 98 | """Tests the data dependency analysis with enabling callers.""" 99 | s = load_instance("data-dependencies-with-callers") 100 | s.run_simulation() 101 | dependencies = s.get_diagnostics().get_data_dependencies(2, include_callers=True) 102 | assert dependencies == [2, 4, 6, 8, 9] 103 | 104 | dependencies = s.get_diagnostics().get_data_dependencies(7, include_callers=True) 105 | assert dependencies == [2, 4, 6, 7] 106 | # 8 and 9 are not included `test` doesn't have unknown callers in this case, so the analysis won't include all callers. 107 | -------------------------------------------------------------------------------- /python/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 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Welcome to MQT Debugger's documentation! 2 | 3 | MQT Debugger is a comprehensive framework for debugging and analyzing the behaviour of quantum programs. 4 | It is part of the _{doc}`Munich Quantum Toolkit (MQT) <mqt:index>`_. 5 | 6 | The framework provides tools for running quantum programs in a simulated environment, stepping through the code instruction-by-instruction, and displaying the quantum state at each step. 7 | It also allows developers to include assertions in their code, testing the correctness of provided programs, and, when an assertion fails, suggesting possible causes of the failure. 8 | 9 | MQT Debugger is accessible as a stand-alone application, as a C++ and Python library to include in custom programs, and as a DAP server that can be accessed by popular IDEs such as Visual Studio Code and CLion. 10 | 11 | We recommend you to start with the {doc}`installation instructions <installation>`. 12 | Then proceed to the {doc}`quickstart guide <quickstart>` and read the {doc}`reference documentation <library/Library>`. 13 | If you are interested in the theory behind MQT Debugger, have a look at the publications in the {doc}`publication list <references>`. 14 | 15 | We appreciate any feedback and contributions to the project. 16 | If you want to contribute, you can find more information in the {doc}`contribution guide <contributing>`. 17 | If you are having trouble with the installation or the usage of MQT Debugger, please let us know on our {doc}`support page <support>`. 18 | 19 | ```{toctree} 20 | :hidden: 21 | 22 | self 23 | ``` 24 | 25 | ```{toctree} 26 | :caption: User Guide 27 | :glob: 28 | :hidden: 29 | :maxdepth: 1 30 | 31 | installation 32 | quickstart 33 | debugging 34 | assertions 35 | diagnosis 36 | assertion_refinement 37 | runtime_verification 38 | references 39 | ``` 40 | 41 | ```{toctree} 42 | :caption: Developers 43 | :glob: 44 | :hidden: 45 | :maxdepth: 1 46 | 47 | contributing 48 | support 49 | ``` 50 | 51 | ```{toctree} 52 | :caption: API Reference 53 | :glob: 54 | :hidden: 55 | :maxdepth: 6 56 | 57 | library/Library 58 | ``` 59 | 60 | ## Contributors and Supporters 61 | 62 | The _[Munich Quantum Toolkit (MQT)](https://mqt.readthedocs.io)_ is developed by the [Chair for Design Automation](https://www.cda.cit.tum.de/) at the [Technical University of Munich](https://www.tum.de/) and supported by the [Munich Quantum Software Company (MQSC)](https://munichquantum.software). 63 | Among others, it is part of the [Munich Quantum Software Stack (MQSS)](https://www.munich-quantum-valley.de/research/research-areas/mqss) ecosystem, which is being developed as part of the [Munich Quantum Valley (MQV)](https://www.munich-quantum-valley.de) initiative. 64 | 65 | <div style="margin-top: 0.5em"> 66 | <div class="only-light" align="center"> 67 | <img src="https://raw.githubusercontent.com/munich-quantum-toolkit/.github/refs/heads/main/docs/_static/mqt-logo-banner-light.svg" width="90%" alt="MQT Banner"> 68 | </div> 69 | <div class="only-dark" align="center"> 70 | <img src="https://raw.githubusercontent.com/munich-quantum-toolkit/.github/refs/heads/main/docs/_static/mqt-logo-banner-dark.svg" width="90%" alt="MQT Banner"> 71 | </div> 72 | </div> 73 | 74 | Thank you to all the contributors who have helped make MQT Debugger a reality! 75 | 76 | <p align="center"> 77 | <a href="https://github.com/munich-quantum-toolkit/debugger/graphs/contributors"> 78 | <img src="https://contrib.rocks/image?repo=munich-quantum-toolkit/debugger" /> 79 | </a> 80 | </p> 81 | 82 | The MQT will remain free, open-source, and permissively licensed—now and in the future. 83 | We are firmly committed to keeping it open and actively maintained for the quantum computing community. 84 | 85 | To support this endeavor, please consider: 86 | 87 | - Starring and sharing our repositories: [https://github.com/munich-quantum-toolkit](https://github.com/munich-quantum-toolkit) 88 | - Contributing code, documentation, tests, or examples via issues and pull requests 89 | - Citing the MQT in your publications (see {doc}`References <references>`) 90 | - Using the MQT in research and teaching, and sharing feedback and use cases 91 | - Sponsoring us on GitHub: [https://github.com/sponsors/munich-quantum-toolkit](https://github.com/sponsors/munich-quantum-toolkit) 92 | 93 | <p align="center"> 94 | <iframe src="https://github.com/sponsors/munich-quantum-toolkit/button" title="Sponsor munich-quantum-toolkit" height="32" width="114" style="border: 0; border-radius: 6px;"></iframe> 95 | </p> 96 | -------------------------------------------------------------------------------- /include/common/ComplexMathematics.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024 - 2025 Chair for Design Automation, TUM 3 | * Copyright (c) 2025 Munich Quantum Software Company GmbH 4 | * All rights reserved. 5 | * 6 | * SPDX-License-Identifier: MIT 7 | * 8 | * Licensed under the MIT License 9 | */ 10 | 11 | /** 12 | * @file ComplexMathematics.hpp 13 | * @brief Provides maths methods for complex numbers. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include "common.h" 19 | 20 | #include <Eigen/Dense> 21 | #include <cstddef> 22 | #include <string> 23 | #include <vector> 24 | 25 | namespace mqt::debugger { 26 | 27 | /** 28 | * @brief Compute the magnitude of a complex number. 29 | * @param c The complex number. 30 | * @return The computed magnitude. 31 | */ 32 | double complexMagnitude(Complex& c); 33 | 34 | /** 35 | * @brief Add two complex numbers. 36 | * @param c1 The first complex number. 37 | * @param c2 The second complex number. 38 | * @return The sum of the two complex numbers. 39 | */ 40 | Complex complexAddition(const Complex& c1, const Complex& c2); 41 | 42 | /** 43 | * @brief Multiply two complex numbers. 44 | * @param c1 The first complex number. 45 | * @param c2 The second complex number. 46 | * @return The product of the two complex numbers. 47 | */ 48 | Complex complexMultiplication(const Complex& c1, const Complex& c2); 49 | 50 | /** 51 | * @brief Compute the complex conjugate of a complex number. 52 | * @param c The complex number. 53 | * @return The complex conjugate of the input complex number. 54 | */ 55 | Complex complexConjugate(const Complex& c); 56 | 57 | /** 58 | * @brief Check if two qubits are entangled in a given density matrix. 59 | * 60 | * This is done by tracing out all other qubits from a density matrix and then 61 | * checking whether the shared information is greater than 0. 62 | * @param densityMatrix The density matrix to check for entanglement. 63 | * @param qubit1 The first qubit to check. 64 | * @param qubit2 The second qubit to check. 65 | * @return True if the qubits are entangled, false otherwise. 66 | */ 67 | bool areQubitsEntangled(std::vector<std::vector<Complex>>& densityMatrix, 68 | size_t qubit1, size_t qubit2); 69 | 70 | /** 71 | * @brief Translate a given statevector to a density matrix. 72 | * 73 | * @param sv The statevector to translate. 74 | * @return The computed density matrix. 75 | */ 76 | std::vector<std::vector<Complex>> toDensityMatrix(const Statevector& sv); 77 | 78 | /** 79 | * @brief Check if the partial trace of a given state vector is pure. 80 | * 81 | * This is true if and only if the trace of the square of the traced out density 82 | * matrix is equal to 1. 83 | * @param sv The state vector to check. 84 | * @param traceOut The indices of the qubits to trace out. 85 | * @return True if the partial trace is pure, false otherwise. 86 | */ 87 | bool partialTraceIsPure(const Statevector& sv, 88 | const std::vector<size_t>& traceOut); 89 | 90 | /** 91 | * @brief Gets the partial state vector by tracing out individual qubits from 92 | * the full state vector. 93 | * @param sv The full state vector. 94 | * @param traceOut The indices of the qubits to trace out. 95 | * @return The partial state vector. 96 | */ 97 | std::vector<std::vector<Complex>> 98 | getPartialTraceFromStateVector(const Statevector& sv, 99 | const std::vector<size_t>& traceOut); 100 | 101 | /** 102 | * @brief Convert a given vector-of-vectors matrix to an Eigen3 matrix. 103 | * @param matrix The vector-of-vectors matrix to convert. 104 | * @return A new Eigen3 matrix. 105 | */ 106 | Eigen::MatrixXcd // NOLINT 107 | toEigenMatrix(const std::vector<std::vector<Complex>>& matrix); 108 | 109 | /** 110 | * @brief Compute the amplitudes of a given state vector's sub-state. 111 | * @param sv The state vector to compute the sub-state from. 112 | * @param qubits The indices of the qubits to include in the sub-state. 113 | * @return The computed sub-state vector amplitudes. 114 | */ 115 | std::vector<Complex> 116 | getSubStateVectorAmplitudes(const Statevector& sv, 117 | const std::vector<size_t>& qubits); 118 | 119 | /** 120 | * @brief Generate a string representation of a complex number. 121 | * @param c The complex number. 122 | * @return The string representation of the complex number. 123 | */ 124 | std::string complexToString(const Complex& c); 125 | 126 | /** 127 | * @brief Compute the dot product of two state vectors. 128 | * @param sv1 The first state vector. 129 | * @param sv2 The second state vector. 130 | * @return The computed dot product. 131 | */ 132 | double dotProduct(const Statevector& sv1, const Statevector& sv2); 133 | 134 | } // namespace mqt::debugger 135 | -------------------------------------------------------------------------------- /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 3.0.1 CONFIG REQUIRED) 26 | endif() 27 | 28 | # ---------------------------------------------------------------------------------Fetch MQT Core 29 | # cmake-format: off 30 | set(MQT_CORE_MINIMUM_VERSION 3.3.1 31 | CACHE STRING "MQT Core minimum version") 32 | set(MQT_CORE_VERSION 3.3.3 33 | CACHE STRING "MQT Core version") 34 | set(MQT_CORE_REV "8c9f6ab24968401e450812fc0ff7d05b5ae07a63" 35 | CACHE STRING "MQT Core identifier (tag, branch or commit hash)") 36 | set(MQT_CORE_REPO_OWNER "munich-quantum-toolkit" 37 | CACHE STRING "MQT Core repository owner (change when using a fork)") 38 | # cmake-format: on 39 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) 40 | # Fetch MQT Core 41 | FetchContent_Declare( 42 | mqt-core 43 | GIT_REPOSITORY https://github.com/${MQT_CORE_REPO_OWNER}/core.git 44 | GIT_TAG ${MQT_CORE_REV}) 45 | list(APPEND FETCH_PACKAGES mqt-core) 46 | else() 47 | find_package(mqt-core ${MQT_CORE_MINIMUM_VERSION} QUIET) 48 | if(NOT mqt-core_FOUND) 49 | FetchContent_Declare( 50 | mqt-core 51 | GIT_REPOSITORY https://github.com/${MQT_CORE_REPO_OWNER}/core.git 52 | GIT_TAG ${MQT_CORE_REV}) 53 | list(APPEND FETCH_PACKAGES mqt-core) 54 | endif() 55 | endif() 56 | 57 | # ---------------------------------------------------------------------------------Fetch Eigen3 58 | # cmake-format: off 59 | set(EIGEN_VERSION 3.4.0 60 | CACHE STRING "Eigen3 version") 61 | # cmake-format: on 62 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) 63 | # Fetch Eigen3 64 | FetchContent_Declare( 65 | Eigen3 66 | GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git 67 | GIT_TAG ${EIGEN_VERSION} 68 | GIT_SHALLOW TRUE) 69 | list(APPEND FETCH_PACKAGES Eigen3) 70 | set(EIGEN_BUILD_TESTING 71 | OFF 72 | CACHE BOOL "Disable testing for Eigen") 73 | set(BUILD_TESTING 74 | OFF 75 | CACHE BOOL "Disable general testing") 76 | set(EIGEN_BUILD_DOC 77 | OFF 78 | CACHE BOOL "Disable documentation build for Eigen") 79 | else() 80 | find_package(Eigen3 ${EIGEN3_VERSION} REQUIRED NO_MODULE) 81 | if(NOT Eigen3_FOUND) 82 | FetchContent_Declare( 83 | Eigen3 84 | GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git 85 | GIT_TAG ${EIGEN3_VERSION} 86 | GIT_SHALLOW TRUE) 87 | list(APPEND FETCH_PACKAGES Eigen3) 88 | set(EIGEN_BUILD_TESTING 89 | OFF 90 | CACHE BOOL "Disable testing for Eigen") 91 | set(BUILD_TESTING 92 | OFF 93 | CACHE BOOL "Disable general testing") 94 | set(EIGEN_BUILD_DOC 95 | OFF 96 | CACHE BOOL "Disable documentation build for Eigen") 97 | endif() 98 | endif() 99 | 100 | if(BUILD_MQT_DEBUGGER_TESTS) 101 | set(gtest_force_shared_crt 102 | ON 103 | CACHE BOOL "" FORCE) 104 | set(GTEST_VERSION 105 | 1.17.0 106 | CACHE STRING "Google Test version") 107 | set(GTEST_URL https://github.com/google/googletest/archive/refs/tags/v${GTEST_VERSION}.tar.gz) 108 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) 109 | FetchContent_Declare(googletest URL ${GTEST_URL} FIND_PACKAGE_ARGS ${GTEST_VERSION} NAMES GTest) 110 | list(APPEND FETCH_PACKAGES googletest) 111 | else() 112 | find_package(googletest ${GTEST_VERSION} QUIET NAMES GTest) 113 | if(NOT googletest_FOUND) 114 | FetchContent_Declare(googletest URL ${GTEST_URL}) 115 | list(APPEND FETCH_PACKAGES googletest) 116 | endif() 117 | endif() 118 | endif() 119 | 120 | if(BUILD_MQT_DEBUGGER_BINDINGS) 121 | # add pybind11_json library 122 | if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24) 123 | FetchContent_Declare( 124 | pybind11_json 125 | GIT_REPOSITORY https://github.com/pybind/pybind11_json 126 | FIND_PACKAGE_ARGS) 127 | list(APPEND FETCH_PACKAGES pybind11_json) 128 | else() 129 | find_package(pybind11_json QUIET) 130 | if(NOT pybind11_json_FOUND) 131 | FetchContent_Declare(pybind11_json GIT_REPOSITORY https://github.com/pybind/pybind11_json) 132 | list(APPEND FETCH_PACKAGES pybind11_json) 133 | endif() 134 | endif() 135 | endif() 136 | 137 | # Make all declared dependencies available. 138 | FetchContent_MakeAvailable(${FETCH_PACKAGES}) 139 | 140 | # Hide Eigen3 warnings 141 | get_target_property(Eigen3_Includes Eigen3::Eigen INTERFACE_INCLUDE_DIRECTORIES) 142 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # To run all pre-commit checks, use: 2 | # 3 | # pre-commit run -a 4 | # 5 | # To install pre-commit hooks that run every time you commit: 6 | # 7 | # pre-commit install 8 | # 9 | 10 | ci: 11 | autoupdate_commit_msg: "⬆️🪝 update pre-commit hooks" 12 | autoupdate_schedule: quarterly 13 | autofix_commit_msg: "🎨 pre-commit fixes" 14 | skip: [mypy] 15 | 16 | repos: 17 | # Standard hooks 18 | - repo: https://github.com/pre-commit/pre-commit-hooks 19 | rev: v6.0.0 20 | hooks: 21 | - id: check-added-large-files 22 | args: ["--maxkb=2048"] 23 | - id: check-case-conflict 24 | - id: check-vcs-permalinks 25 | - id: check-merge-conflict 26 | - id: check-symlinks 27 | - id: check-json 28 | - id: check-toml 29 | - id: check-yaml 30 | - id: debug-statements 31 | - id: end-of-file-fixer 32 | - id: mixed-line-ending 33 | - id: trailing-whitespace 34 | 35 | # Clean jupyter notebooks 36 | - repo: https://github.com/srstevenson/nb-clean 37 | rev: 4.0.1 38 | hooks: 39 | - id: nb-clean 40 | args: 41 | - --remove-empty-cells 42 | - --preserve-cell-metadata 43 | - raw_mimetype 44 | - -- 45 | 46 | # Handling unwanted unicode characters 47 | - repo: https://github.com/sirosen/texthooks 48 | rev: 0.7.1 49 | hooks: 50 | - id: fix-ligatures 51 | - id: fix-smartquotes 52 | 53 | # Check for common mistakes 54 | - repo: https://github.com/pre-commit/pygrep-hooks 55 | rev: v1.10.0 56 | hooks: 57 | - id: rst-backticks 58 | - id: rst-directive-colons 59 | - id: rst-inline-touching-normal 60 | 61 | # Ensure uv lock file is up-to-date 62 | - repo: https://github.com/astral-sh/uv-pre-commit 63 | rev: 0.9.17 64 | hooks: 65 | - id: uv-lock 66 | 67 | # Python linting using ruff 68 | - repo: https://github.com/astral-sh/ruff-pre-commit 69 | rev: v0.14.9 70 | hooks: 71 | - id: ruff-check 72 | - id: ruff-format 73 | 74 | # Static type checking using mypy 75 | - repo: https://github.com/pre-commit/mirrors-mypy 76 | rev: v1.19.0 77 | hooks: 78 | - id: mypy 79 | files: ^(python/mqt|test/python|noxfile.py) 80 | args: [] 81 | additional_dependencies: 82 | - nox 83 | - numpy 84 | - pytest 85 | 86 | # Also run Black on examples in the documentation 87 | - repo: https://github.com/adamchainz/blacken-docs 88 | rev: 1.20.0 89 | hooks: 90 | - id: blacken-docs 91 | additional_dependencies: [black==25.*] 92 | 93 | # Check for license headers 94 | - repo: https://github.com/emzeat/mz-lictools 95 | rev: v2.9.0 96 | hooks: 97 | - id: license-tools 98 | 99 | # Clang-format the C++ part of the code base automatically 100 | - repo: https://github.com/pre-commit/mirrors-clang-format 101 | rev: v21.1.7 102 | hooks: 103 | - id: clang-format 104 | types_or: [c++, c, cuda] 105 | 106 | # CMake format and lint the CMakeLists.txt files 107 | - repo: https://github.com/cheshirekow/cmake-format-precommit 108 | rev: v0.6.13 109 | hooks: 110 | - id: cmake-format 111 | additional_dependencies: [pyyaml] 112 | types: [file] 113 | files: (\.cmake|CMakeLists.txt)(.in)?$ 114 | 115 | # Format configuration files with prettier 116 | - repo: https://github.com/rbubley/mirrors-prettier 117 | rev: v3.7.4 118 | hooks: 119 | - id: prettier 120 | types_or: [yaml, markdown, html, css, scss, javascript, json] 121 | 122 | # Check for spelling 123 | - repo: https://github.com/crate-ci/typos 124 | rev: v1.40.0 125 | hooks: 126 | - id: typos 127 | 128 | # Catch common capitalization mistakes 129 | - repo: local 130 | hooks: 131 | - id: disallow-caps 132 | name: Disallow improper capitalization 133 | language: pygrep 134 | entry: PyBind|Numpy|Cmake|CCache|Github|PyTest|Mqt|Tum|MQTopt|MQTref 135 | exclude: .pre-commit-config.yaml 136 | 137 | # Check best practices for scientific Python code 138 | - repo: https://github.com/scientific-python/cookie 139 | rev: 2025.11.21 140 | hooks: 141 | - id: sp-repo-review 142 | additional_dependencies: ["repo-review[cli]"] 143 | 144 | # Check JSON schemata 145 | - repo: https://github.com/python-jsonschema/check-jsonschema 146 | rev: 0.35.0 147 | hooks: 148 | - id: check-dependabot 149 | - id: check-github-workflows 150 | - id: check-readthedocs 151 | 152 | # Check the pyproject.toml file 153 | - repo: https://github.com/henryiii/validate-pyproject-schema-store 154 | rev: 2025.11.21 155 | hooks: 156 | - id: validate-pyproject 157 | 158 | # Tidy up BibTeX files 159 | - repo: https://github.com/FlamingTempura/bibtex-tidy 160 | rev: v1.14.0 161 | hooks: 162 | - id: bibtex-tidy 163 | args: 164 | [ 165 | "--align=20", 166 | "--curly", 167 | "--months", 168 | "--blank-lines", 169 | "--sort", 170 | "--strip-enclosing-braces", 171 | "--sort-fields", 172 | "--trailing-commas", 173 | "--remove-empty-fields", 174 | ] 175 | -------------------------------------------------------------------------------- /docs/assertions.md: -------------------------------------------------------------------------------- 1 | # Assertions 2 | 3 | This document describes the syntax and semantics of assertions in MQT Debugger. 4 | Assertions are a useful tool to test the correctness of programs by comparing the current state of the system to an expected state. 5 | The assertions in MQT Debugger allow developers to test either the exact or approximate state or certain properties, such as entanglement or superposition. 6 | 7 | The following sections will give an overview of the assertion syntax and the different types of assertions that can be used in MQT Debugger. 8 | 9 | ```{note} 10 | The targets of an assertion can be either individual qubits or full quantum registers. However, when the selected targets are a subset of the full quantum state, 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 as state vectors are not well-defined if they are entangled with other qubits. 11 | ``` 12 | 13 | ## Entanglement Assertion 14 | 15 | 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 entanglement must exist between each possible pair. 16 | 17 | **QASM Syntax**: 18 | 19 | ```qasm 20 | assert-ent <target_qubit_1> [, <target_qubit_2> ...]*; 21 | ``` 22 | 23 | _Example_: 24 | 25 | ```qasm 26 | assert-ent q[0], q[1], q[2]; 27 | ``` 28 | 29 | This assertion checks for entanglement between qubits {code}`q[0]` and {code}`q[1]`, {code}`q[0]` and {code}`q[2]`, as well as {code}`q[1]` and {code}`q[2]`. 30 | 31 | An example for a quantum state that would pass this assertion is the GHZ state {math}`\frac{1}{\sqrt{2}}(|000\rangle + |111\rangle)`. 32 | As none of the individual qubits is separable, this state is entangled. 33 | 34 | An example for a quantum state that would fail this assertion is the state {math}`\frac{1}{\sqrt{2}}(|000\rangle + |110\rangle)`. 35 | As the least-significant qubit is separable, this state is not entangled. 36 | 37 | ## Superposition Assertion 38 | 39 | Superposition assertions check whether a set of qubits is in a superposition state. 40 | 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. 41 | 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. 42 | 43 | **QASM Syntax**: 44 | 45 | ```qasm 46 | assert-sup <target_qubit_1> [, <target_qubit_2> ...]*; 47 | ``` 48 | 49 | _Example_: 50 | 51 | ```qasm 52 | assert-sup q[0], q[1], q[2]; 53 | ``` 54 | 55 | This assertion checks for superposition of qubits {code}`q[0]`, {code}`q[1]`, and {code}`q[2]`. 56 | 57 | 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`. 58 | As two basis states have non-zero amplitudes ({math}`|00\rangle` and {math}`|10\rangle`), this state is in a superposition. 59 | 60 | An example for a quantum state that would fail this assertion is the state {math}`|00\rangle`. 61 | In this case, only a single state ({math}`|00\rangle`) has a non-zero amplitude, so the state is not in a superposition. 62 | 63 | ## Equality Assertion 64 | 65 | Equality assertions compare the state of a set of qubits to a given state and fail if the states are not equal. 66 | Furthermore, a similarity threshold can be passed to the assertion, allowing for approximate comparisons. The similarity is computed through the 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. 67 | 68 | The target state to compare to can be expressed as a state vector or as a new quantum circuit. 69 | 70 | **QASM Syntax**: 71 | 72 | ```qasm 73 | assert-eq [similarity], <target_qubit_1> [, <target_qubit_2> ...]* { STATE_REPRESENTATION } 74 | ``` 75 | 76 | ```qasm 77 | STATE_REPRESENTATION = 78 | | <STATEVECTOR> 79 | | <CIRCUIT> 80 | 81 | STATEVECTOR = <amplitude_1> [, <amplitude_2> ...]* 82 | CIRCUIT = <QASM_CODE> 83 | ``` 84 | 85 | 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 and use $2^n$ amplitudes. Amplitudes can be real or complex numbers using $i$ or $j$ for the imaginary unit. 86 | 87 | 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 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. 88 | 89 | _Example_: 90 | 91 | ```qasm 92 | assert-eq 0.9, q[0], q[1] { 0.5, 0.5, 0.5, 0.5 }; 93 | ``` 94 | 95 | This assertion checks whether the state of qubits {code}`q[0]` and {code}`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. 96 | 97 | ```qasm 98 | assert-eq q[0], q[1] { 99 | h q[0]; 100 | cx q[0], q[1]; 101 | }; 102 | ``` 103 | 104 | This assertion checks whether the state of qubits {code}`q[0]` and {code}`q[1]` is equal to the bell state {math}`\frac{1}{\sqrt{2}}(|00\rangle + |11\rangle)`. 105 | -------------------------------------------------------------------------------- /docs/diagnosis.md: -------------------------------------------------------------------------------- 1 | # Diagnosis Methods 2 | 3 | This document describes the diagnostics methods available in MQT Debugger. 4 | The diagnostics methods can be used to analyze the state of the system, in particular to find potential error causes when an {doc}`assertion <Assertions>` fails. 5 | 6 | All of these methods require a {py:class}`Diagnostics <mqt.debugger.Diagnostics>` object to be executed, which can be obtained from the {py:class}`SimulationState <mqt.debugger.SimulationState>` object using {cpp:member}`SimulationState::getDiagnostics <SimulationStateStruct::getDiagnostics>`/{py:meth}`SimulationState.get_diagnostics <mqt.debugger.SimulationState.get_diagnostics>`. 7 | 8 | ## Error Cause Analysis 9 | 10 | When an assertion fails, several methods can be used to find potential error causes. 11 | 12 | For further information, please refer to {cite:p}`rovara2024debugging`. 13 | 14 | ### Cone of Influence Analysis 15 | 16 | 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. 17 | 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 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 to find potential error causes more efficiently. 18 | 19 | The cone of influence can be obtained using {cpp:member}`Diagnostics::getDataDependencies <DiagnosticsStruct::getDataDependencies>`/{py:meth}`Diagnostics.get_data_dependencies <mqt.debugger.Diagnostics.get_data_dependencies>`. 20 | 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. 21 | 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 {code}`true` if the instruction is part of the cone of influence. 22 | 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 inside a custom gate definition, it will only include data dependencies within the gate. 23 | 24 | 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. 25 | 26 | ### Interaction Analysis 27 | 28 | Interaction Analysis is a method to find potential error causes by analyzing the interactions between qubits in the system. 29 | It is automatically called when using {cpp:member}`Diagnostics::potentialErrorCauses <DiagnosticsStruct::potentialErrorCauses>`/{py:meth}`Diagnostics.potential_error_causes <mqt.debugger.Diagnostics.potential_error_causes>`. 30 | 31 | This analysis method can be used to find reasons for failing entanglement assertions: 32 | Whenever a failed entanglement assertion is encountered, the Interaction Analysis checks whether the target qubits of the assertion interact with each other. 33 | 34 | 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 in which Interaction Analysis would find a potential error cause: 35 | 36 | ```qasm 37 | qreg q[3]; 38 | 39 | h q[0]; 40 | cx q[0], q[1]; 41 | 42 | assert-ent q; 43 | ``` 44 | 45 | Here, calling {code}`potential_error_causes()` yields two errors: {code}`Missing interaction between q[0] and q[2]` and {code}`Missing interaction between q[1] and q[2]`. 46 | 47 | ```{note} 48 | Interaction Analysis is generally a static analysis method. 49 | However, when it is performed at runtime for the {code}`potential_error_causes()` method, it further uses dynamically obtained information to improve its results. 50 | During execution, the diagnostics tool keeps track of all actual qubits that were involved in instructions, even inside custom gate definitions, where static analysis would not be able to determine the exact qubits involved. 51 | This way, it can extend the interaction analysis throughout the entire program, even in other scopes. 52 | This is not always possible when performing interaction analysis statically. 53 | ``` 54 | 55 | ## Control-Value Analysis 56 | 57 | Control-Value Analysis is a method that dynamically analyzes the program during execution to find incorrectly defined controlled gates. 58 | In particular, it looks for controlled gates for which the control is always purely in the state {math}`|0\rangle`. In these cases, 59 | the controlled gate will never affect the full state, which could be a sign for an error. 60 | 61 | This analysis also similarly checks for inverse-controlled gates (i.e., controlled gates that trigger when the control value is {math}`|1\rangle`) that always 62 | have the state {math}`|0\rangle` as control. 63 | 64 | It is automatically called when using {cpp:member}`Diagnostics::potentialErrorCauses <DiagnosticsStruct::potentialErrorCauses>`/{py:meth}`Diagnostics.potential_error_causes <mqt.debugger.Diagnostics.potential_error_causes>`. 65 | 66 | The following code shows an example situation, in which Control-Value Analysis would find a potential error cause: 67 | 68 | ```qasm 69 | qreg q[3]; 70 | 71 | h q[0]; 72 | cx q[0], q[1]; 73 | cx q[2], q[0]; 74 | 75 | assert-ent q; 76 | ``` 77 | 78 | Here, calling {code}`potential_error_causes()` yields the error {code}`Controlled gate with constant control value for instruction cx q[2], q[0]`. 79 | -------------------------------------------------------------------------------- /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 | nb_execution_raise_on_error = True 122 | 123 | 124 | class CDAStyle(UnsrtStyle): 125 | """Custom style for including PDF links.""" 126 | 127 | def format_url(self, _e: Entry) -> HRef: # noqa: PLR6301 128 | """Format URL field as a link to the PDF. 129 | 130 | Returns: 131 | The formatted URL field. 132 | """ 133 | url = field("url", raw=True) 134 | return href()[url, "[PDF]"] 135 | 136 | 137 | pybtex.plugin.register_plugin("pybtex.style.formatting", "cda_style", CDAStyle) 138 | 139 | bibtex_bibfiles = ["lit_header.bib", "refs.bib"] 140 | bibtex_default_style = "cda_style" 141 | 142 | copybutton_prompt_text = r"(?:\(\.?venv\) )?(?:\[.*\] )?\$ " 143 | copybutton_prompt_is_regexp = True 144 | copybutton_line_continuation_character = "\\" 145 | 146 | modindex_common_prefix = ["mqt.debugger."] 147 | 148 | add_module_names = False 149 | toc_object_entries_show_parents = "hide" 150 | python_use_unqualified_type_names = True 151 | napoleon_google_docstring = True 152 | napoleon_numpy_docstring = False 153 | 154 | 155 | breathe_projects = {"mqt-debugger": "doxygen/xml"} 156 | breathe_default_project = "mqt-debugger" 157 | 158 | read_the_docs_build = os.environ.get("READTHEDOCS", None) == "True" 159 | if read_the_docs_build: 160 | subprocess.call("mkdir -p _build/doxygen && doxygen", shell=True) # noqa: S602, S607 161 | subprocess.call( # noqa: S602 162 | "mkdir -p api/cpp && breathe-apidoc -o api/cpp -m -f -g namespace _build/doxygen/xml/", # noqa: S607 163 | shell=True, 164 | ) 165 | 166 | # -- Options for HTML output ------------------------------------------------- 167 | html_theme = "furo" 168 | html_static_path = ["_static"] 169 | html_theme_options = { 170 | "light_logo": "mqt_dark.png", 171 | "dark_logo": "mqt_light.png", 172 | "source_repository": "https://github.com/munich-quantum-toolkit/debugger/", 173 | "source_branch": "main", 174 | "source_directory": "docs/", 175 | "navigation_with_keys": True, 176 | } 177 | --------------------------------------------------------------------------------