From 08647248aa53665481425c4d88f05ec34db12cba Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 22 May 2025 15:43:25 +0000 Subject: [PATCH 1/5] fixing some docs warnings Signed-off-by: Bettina Heim --- docs/sphinx/api/qec/nv_qldpc_decoder_api.rst | 2 ++ libs/qec/include/cudaq/qec/codes/surface_code.h | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/sphinx/api/qec/nv_qldpc_decoder_api.rst b/docs/sphinx/api/qec/nv_qldpc_decoder_api.rst index 7f8bb7e5..86bab484 100644 --- a/docs/sphinx/api/qec/nv_qldpc_decoder_api.rst +++ b/docs/sphinx/api/qec/nv_qldpc_decoder_api.rst @@ -82,6 +82,7 @@ (defaults to 1). Ignored unless `use_osd` is true. - `osd_order` (int): OSD postprocessor order (defaults to 0). Ref: `Decoding Across the Quantum LDPC Code Landscape `_ + - For `osd_method=2` (Exhaustive), the number of possible permutations searched after OSD-0 grows by 2^osd_order. - For `osd_method=3` (Combination Sweep), this is the λ parameter. All @@ -89,6 +90,7 @@ permutations are searched after OSD-0. This is (syndrome_length - block_size + λ * (λ - 1) / 2) additional permutations. - For other `osd_method` values, this is ignored. + - `bp_batch_size` (int): Number of syndromes that will be decoded in parallel for the BP decoder (defaults to 1) - `osd_batch_size` (int): Number of syndromes that will be decoded in diff --git a/libs/qec/include/cudaq/qec/codes/surface_code.h b/libs/qec/include/cudaq/qec/codes/surface_code.h index 2abeb408..c7096ad6 100644 --- a/libs/qec/include/cudaq/qec/codes/surface_code.h +++ b/libs/qec/include/cudaq/qec/codes/surface_code.h @@ -1,5 +1,5 @@ /****************************************************************-*- C++ -*-**** - * Copyright (c) 2024 NVIDIA Corporation & Affiliates. * + * Copyright (c) 2024 - 2025 NVIDIA Corporation & Affiliates. * * All rights reserved. * * * * This source code and the accompanying materials are made available under * @@ -251,6 +251,9 @@ class surface_code : public cudaq::qec::code { std::size_t get_num_ancilla_z_qubits() const override; public: + /// @brief Grid to keep track of topological arrangement of qubits. + stabilizer_grid grid; + /// @brief Constructor for the surface_code surface_code(const heterogeneous_map &); // Grid constructor would be useful @@ -261,9 +264,6 @@ class surface_code : public cudaq::qec::code { const cudaqx::heterogeneous_map &options) { return std::make_unique(options); }) - - /// @brief Grid to keep track of topological arrangement of qubits. - stabilizer_grid grid; }; } // namespace cudaq::qec::surface_code From b570fbd3ee7c7e625a6b54b9974a66502976021a Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 2 Jun 2025 16:57:17 +0000 Subject: [PATCH 2/5] fixing some warnings Signed-off-by: Bettina Heim --- docs/sphinx/api/qec/python_api.rst | 3 +++ docs/sphinx/conf.py.in | 5 +++++ libs/qec/python/cudaq_qec/__init__.py | 1 + 3 files changed, 9 insertions(+) diff --git a/docs/sphinx/api/qec/python_api.rst b/docs/sphinx/api/qec/python_api.rst index 2c6df892..9e7314bf 100644 --- a/docs/sphinx/api/qec/python_api.rst +++ b/docs/sphinx/api/qec/python_api.rst @@ -19,6 +19,9 @@ Decoder Interfaces .. autoclass:: cudaq_qec.DecoderResult :members: +.. autoclass:: cudaq_qec.AsyncDecoderResult + :members: + Built-in Decoders ================= diff --git a/docs/sphinx/conf.py.in b/docs/sphinx/conf.py.in index 71560a38..21cdb072 100644 --- a/docs/sphinx/conf.py.in +++ b/docs/sphinx/conf.py.in @@ -139,6 +139,7 @@ htmlhelp_basename = 'cudaqxDoc' doc_replacements = ( ('_pycudaqx_qec_the_suffix_matters_cudaq_qec.qecrt.', ''), ('mlir._mlir_libs._quakeDialects.cudaq_runtime.', ''), + ('*', '\*'), ) def fix_string(input): @@ -183,6 +184,8 @@ redirects = {"versions": "../latest/releases.html"} nitpick_ignore = [ ('cpp:identifier', 'pid_t'), ('cpp:identifier', 'uint8_t'), + ('cpp:identifier', 'uint32_t'), + ('cpp:identifier', 'vec2d'), ('cpp:identifier', 'details::tensor_impl::scalar_type'), ('cpp:identifier', 'cudaqx'), ('cpp:identifier', 'size_t'), @@ -198,6 +201,8 @@ nitpick_ignore = [ ('cpp:identifier', 'cudaq::qvector<>'), ('cpp:identifier', 'cudaq::qview<>'), ('cpp:identifier', 'cudaq::measure_result'), + ('py:class', 'cudaq.qview'), + ('py:class', 'cudaq.SpinOperator'), ('py:class', 'SpinOperator'), ('py:class', 'numpy.int32'), ('py:class', 'numpy.uint8'), diff --git a/libs/qec/python/cudaq_qec/__init__.py b/libs/qec/python/cudaq_qec/__init__.py index ca0e55b0..20ede4e7 100644 --- a/libs/qec/python/cudaq_qec/__init__.py +++ b/libs/qec/python/cudaq_qec/__init__.py @@ -21,6 +21,7 @@ get_available_codes = qecrt.get_available_codes get_decoder = qecrt.get_decoder DecoderResult = qecrt.DecoderResult +AsyncDecoderResult = qecrt.AsyncDecoderResult generate_random_bit_flips = qecrt.generate_random_bit_flips sample_memory_circuit = qecrt.sample_memory_circuit sample_code_capacity = qecrt.sample_code_capacity From cb4413674102fd665997a801d91b6314d7473319 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 3 Jun 2025 11:48:48 +0000 Subject: [PATCH 3/5] fixing the missing labels warnings Signed-off-by: Bettina Heim --- docs/Doxyfile.in | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index 29b697c8..defee60a 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -16,9 +16,18 @@ ALIASES += "pure_device_kernel=\par This function is a pure-device CUDA-Q quantu # Build related configuration options #--------------------------------------------------------------------------- -EXTRACT_ALL = YES -EXTRACT_PRIVATE = YES +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO EXTRACT_STATIC = YES +WARN_AS_ERROR = FAIL_ON_WARNINGS +WARN_IF_INCOMPLETE_DOC = NO +WARN_IF_UNDOCUMENTED = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +EXCLUDE_SYMBOLS = "classcudaqx_1_1*" #--------------------------------------------------------------------------- # Configuration options related to the preprocessor From 930d3a247248457fe582c5ff445d51725fae8561 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 3 Jun 2025 12:33:18 +0000 Subject: [PATCH 4/5] fixing the last warning Signed-off-by: Bettina Heim --- docs/sphinx/api/qec/cpp_api.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/sphinx/api/qec/cpp_api.rst b/docs/sphinx/api/qec/cpp_api.rst index 6df1f6c5..18afe15c 100644 --- a/docs/sphinx/api/qec/cpp_api.rst +++ b/docs/sphinx/api/qec/cpp_api.rst @@ -16,10 +16,12 @@ Code .. doxygenclass:: cudaq::qec::steane::steane :members: -.. doxygenclass:: cudaq::qec::surface_code::stabilizer_grid +.. doxygenclass:: cudaq::qec::surface_code::surface_code :members: -.. doxygenclass:: cudaq::qec::surface_code::surface_code +.. doxygenenum:: cudaq::qec::surface_code::surface_role + +.. doxygenclass:: cudaq::qec::surface_code::stabilizer_grid :members: From 8d688b50a354828256af3480eccdb0124d2c7b92 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 3 Jun 2025 14:39:38 +0000 Subject: [PATCH 5/5] removing docs Signed-off-by: Bettina Heim --- docs/CMakeLists.txt | 74 +- docs/sphinx/_static/cuda_quantum_icon.svg | 553 --------- docs/sphinx/_static/cudaq_override.css | 27 - docs/sphinx/_templates/autosummary/class.rst | 26 - .../_templates/autosummary/dataclass.rst | 10 - docs/sphinx/_templates/layout.html | 62 - docs/sphinx/_templates/openapi.html | 17 - docs/sphinx/api/core/cpp_api.rst | 46 - docs/sphinx/api/qec/cpp_api.rst | 62 - docs/sphinx/api/qec/nv_qldpc_decoder_api.rst | 99 -- docs/sphinx/api/qec/python_api.rst | 40 - docs/sphinx/api/solvers/cpp_api.rst | 79 -- docs/sphinx/api/solvers/python_api.rst | 32 - docs/sphinx/components/qec/introduction.rst | 1050 ----------------- .../components/solvers/introduction.rst | 553 --------- docs/sphinx/conf.py.in | 217 ---- .../examples/qec/cpp/circuit_level_noise.cpp | 125 -- .../examples/qec/cpp/code_capacity_noise.cpp | 104 -- .../qec/python/circuit_level_noise.py | 93 -- .../qec/python/code_capacity_noise.py | 65 - .../examples/qec/python/nv-qldpc-decoder.py | 227 ---- .../examples/qec/python/pseudo_threshold.py | 68 -- .../repetition_code_fine_grain_noise.py | 184 --- docs/sphinx/examples/solvers/cpp/adapt_h2.cpp | 40 - .../solvers/cpp/molecular_docking_qaoa.cpp | 60 - .../sphinx/examples/solvers/cpp/uccsd_vqe.cpp | 54 - .../examples/solvers/python/adapt_h2.py | 55 - .../python/generate_molecular_hamiltonians.py | 85 -- .../solvers/python/molecular_docking_qaoa.py | 53 - .../examples/solvers/python/uccsd_vqe.py | 43 - .../examples_rst/qec/circuit_level_noise.rst | 148 --- .../examples_rst/qec/code_capacity_noise.rst | 86 -- docs/sphinx/examples_rst/qec/examples.rst | 11 - docs/sphinx/examples_rst/solvers/adapt.rst | 41 - docs/sphinx/examples_rst/solvers/examples.rst | 13 - .../solvers/molecular_hamiltonians.rst | 103 -- docs/sphinx/examples_rst/solvers/qaoa.rst | 62 - docs/sphinx/examples_rst/solvers/vqe.rst | 68 -- docs/sphinx/index.rst | 66 -- docs/sphinx/quickstart/installation.rst | 151 --- scripts/build_docs.sh | 60 +- 41 files changed, 5 insertions(+), 5007 deletions(-) delete mode 100644 docs/sphinx/_static/cuda_quantum_icon.svg delete mode 100644 docs/sphinx/_static/cudaq_override.css delete mode 100644 docs/sphinx/_templates/autosummary/class.rst delete mode 100644 docs/sphinx/_templates/autosummary/dataclass.rst delete mode 100644 docs/sphinx/_templates/layout.html delete mode 100644 docs/sphinx/_templates/openapi.html delete mode 100644 docs/sphinx/api/core/cpp_api.rst delete mode 100644 docs/sphinx/api/qec/cpp_api.rst delete mode 100644 docs/sphinx/api/qec/nv_qldpc_decoder_api.rst delete mode 100644 docs/sphinx/api/qec/python_api.rst delete mode 100644 docs/sphinx/api/solvers/cpp_api.rst delete mode 100644 docs/sphinx/api/solvers/python_api.rst delete mode 100644 docs/sphinx/components/qec/introduction.rst delete mode 100644 docs/sphinx/components/solvers/introduction.rst delete mode 100644 docs/sphinx/conf.py.in delete mode 100644 docs/sphinx/examples/qec/cpp/circuit_level_noise.cpp delete mode 100644 docs/sphinx/examples/qec/cpp/code_capacity_noise.cpp delete mode 100644 docs/sphinx/examples/qec/python/circuit_level_noise.py delete mode 100644 docs/sphinx/examples/qec/python/code_capacity_noise.py delete mode 100644 docs/sphinx/examples/qec/python/nv-qldpc-decoder.py delete mode 100644 docs/sphinx/examples/qec/python/pseudo_threshold.py delete mode 100644 docs/sphinx/examples/qec/python/repetition_code_fine_grain_noise.py delete mode 100644 docs/sphinx/examples/solvers/cpp/adapt_h2.cpp delete mode 100644 docs/sphinx/examples/solvers/cpp/molecular_docking_qaoa.cpp delete mode 100644 docs/sphinx/examples/solvers/cpp/uccsd_vqe.cpp delete mode 100644 docs/sphinx/examples/solvers/python/adapt_h2.py delete mode 100644 docs/sphinx/examples/solvers/python/generate_molecular_hamiltonians.py delete mode 100644 docs/sphinx/examples/solvers/python/molecular_docking_qaoa.py delete mode 100644 docs/sphinx/examples/solvers/python/uccsd_vqe.py delete mode 100644 docs/sphinx/examples_rst/qec/circuit_level_noise.rst delete mode 100644 docs/sphinx/examples_rst/qec/code_capacity_noise.rst delete mode 100644 docs/sphinx/examples_rst/qec/examples.rst delete mode 100644 docs/sphinx/examples_rst/solvers/adapt.rst delete mode 100644 docs/sphinx/examples_rst/solvers/examples.rst delete mode 100644 docs/sphinx/examples_rst/solvers/molecular_hamiltonians.rst delete mode 100644 docs/sphinx/examples_rst/solvers/qaoa.rst delete mode 100644 docs/sphinx/examples_rst/solvers/vqe.rst delete mode 100644 docs/sphinx/index.rst delete mode 100644 docs/sphinx/quickstart/installation.rst diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index dc696d77..735cf6f1 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -1,5 +1,5 @@ # ============================================================================ # -# Copyright (c) 2024 NVIDIA Corporation & Affiliates. # +# Copyright (c) 2024 - 2025 NVIDIA Corporation & Affiliates. # # All rights reserved. # # # # This source code and the accompanying materials are made available under # @@ -25,13 +25,6 @@ foreach(lib ${CUDAQX_ALL_LIBS}) list(APPEND CUDAQX_PUBLIC_PYTHON ${LIB_PUBLIC_PYTHON}) endforeach() -# Find all the rst files -set(CUDAQX_PUBLIC_RST) -foreach(lib ${CUDAQX_ALL_LIBS}) - file(GLOB_RECURSE RST_FILES "*.rst") - list(APPEND CUDAQX_PUBLIC_RST ${RST_FILES}) -endforeach() - # ============================================================================== # Doxygen # ============================================================================== @@ -58,69 +51,6 @@ add_custom_command( add_custom_target(doxygen_docs DEPENDS ${DOXYGEN_INDEX_FILE}) -# ============================================================================== -# Sphinx -# ============================================================================== - -find_package(Python COMPONENTS Interpreter REQUIRED) - -function(require_python_module module_name) - execute_process( - COMMAND ${Python_EXECUTABLE} -c "import ${module_name}" - RESULT_VARIABLE result - OUTPUT_QUIET - ERROR_QUIET - ) - if(NOT result EQUAL 0) - message(FATAL_ERROR "Python module '${module_name}' not found") - endif() -endfunction() - -require_python_module(IPython) -require_python_module(breathe) -require_python_module(enum_tools) -require_python_module(myst_parser) -require_python_module(nbsphinx) -require_python_module(sphinx_copybutton) -require_python_module(sphinx_inline_tabs) -require_python_module(sphinx_gallery) -require_python_module(sphinx_rtd_theme) -require_python_module(sphinx_reredirects) -require_python_module(sphinx_toolbox) - -set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/sphinx) -set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/build) -set(SPHINX_INDEX_FILE ${SPHINX_BUILD}/index.html) - -set(SPHINX_CONF_IN ${SPHINX_SOURCE}/conf.py.in) -set(SPHINX_CONF ${CMAKE_CURRENT_BINARY_DIR}/conf.py) - -configure_file(${SPHINX_CONF_IN} ${SPHINX_CONF} @ONLY) - -# Only regenerate Sphinx when: -# - Doxygen has rerun -# - Our doc files have been updated -# - The Sphinx config has been updated -# TODO: set warning as error (-W flag) -add_custom_command( - OUTPUT ${SPHINX_INDEX_FILE} - COMMAND ${SPHINX_EXECUTABLE} -v -n --keep-going -b html - -c ${CMAKE_CURRENT_BINARY_DIR} - -Dbreathe_projects.cudaqx=${DOXYGEN_OUTPUT_DIR}/xml - ${SPHINX_SOURCE} ${SPHINX_BUILD} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS - ${SPHINX_SOURCE}/index.rst - ${DOXYGEN_INDEX_FILE} - cudaqx-pymodules - ${CUDAQX_PUBLIC_PYTHON} - ${CUDAQX_PUBLIC_RST} - MAIN_DEPENDENCY ${SPHINX_CONF_IN} ${SPHINX_CONF} - COMMENT "Generating documentation with Sphinx" -) - -add_custom_target(sphinx_docs DEPENDS ${SPHINX_INDEX_FILE}) - # ============================================================================== -add_custom_target(docs DEPENDS doxygen_docs sphinx_docs) +add_custom_target(docs DEPENDS doxygen_docs) diff --git a/docs/sphinx/_static/cuda_quantum_icon.svg b/docs/sphinx/_static/cuda_quantum_icon.svg deleted file mode 100644 index e7f2901f..00000000 --- a/docs/sphinx/_static/cuda_quantum_icon.svg +++ /dev/null @@ -1,553 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/sphinx/_static/cudaq_override.css b/docs/sphinx/_static/cudaq_override.css deleted file mode 100644 index 4ab0ac17..00000000 --- a/docs/sphinx/_static/cudaq_override.css +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - * * - * File: cudaq_override.css * - * Created Date: 16 Feb 2023 * - * Last Modified: 16 Feb 2023 * - ******************************************************************************/ - -.wy-nav-content { -max-width: 1240px !important; -} - -.wy-menu-vertical a { - font-size: 92%; -} - -.wy-menu-vertical li code, .wy-menu-vertical li .rst-content tt, .rst-content .wy-menu-vertical li tt { - font-size: 100%; -} - -code.code span.pre, code.cpp span.pre, code.docutils span.pre{ - color: darkgreen; -} diff --git a/docs/sphinx/_templates/autosummary/class.rst b/docs/sphinx/_templates/autosummary/class.rst deleted file mode 100644 index b45a3fd5..00000000 --- a/docs/sphinx/_templates/autosummary/class.rst +++ /dev/null @@ -1,26 +0,0 @@ -{{ fullname | escape | underline}} - -.. currentmodule:: {{ module }} - -.. autoclass:: {{ objname }} - - {% block methods %} - {% if methods %} - .. rubric:: {{ _('Methods') }} - - {% for item in methods %} - .. automethod:: {{ item }} - {%- endfor %} - - {% endif %} - {% endblock %} - - {% block attributes %} - {% if attributes %} - .. rubric:: {{ _('Attributes') }} - - {% for item in attributes %} - .. autoattribute:: {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} diff --git a/docs/sphinx/_templates/autosummary/dataclass.rst b/docs/sphinx/_templates/autosummary/dataclass.rst deleted file mode 100644 index b8c35324..00000000 --- a/docs/sphinx/_templates/autosummary/dataclass.rst +++ /dev/null @@ -1,10 +0,0 @@ -{{ fullname | escape | underline}} - -.. currentmodule:: {{ module }} - -.. autoclass:: {{ objname }} - - {% block methods %} - .. automethod:: __init__ - {% endblock %} - diff --git a/docs/sphinx/_templates/layout.html b/docs/sphinx/_templates/layout.html deleted file mode 100644 index c0f02f29..00000000 --- a/docs/sphinx/_templates/layout.html +++ /dev/null @@ -1,62 +0,0 @@ -{% extends "!layout.html" %} - {% block sidebartitle %} {{ super() }} - - - {% endblock %} - - {% block footer %} {{ super() }} - - - {% endblock %} diff --git a/docs/sphinx/_templates/openapi.html b/docs/sphinx/_templates/openapi.html deleted file mode 100644 index d50627fd..00000000 --- a/docs/sphinx/_templates/openapi.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - -
- - - - \ No newline at end of file diff --git a/docs/sphinx/api/core/cpp_api.rst b/docs/sphinx/api/core/cpp_api.rst deleted file mode 100644 index 6d323af3..00000000 --- a/docs/sphinx/api/core/cpp_api.rst +++ /dev/null @@ -1,46 +0,0 @@ -CUDA-QX Namespaces and Core Library C++ API -******************************************** - -Namespaces -========== -.. doxygennamespace:: cudaqx - :desc-only: -.. doxygennamespace:: cudaq - :desc-only: -.. doxygennamespace:: cudaq::qec - :desc-only: -.. doxygennamespace:: cudaq::qec::steane - :desc-only: -.. doxygennamespace:: cudaq::qec::surface_code - :desc-only: -.. doxygennamespace:: cudaq::qec::repetition - :desc-only: -.. doxygennamespace:: cudaq::solvers - :desc-only: -.. doxygennamespace:: cudaq::solvers::stateprep - :desc-only: -.. doxygennamespace:: cudaq::solvers::adapt - :desc-only: -.. doxygennamespace:: cudaq::optim - :desc-only: - -Core -============= - -.. doxygenclass:: cudaqx::extension_point - :members: - -.. doxygenclass:: cudaqx::heterogeneous_map - :members: - -.. doxygenclass:: cudaqx::tear_down - :members: - -.. doxygenclass:: cudaqx::details::tensor_impl - :members: - -.. doxygenclass:: cudaqx::tensor - :members: - -.. doxygenclass:: cudaqx::graph - :members: diff --git a/docs/sphinx/api/qec/cpp_api.rst b/docs/sphinx/api/qec/cpp_api.rst deleted file mode 100644 index 18afe15c..00000000 --- a/docs/sphinx/api/qec/cpp_api.rst +++ /dev/null @@ -1,62 +0,0 @@ -CUDA-Q QEC C++ API -****************************** - -Code -============= - -.. doxygenclass:: cudaq::qec::code - :members: - -.. doxygenstruct:: cudaq::qec::patch - :members: - -.. doxygenclass:: cudaq::qec::repetition::repetition - :members: - -.. doxygenclass:: cudaq::qec::steane::steane - :members: - -.. doxygenclass:: cudaq::qec::surface_code::surface_code - :members: - -.. doxygenenum:: cudaq::qec::surface_code::surface_role - -.. doxygenclass:: cudaq::qec::surface_code::stabilizer_grid - :members: - - -Decoder Interfaces -================== - -.. doxygenclass:: cudaq::qec::decoder - :members: - -.. doxygenstruct:: cudaq::qec::decoder_result - :members: - -Built-in Decoders -================= - -.. _nv_qldpc_decoder_api_cpp: - -NVIDIA QLDPC Decoder --------------------- - -.. include:: nv_qldpc_decoder_api.rst - -Common -============= - -.. doxygentypedef:: cudaq::qec::float_t - -.. doxygenenum:: cudaq::qec::operation - -.. doxygenfunction:: cudaq::qec::sample_code_capacity(const cudaqx::tensor &, std::size_t, double) -.. doxygenfunction:: cudaq::qec::sample_code_capacity(const cudaqx::tensor &, std::size_t, double, unsigned) -.. doxygenfunction:: cudaq::qec::sample_code_capacity(const code &, std::size_t, double) -.. doxygenfunction:: cudaq::qec::sample_code_capacity(const code &, std::size_t, double, unsigned) - -.. doxygenfunction:: cudaq::qec::sample_memory_circuit(const code &, std::size_t, std::size_t) -.. doxygenfunction:: cudaq::qec::sample_memory_circuit(const code &, std::size_t, std::size_t, cudaq::noise_model &) -.. doxygenfunction:: cudaq::qec::sample_memory_circuit(const code &, operation, std::size_t, std::size_t) -.. doxygenfunction:: cudaq::qec::sample_memory_circuit(const code &, operation, std::size_t, std::size_t, cudaq::noise_model &) diff --git a/docs/sphinx/api/qec/nv_qldpc_decoder_api.rst b/docs/sphinx/api/qec/nv_qldpc_decoder_api.rst deleted file mode 100644 index 86bab484..00000000 --- a/docs/sphinx/api/qec/nv_qldpc_decoder_api.rst +++ /dev/null @@ -1,99 +0,0 @@ -.. class:: nv_qldpc_decoder - - A general purpose Quantum Low-Density Parity-Check Decoder (QLDPC) - decoder based on GPU accelerated belief propagation (BP). Since belief - propagation is an iterative method, decoding can be improved with a - second-stage post-processing step. Optionally, ordered statistics decoding - (OSD) can be chosen to perform the second stage of decoding. - - An [[n,k,d]] quantum error correction (QEC) code encodes k logical qubits - into an n qubit data block, with a code distance d. Quantum low-density - parity-check (QLDPC) codes are characterized by sparse parity-check matrices - (or Tanner graphs), corresponding to a bounded number of parity checks per - data qubit. - - Requires a CUDA-Q compatible GPU. See the `CUDA-Q GPU Compatibility - List `_ - for a list of valid GPU configurations. - - References: - `Decoding Across the Quantum LDPC Code Landscape `_ - - .. note:: - It is required to create decoders with the `get_decoder` API from the CUDA-QX - extension points API, such as - - .. tab:: Python - - .. code-block:: python - - import cudaq_qec as qec - import numpy as np - H = np.array([[1, 0, 0, 1, 0, 1, 1], - [0, 1, 0, 1, 1, 0, 1], - [0, 0, 1, 0, 1, 1, 1]], dtype=np.uint8) # sample 3x7 PCM - opts = dict() # see below for options - # Note: H must be in row-major order. If you use - # `scipy.sparse.csr_matrix.todense()` to get the parity check - # matrix, you must specify todense(order='C') to get a row-major - # matrix. - nvdec = qec.get_decoder('nv-qldpc-decoder', H, **opts) - - .. tab:: C++ - - .. code-block:: cpp - - std::size_t block_size = 7; - std::size_t syndrome_size = 3; - cudaqx::tensor H; - - std::vector H_vec = {1, 0, 0, 1, 0, 1, 1, - 0, 1, 0, 1, 1, 0, 1, - 0, 0, 1, 0, 1, 1, 1}; - H.copy(H_vec.data(), {syndrome_size, block_size}); - - cudaqx::heterogeneous_map nv_custom_args; - nv_custom_args.insert("use_osd", true); - // See below for options - - auto nvdec = cudaq::qec::get_decoder("nv-qldpc-decoder", H, nv_custom_args); - - .. note:: - The `"nv-qldpc-decoder"` implements the :class:`cudaq_qec.Decoder` - interface for Python and the :cpp:class:`cudaq::qec::decoder` interface - for C++, so it supports all the methods in those respective classes. - - :param H: Parity check matrix (tensor format) - :param params: Heterogeneous map of parameters: - - - `use_sparsity` (bool): Whether or not to use a sparse matrix solver - - `error_rate` (double): Probability of an error (in 0-1 range) on a - block data bit (defaults to 0.001) - - `error_rate_vec` (double): Vector of length "block size" containing - the probability of an error (in 0-1 range) on a block data bit (defaults - to 0.001). This overrides `error_rate`. - - `max_iterations` (int): Maximum number of BP iterations to perform - (defaults to 30) - - `n_threads` (int): Number of CUDA threads to use for the GPU decoder - (defaults to smart selection based on parity matrix size) - - `use_osd` (bool): Whether or not to use an OSD post processor if the - initial BP algorithm fails to converge on a solution - - `osd_method` (int): 1=OSD-0, 2=Exhaustive, 3=Combination Sweep - (defaults to 1). Ignored unless `use_osd` is true. - - `osd_order` (int): OSD postprocessor order (defaults to 0). Ref: - `Decoding Across the Quantum LDPC Code Landscape `_ - - - For `osd_method=2` (Exhaustive), the number of possible - permutations searched after OSD-0 grows by 2^osd_order. - - For `osd_method=3` (Combination Sweep), this is the λ parameter. All - weight 1 permutations and the first λ bits worth of weight 2 - permutations are searched after OSD-0. This is (syndrome_length - - block_size + λ * (λ - 1) / 2) additional permutations. - - For other `osd_method` values, this is ignored. - - - `bp_batch_size` (int): Number of syndromes that will be decoded in - parallel for the BP decoder (defaults to 1) - - `osd_batch_size` (int): Number of syndromes that will be decoded in - parallel for OSD (defaults to the number of concurrent threads supported - by the hardware) - diff --git a/docs/sphinx/api/qec/python_api.rst b/docs/sphinx/api/qec/python_api.rst deleted file mode 100644 index 9e7314bf..00000000 --- a/docs/sphinx/api/qec/python_api.rst +++ /dev/null @@ -1,40 +0,0 @@ -CUDA-Q QEC Python API -****************************** - -.. automodule:: cudaq_qec - :members: - -Code -============= - -.. autoclass:: cudaq_qec.Code - :members: - -Decoder Interfaces -================== - -.. autoclass:: cudaq_qec.Decoder - :members: - -.. autoclass:: cudaq_qec.DecoderResult - :members: - -.. autoclass:: cudaq_qec.AsyncDecoderResult - :members: - -Built-in Decoders -================= - -.. _nv_qldpc_decoder_api_python: - -NVIDIA QLDPC Decoder --------------------- - -.. include:: nv_qldpc_decoder_api.rst - -Common -============= - -.. autofunction:: cudaq_qec.sample_memory_circuit - -.. autofunction:: cudaq_qec.sample_code_capacity diff --git a/docs/sphinx/api/solvers/cpp_api.rst b/docs/sphinx/api/solvers/cpp_api.rst deleted file mode 100644 index e363f396..00000000 --- a/docs/sphinx/api/solvers/cpp_api.rst +++ /dev/null @@ -1,79 +0,0 @@ -CUDA-Q Solvers C++ API -****************************** - -.. doxygenclass:: cudaq::solvers::operator_pool - :members: - -.. doxygenclass:: cudaq::solvers::spin_complement_gsd -.. doxygenclass:: cudaq::solvers::uccsd -.. doxygenclass:: cudaq::solvers::qaoa_pool - -.. doxygenfunction:: cudaq::solvers::get_operator_pool - -.. doxygenstruct:: cudaq::solvers::atom - :members: - -.. doxygenclass:: cudaq::solvers::molecular_geometry - :members: - -.. doxygenstruct:: cudaq::solvers::molecular_hamiltonian - :members: - -.. doxygenstruct:: cudaq::solvers::molecule_options - :members: - -.. doxygenfunction:: cudaq::solvers::create_molecule - -.. doxygenfunction:: cudaq::solvers::get_maxcut_hamiltonian - -.. doxygenfunction:: cudaq::solvers::get_clique_hamiltonian - -.. doxygenfunction:: cudaq::solvers::one_particle_op - -.. doxygentypedef:: cudaq::ParameterizedKernel -.. doxygentypedef:: cudaq::optim::optimization_result -.. doxygenclass:: cudaq::optim::optimizable_function -.. doxygenclass:: cudaq::optim::optimizer - :members: -.. doxygenclass:: cudaq::optim::cobyla -.. doxygenclass:: cudaq::optim::lbfgs -.. doxygenclass:: cudaq::observe_gradient - :members: -.. doxygenstruct:: cudaq::observe_iteration - :members: -.. doxygenclass:: cudaq::central_difference -.. doxygenclass:: cudaq::forward_difference -.. doxygenclass:: cudaq::parameter_shift - -.. doxygenenum:: cudaq::observe_execution_type - -.. doxygenstruct:: cudaq::solvers::vqe_result -.. doxygenfunction:: cudaq::solvers::vqe(QuantumKernel &&, const spin_op &, const std::string &, const std::string &, const std::vector &, heterogeneous_map) -.. doxygenfunction:: cudaq::solvers::vqe(QuantumKernel &&, const spin_op &, const std::string &, const std::vector &, heterogeneous_map) -.. doxygenfunction:: cudaq::solvers::vqe(QuantumKernel &&, const spin_op &, const std::string &, observe_gradient &, const std::vector &, heterogeneous_map) -.. doxygenfunction:: cudaq::solvers::vqe(QuantumKernel &&, const spin_op &, optim::optimizer &, const std::string &, const std::vector &, heterogeneous_map) -.. doxygenfunction:: cudaq::solvers::vqe(QuantumKernel &&, const spin_op &, optim::optimizer &, const std::vector &, heterogeneous_map) -.. doxygenfunction:: cudaq::solvers::vqe(QuantumKernel &&, const spin_op &, optim::optimizer &, observe_gradient &, const std::vector &, heterogeneous_map) - -.. doxygentypedef:: cudaq::solvers::adapt::result -.. doxygenfunction:: cudaq::solvers::adapt_vqe(const cudaq::qkernel&)> &, const spin_op &, const std::vector &, const heterogeneous_map) -.. doxygenfunction:: cudaq::solvers::adapt_vqe(const cudaq::qkernel&)> &, const spin_op &, const std::vector &, const optim::optimizer&, const heterogeneous_map) -.. doxygenfunction:: cudaq::solvers::adapt_vqe(const cudaq::qkernel&)> &, const spin_op &, const std::vector &, const optim::optimizer&, const std::string&, const heterogeneous_map) - -.. doxygentypedef:: cudaq::solvers::stateprep::excitation_list -.. doxygenfunction:: cudaq::solvers::stateprep::get_uccsd_excitations -.. doxygenfunction:: cudaq::solvers::stateprep::get_num_uccsd_parameters -.. doxygenfunction:: cudaq::solvers::stateprep::single_excitation -.. doxygenfunction:: cudaq::solvers::stateprep::double_excitation -.. doxygenfunction:: cudaq::solvers::stateprep::uccsd(cudaq::qview<>, const std::vector&, std::size_t, std::size_t) -.. doxygenfunction:: cudaq::solvers::stateprep::uccsd(cudaq::qview<>, const std::vector&, std::size_t) - - -.. doxygenstruct:: cudaq::solvers::qaoa_result - :members: -.. doxygenfunction:: cudaq::solvers::qaoa(const cudaq::spin_op &, const cudaq::spin_op &, const optim::optimizer &, std::size_t, const std::vector &, const heterogeneous_map) -.. doxygenfunction:: cudaq::solvers::qaoa(const cudaq::spin_op &, const optim::optimizer &, std::size_t, const std::vector &, const heterogeneous_map) -.. doxygenfunction:: cudaq::solvers::qaoa(const cudaq::spin_op &, std::size_t, const std::vector &, const heterogeneous_map) -.. doxygenfunction:: cudaq::solvers::qaoa(const cudaq::spin_op &, const cudaq::spin_op &, std::size_t, const std::vector &, const heterogeneous_map) -.. doxygenfunction:: cudaq::solvers::get_num_qaoa_parameters(const cudaq::spin_op &, const cudaq::spin_op &, std::size_t, const heterogeneous_map) -.. doxygenfunction:: cudaq::solvers::get_num_qaoa_parameters(const cudaq::spin_op &, std::size_t, const heterogeneous_map) diff --git a/docs/sphinx/api/solvers/python_api.rst b/docs/sphinx/api/solvers/python_api.rst deleted file mode 100644 index 150935b8..00000000 --- a/docs/sphinx/api/solvers/python_api.rst +++ /dev/null @@ -1,32 +0,0 @@ -CUDA-Q Solvers Python API -****************************** - -.. automodule:: cudaq_solvers - :members: - -.. autofunction:: cudaq_solvers.jordan_wigner - -.. autofunction:: cudaq_solvers.bravyi_kitaev - -.. autoclass:: cudaq_solvers.MolecularHamiltonian - :members: - -.. autofunction:: cudaq_solvers.get_operator_pool - -.. autofunction:: cudaq_solvers.optim.optimize -.. autoclass:: cudaq_solvers.ObserveExecutionType - :members: - :undoc-members: -.. autoclass:: cudaq_solvers.ObserveIteration - :members: -.. autofunction:: cudaq_solvers.vqe -.. autofunction:: cudaq_solvers.adapt_vqe - -.. autofunction:: cudaq_solvers.stateprep.uccsd -.. autofunction:: cudaq_solvers.stateprep.single_excitation -.. autofunction:: cudaq_solvers.stateprep.double_excitation -.. autofunction:: cudaq_solvers.stateprep.get_num_uccsd_parameters -.. autofunction:: cudaq_solvers.stateprep.get_uccsd_excitations - -.. autofunction:: cudaq_solvers.get_num_qaoa_parameters - diff --git a/docs/sphinx/components/qec/introduction.rst b/docs/sphinx/components/qec/introduction.rst deleted file mode 100644 index 41f2588f..00000000 --- a/docs/sphinx/components/qec/introduction.rst +++ /dev/null @@ -1,1050 +0,0 @@ -CUDA-Q QEC - Quantum Error Correction Library -============================================= - -Overview --------- -The ``cudaq-qec`` library provides a comprehensive framework for quantum -error correction research and development. It leverages GPU acceleration -for efficient syndrome decoding and error correction simulations (coming soon). - -Core Components ----------------- -``cudaq-qec`` is composed of two main interfaces - the :code:`cudaq::qec::code` and -:code:`cudaq::qec::decoder` types. These types are meant to be extended by developers -to provide new error correcting codes and new decoding strategies. - -QEC Code Framework :code:`cudaq::qec::code` -------------------------------------------- - -The :code:`cudaq::qec::code` class serves as the base class for all quantum error correcting codes in CUDA-Q QEC. It provides -a flexible extension point for implementing new codes and defines the core interface that all QEC codes must support. - -The core abstraction here is that of a mapping or dictionary of logical operations to their -corresponding physical implementation in the error correcting code as CUDA-Q quantum kernels. - -Class Structure -^^^^^^^^^^^^^^^ - -The code base class provides: - -1. **Operation Enumeration**: Defines supported logical operations - - .. code-block:: cpp - - enum class operation { - x, // Logical X gate - y, // Logical Y gate - z, // Logical Z gate - h, // Logical Hadamard gate - s, // Logical S gate - cx, // Logical CNOT gate - cy, // Logical CY gate - cz, // Logical CZ gate - stabilizer_round, // Stabilizer measurement round - prep0, // Prepare |0⟩ state - prep1, // Prepare |1⟩ state - prepp, // Prepare |+⟩ state - prepm // Prepare |-⟩ state - }; - - -2. **Patch Type**: Defines the structure of a logical qubit patch - - .. code-block:: cpp - - struct patch { - cudaq::qview<> data; // View of data qubits - cudaq::qview<> ancx; // View of X stabilizer ancilla qubits - cudaq::qview<> ancz; // View of Z stabilizer ancilla qubits - }; - - The `patch` type represents a logical qubit in quantum error correction codes. It contains: - - `data`: A view of the data qubits in the patch - - `ancx`: A view of the ancilla qubits used for X stabilizer measurements - - `ancz`: A view of the ancilla qubits used for Z stabilizer measurements - - This structure is designed for use within CUDA-Q kernel code and provides a - convenient way to access different qubit subsets within a logical qubit patch. - - -3. **Kernel Type Aliases**: Defines quantum kernel signatures - - .. code-block:: cpp - - using one_qubit_encoding = cudaq::qkernel; - using two_qubit_encoding = cudaq::qkernel; - using stabilizer_round = cudaq::qkernel( - patch, const std::vector&, const std::vector&)>; - -4. **Protected Members**: - - - :code:`operation_encodings`: Maps operations to their quantum kernel implementations. The key is the ``operation`` enum and the value is a variant on the above kernel type aliases. - - :code:`m_stabilizers`: Stores the code's stabilizer generators - -Implementing a New Code -^^^^^^^^^^^^^^^^^^^^^^^ - -To implement a new quantum error correcting code: - -1. **Create a New Class**: - - .. code-block:: cpp - - class my_code : public qec::code { - protected: - // Implement required virtual methods - public: - my_code(const heterogeneous_map& options); - }; - -2. **Implement Required Virtual Methods**: - - .. code-block:: cpp - - // Number of physical data qubits - std::size_t get_num_data_qubits() const override; - - // Total number of ancilla qubits - std::size_t get_num_ancilla_qubits() const override; - - // Number of X-type ancilla qubits - std::size_t get_num_ancilla_x_qubits() const override; - - // Number of Z-type ancilla qubits - std::size_t get_num_ancilla_z_qubits() const override; - -3. **Define Quantum Kernels**: - - Create CUDA-Q kernels for each logical operation: - - .. code-block:: cpp - - __qpu__ void x(patch p) { - // Implement logical X - } - - __qpu__ std::vector stabilizer(patch p, - const std::vector& x_stabs, - const std::vector& z_stabs) { - // Implement stabilizer measurements - } - -4. **Register Operations**: - - In the constructor, register quantum kernels for each operation: - - .. code-block:: cpp - - my_code::my_code(const heterogeneous_map& options) : code() { - // Register operations - operation_encodings.insert( - std::make_pair(operation::x, x)); - operation_encodings.insert( - std::make_pair(operation::stabilizer_round, stabilizer)); - - // Define stabilizer generators - m_stabilizers = qec::stabilizers({"XXXX", "ZZZZ"}); - } - - - Note that in your constructor, you have access to user-provided ``options``. For - example, if your code depends on an integer parameter called ``distance``, you can - retrieve that from the user via - - .. code-block:: cpp - - my_code::my_code(const heterogeneous_map& options) : code() { - // ... fill the map and stabilizers ... - - // Get the user-provided distance, or just - // set to 3 if user did not provide one - this->distance = options.get("distance", /*defaultValue*/ 3); - } - -5. **Register Extension Point**: - - Add extension point registration: - - .. code-block:: cpp - - CUDAQ_EXTENSION_CUSTOM_CREATOR_FUNCTION( - my_code, - static std::unique_ptr create( - const heterogeneous_map &options) { - return std::make_unique(options); - } - ) - - CUDAQ_REGISTER_TYPE(my_code) - -Example: Steane Code -^^^^^^^^^^^^^^^^^^^^^ - -The Steane [[7,1,3]] code provides a complete example implementation: - -1. **Header Definition**: - - - Declares quantum kernels for all logical operations - - Defines the code class with required virtual methods - - Specifies 7 data qubits and 6 ancilla qubits (3 X-type, 3 Z-type) - -2. **Implementation**: - - .. code-block:: cpp - - steane::steane(const heterogeneous_map &options) : code() { - // Register all logical operations - operation_encodings.insert( - std::make_pair(operation::x, x)); - // ... register other operations ... - - // Define stabilizer generators - m_stabilizers = qec::stabilizers({ - "XXXXIII", "IXXIXXI", "IIXXIXX", - "ZZZZIII", "IZZIZZI", "IIZZIZZ" - }); - } - -3. **Quantum Kernels**: - - Implements fault-tolerant logical operations: - - .. code-block:: cpp - - __qpu__ void x(patch logicalQubit) { - // Apply logical X to specific data qubits - x(logicalQubit.data[4], logicalQubit.data[5], - logicalQubit.data[6]); - } - - __qpu__ std::vector stabilizer(patch logicalQubit, - const std::vector& x_stabilizers, - const std::vector& z_stabilizers) { - // Measure X stabilizers - h(logicalQubit.ancx); - // ... apply controlled-X gates ... - h(logicalQubit.ancx); - - // Measure Z stabilizers - // ... apply controlled-X gates ... - - // Return measurement results - return mz(logicalQubit.ancz, logicalQubit.ancx); - } - -Implementing a New Code in Python -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -CUDA-Q QEC supports implementing quantum error correction codes in Python -using the :code:`@qec.code` decorator. This provides a more accessible way -to prototype and develop new codes. - -1. **Create a New Python File**: - - Create a new file (e.g., :code:`my_steane.py`) with your code implementation: - - .. code-block:: python - - import cudaq - import cudaq_qec as qec - from cudaq_qec import patch - -2. **Define Quantum Kernels**: - - Implement the required quantum kernels using the :code:`@cudaq.kernel` decorator: - - .. code-block:: python - - @cudaq.kernel - def prep0(logicalQubit: patch): - h(logicalQubit.data[0], logicalQubit.data[4], logicalQubit.data[6]) - x.ctrl(logicalQubit.data[0], logicalQubit.data[1]) - x.ctrl(logicalQubit.data[4], logicalQubit.data[5]) - # ... additional initialization gates ... - - @cudaq.kernel - def stabilizer(logicalQubit: patch, - x_stabilizers: list[int], - z_stabilizers: list[int]) -> list[bool]: - # Measure X stabilizers - h(logicalQubit.ancx) - for xi in range(len(logicalQubit.ancx)): - for di in range(len(logicalQubit.data)): - if x_stabilizers[xi * len(logicalQubit.data) + di] == 1: - x.ctrl(logicalQubit.ancx[xi], logicalQubit.data[di]) - h(logicalQubit.ancx) - - # Measure Z stabilizers - for zi in range(len(logicalQubit.ancx)): - for di in range(len(logicalQubit.data)): - if z_stabilizers[zi * len(logicalQubit.data) + di] == 1: - x.ctrl(logicalQubit.data[di], logicalQubit.ancz[zi]) - - # Get and reset ancillas - results = mz(logicalQubit.ancz, logicalQubit.ancx) - reset(logicalQubit.ancx) - reset(logicalQubit.ancz) - return results - -3. **Implement the Code Class**: - - Create a class decorated with :code:`@qec.code` that implements the required interface: - - .. code-block:: python - - @qec.code('py-steane-example') - class MySteaneCodeImpl: - def __init__(self, **kwargs): - qec.Code.__init__(self, **kwargs) - - # Define stabilizer generators - self.stabilizers = qec.Stabilizers([ - "XXXXIII", "IXXIXXI", "IIXXIXX", - "ZZZZIII", "IZZIZZI", "IIZZIZZ" - ]) - - # Register quantum kernels - self.operation_encodings = { - qec.operation.prep0: prep0, - qec.operation.stabilizer_round: stabilizer - } - - def get_num_data_qubits(self): - return 7 - - def get_num_ancilla_x_qubits(self): - return 3 - - def get_num_ancilla_z_qubits(self): - return 3 - - def get_num_ancilla_qubits(self): - return 6 - -4. **Install the Code**: - - Install your Python-implemented code using :code:`cudaqx-config`: - - .. code-block:: bash - - cudaqx-config --install-code my_steane.py - -5. **Using the Code**: - - The code can now be used like any other CUDA-Q QEC code: - - .. code-block:: python - - import cudaq_qec as qec - - # Create instance of your code - code = qec.get_code('py-steane-example') - - # Use the code for various numerical experiments - -Key Points -^^^^^^^^^^^ - -* The :code:`@qec.code` decorator takes the name of the code as an argument -* Operation encodings are registered via the :code:`operation_encodings` dictionary -* Stabilizer generators are defined using the :code:`qec.Stabilizers` class -* The code must implement all required methods from the base class interface - - -Using the Code Framework -^^^^^^^^^^^^^^^^^^^^^^^^^ - -To use an implemented code: - -.. tab:: Python - - .. code-block:: python - - # Create a code instance - code = qec.get_code("steane") - - # Access stabilizer information - stabilizers = code.get_stabilizers() - parity = code.get_parity() - - # The code can now be used for various numerical - # experiments - see section below. - -.. tab:: C++ - - .. code-block:: cpp - - // Create a code instance - auto code = cudaq::qec::get_code("steane"); - - // Access stabilizer information - auto stabilizers = code->get_stabilizers(); - auto parity = code->get_parity(); - - // The code can now be used for various numerical - // experiments - see section below. - - -Pre-built QEC Codes -------------------- - -CUDA-Q QEC provides several well-studied quantum error correction codes out of the box. Here's a detailed overview of each: - -Steane Code -^^^^^^^^^^^ - -The Steane code is a ``[[7,1,3]]`` CSS (Calderbank-Shor-Steane) code that encodes -one logical qubit into seven physical qubits with a code distance of 3. - -**Key Properties**: - -* Data qubits: 7 -* Encoded qubits: 1 -* Code distance: 3 -* Ancilla qubits: 6 (3 for X stabilizers, 3 for Z stabilizers) - -**Stabilizer Generators**: - -* X-type: ``["XXXXIII", "IXXIXXI", "IIXXIXX"]`` -* Z-type: ``["ZZZZIII", "IZZIZZI", "IIZZIZZ"]`` - -The Steane code can correct any single-qubit error and detect up to two errors. -It is particularly notable for being the smallest CSS code that can implement a universal set of transversal gates. - -Usage: - -.. tab:: Python - - .. code-block:: python - - import cudaq_qec as qec - - # Create Steane code instance - steane = qec.get_code("steane") - -.. tab:: C++ - - .. code-block:: cpp - - auto steane = cudaq::qec::get_code("steane"); - -Repetition Code -^^^^^^^^^^^^^^^ -The repetition code is a simple [[n,1,n]] code that protects against -bit-flip (X) errors by encoding one logical qubit into n physical qubits, where n is the code distance. - -**Key Properties**: - -* Data qubits: n (distance) -* Encoded qubits: 1 -* Code distance: n -* Ancilla qubits: n-1 (all for Z stabilizers) - -**Stabilizer Generators**: - -* For distance 3: ``["ZZI", "IZZ"]`` -* For distance 5: ``["ZZIII", "IZZII", "IIZZI", "IIIZZ"]`` - -The repetition code is primarily educational as it can only correct -X errors. However, it serves as an excellent introduction to QEC concepts. - -Usage: - -.. tab:: Python - - .. code-block:: python - - import cudaq_qec as qec - - # Create distance-3 repetition code - code = qec.get_code('repetition', distance=3) - - # Access stabilizers - stabilizers = code.get_stabilizers() # Returns ["ZZI", "IZZ"] - -.. tab:: C++ - - .. code-block:: cpp - - auto code = qec::get_code("repetition", {{"distance", 3}}); - - // Access stabilizers - auto stabilizers = code->get_stabilizers(); - - -Decoder Framework :code:`cudaq::qec::decoder` ----------------------------------------------- - -The CUDA-Q QEC decoder framework provides an extensible system for implementing -quantum error correction decoders through the :code:`cudaq::qec::decoder` base class. - -Class Structure -^^^^^^^^^^^^^^^ - -The decoder base class defines the core interface for syndrome decoding: - -.. code-block:: cpp - - class decoder { - protected: - std::size_t block_size; // For [n,k] code, this is n - std::size_t syndrome_size; // For [n,k] code, this is n-k - tensor H; // Parity check matrix - - public: - struct decoder_result { - bool converged; // Decoder convergence status - std::vector result; // Soft error probabilities - }; - - virtual decoder_result decode( - const std::vector& syndrome) = 0; - - virtual std::vector decode_batch( - const std::vector>& syndrome); - }; - -Key Components: - -* **Parity Check Matrix**: Defines the code structure via :code:`H` -* **Block Size**: Number of physical qubits in the code -* **Syndrome Size**: Number of stabilizer measurements -* **Decoder Result**: Contains convergence status and error probabilities -* **Multiple Decoding Modes**: Single syndrome or batch processing - -Implementing a New Decoder in C++ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -To implement a new decoder: - -1. **Create Decoder Class**: - -.. code-block:: cpp - - class my_decoder : public qec::decoder { - private: - // Decoder-specific members - - public: - my_decoder(const tensor& H, - const heterogeneous_map& params) - : decoder(H) { - // Initialize decoder - } - - decoder_result decode( - const std::vector& syndrome) override { - // Implement decoding logic - } - }; - -2. **Register Extension Point**: - -.. code-block:: cpp - - CUDAQ_EXTENSION_CUSTOM_CREATOR_FUNCTION( - my_decoder, - static std::unique_ptr create( - const tensor& H, - const heterogeneous_map& params) { - return std::make_unique(H, params); - } - ) - - CUDAQ_REGISTER_TYPE(my_decoder) - -Example: Lookup Table Decoder -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Here's a simple lookup table decoder for the Steane code: - -.. code-block:: cpp - - class single_error_lut : public decoder { - private: - std::map single_qubit_err_signatures; - - public: - single_error_lut(const tensor& H, - const heterogeneous_map& params) - : decoder(H) { - // Build lookup table for single-qubit errors - for (std::size_t qErr = 0; qErr < block_size; qErr++) { - std::string err_sig(syndrome_size, '0'); - for (std::size_t r = 0; r < syndrome_size; r++) { - bool syndrome = 0; - for (std::size_t c = 0; c < block_size; c++) - syndrome ^= (c != qErr) && H.at({r, c}); - err_sig[r] = syndrome ? '1' : '0'; - } - single_qubit_err_signatures.insert({err_sig, qErr}); - } - } - - decoder_result decode( - const std::vector& syndrome) override { - decoder_result result{false, - std::vector(block_size, 0.0)}; - - // Convert syndrome to string - std::string syndrome_str(syndrome_size, '0'); - for (std::size_t i = 0; i < syndrome_size; i++) - syndrome_str[i] = (syndrome[i] >= 0.5) ? '1' : '0'; - - // Lookup error location - auto it = single_qubit_err_signatures.find(syndrome_str); - if (it != single_qubit_err_signatures.end()) { - result.converged = true; - result.result[it->second] = 1.0; - } - - return result; - } - }; - -Implementing a Decoder in Python -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -CUDA-Q QEC supports implementing decoders in Python using the :code:`@qec.decoder` decorator: - -1. **Create Decoder Class**: - -.. code-block:: python - - @qec.decoder("my_decoder") - class MyDecoder: - def __init__(self, H, **kwargs): - qec.Decoder.__init__(self, H) - self.H = H - # Initialize with optional kwargs - - def decode(self, syndrome): - # Create result object - result = qec.DecoderResult() - - # Implement decoding logic - # ... - - # Set results - result.converged = True - result.result = [0.0] * self.block_size - - return result - -2. **Using Custom Parameters**: - -.. code-block:: python - - # Create decoder with custom parameters - decoder = qec.get_decoder("my_decoder", - H=parity_check_matrix, - custom_param=42) - -Key Features -^^^^^^^^^^^^^ - -* **Soft Decision Decoding**: Results are probabilities in [0,1] -* **Batch Processing**: Support for decoding multiple syndromes -* **Asynchronous Decoding**: Optional async interface for parallel processing -* **Custom Parameters**: Flexible configuration via heterogeneous_map -* **Python Integration**: First-class support for Python implementations - -Usage Example -^^^^^^^^^^^^^^ - -.. tab:: Python - - .. code-block:: python - - import cudaq_qec as qec - - # Get a code instance - steane = qec.get_code("steane") - - # Create decoder with code's parity matrix - decoder = qec.get_decoder('single_error_lut', steane.get_parity()) - - # Run stabilizer measurements - syndromes, dataQubitResults = qec.sample_memory_circuit(steane, numShots=1, numRounds=1) - - # Decode a syndrome - result = decoder.decode(syndromes[0]) - if result.converged: - print("Error locations:", - [i for i,p in enumerate(result.result) if p > 0.5]) - # No errors as we did not include a noise model and - # thus prints: - # Error locations: [] - -.. tab:: C++ - - .. code-block:: cpp - - using namespace cudaq; - - // Get a code instance - auto code = qec::get_code("steane"); - - // Create decoder with code's parity matrix - auto decoder = qec::get_decoder("single_error_lut", - code->get_parity()); - - // Run stabilizer measurements - auto [syndromes, dataQubitResults] = qec::sample_memory_circuit(*code, /*numShots*/numShots, /*numRounds*/ 1); - - // Decode syndrome - auto result = decoder->decode(syndromes[0]); - - -Pre-built QEC Decoders ----------------------- - -CUDA-Q QEC provides pre-built decoders. Here's a detailed overview of each: - -Quantum Low-Density Parity-Check Decoder -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The Quantum Low-Density Parity-Check (QLDPC) decoder leverages GPU-accelerated belief propagation (BP) for efficient error correction. -Since belief propagation is an iterative method which may not converge, decoding can be improved with a second-stage post-processing step. The `nv-qldpc-decoder` -API provides various post-processing options, which can be selected through its parameters. - -The QLDPC decoder `nv-qldpc-decoder` requires a CUDA-Q compatible GPU. See the list below for dependencies and compatibility: -https://nvidia.github.io/cuda-quantum/latest/using/install/local_installation.html#dependencies-and-compatibility - -The decoder is based on the following references: - -* https://arxiv.org/pdf/2005.07016 -* https://github.com/quantumgizmos/ldpc - - -Usage: - -.. tab:: Python - - .. code-block:: python - - import cudaq_qec as qec - - H_list = [ - [1, 0, 0, 1, 0, 1, 1], - [0, 1, 0, 1, 1, 0, 1], - [0, 0, 1, 0, 1, 1, 1] - ] - - H_np = np.array(H_list, dtype=np.uint8) - - decoder = qec.get_decoder("nv-qldpc-decoder", H_np) - -.. tab:: C++ - - .. code-block:: cpp - - std::size_t block_size = 7; - std::size_t syndrome_size = 3; - cudaqx::tensor H; - - std::vector H_vec = {1, 0, 0, 1, 0, 1, 1, - 0, 1, 0, 1, 1, 0, 1, - 0, 0, 1, 0, 1, 1, 1}; - H.copy(H_vec.data(), {syndrome_size, block_size}); - - cudaqx::heterogeneous_map nv_custom_args; - nv_custom_args.insert("use_osd", true); - - auto d1 = cudaq::qec::get_decoder("nv-qldpc-decoder", H, nv_custom_args); - - // Alternatively, configure the decoder without instantiating a heterogeneous_map - auto d2 = cudaq::qec::get_decoder("nv-qldpc-decoder", H, {{"use_osd", true}, {"bp_batch_size", 100}}); - - -Numerical Experiments ---------------------- - -CUDA-Q QEC provides utilities for running numerical experiments with quantum error correction codes. - -Conventions -^^^^^^^^^^^ - -To address vectors of qubits (`cudaq::qvector`), CUDAQ indexing starts from 0, and 0 corresponds -to the leftmost position when working with pauli strings (`cudaq::spin_op`). For example, applying a pauli X operator -to qubit 1 out of 7 would be `X_1 = IXIIIII`. - -While implementing your own codes and decoders, you are free to follow any convention that is convenient to you. However, -to interact with the pre-built QEC codes and decoders within this library, the following conventions are used. All of these codes -are CSS codes, and so we separate :math:`X`-type and :math:`Z`-type errors. For example, an error vector for 3 qubits will -have 6 entries, 3 bits representing the presence of a bit-flip on each qubit, and 3 bits representing a phase-flip on each qubit. -An error vector representing a bit-flip on qubit 0, and a phase-flip on qubit 1 would look like `E = 100010`. This means that this -error vector is just two error vectors (`E_X, E_Z`) concatenated together (`E = E_X | E_Z`). - -These errors are detected by stabilizers. :math:`Z`-stabilizers detect :math:`X`-type errors and vice versa. Thus we write our -CSS parity check matrices as - -.. math:: - H_{CSS} = \begin{pmatrix} - H_Z & 0 \\ - 0 & H_X - \end{pmatrix}, - -so that when we generate a syndrome vector by multiplying the parity check matrix by an error vector we get - -.. math:: - \begin{align} - S &= H \cdot E\\ - S_X &= H_Z \cdot E_x\\ - S_Z &= H_X \cdot E_Z. - \end{align} - -This means that for the concatenated syndrome vector `S = S_X | S_Z`, the first part, `S_X`, are syndrome bits triggered by `Z` -stabilizers detecting `X` errors. This is because the `Z` stabilizers like `ZZI` and `IZZ` anti-commute with `X` errors like -`IXI`. - -The decoder prediction as to what error happened is `D = D_X | D_Z`. A successful error decoding does not require that `D = E`, -but that `D + E` is not a logical operator. There are a couple ways to check this. -For bitflip errors, we check that the residual error `R = D_X + E_X` is not `L_X`. Since `X` anticommutes -with `Z`, we can check that `L_Z(D_X + E_X) = 0`. This is because we just need to check if they have mutual support on an even -or odd number of qubits. We could also check that `R` is not a stabilizer. - -Similar to the parity check matrix, the logical observables are also stored in a matrix as - -.. math:: - L = \begin{pmatrix} - L_Z & 0 \\ - 0 & L_X - \end{pmatrix}, - -so that when determining logical errors, we can do matrix multiplication - -.. math:: - \begin{align} - P &= L \cdot R\\ - P_X &= L_Z \cdot R_x\\ - P_Z &= L_X \cdot R_Z. - \end{align} - -Here we're using `P` as this can be stored in a Pauli frame tracker to track observable flips. - -Each logical qubit has logical observables associated with it. Depending on what basis the data qubits are measured in, either the -`X` or `Z` logical observables can be measured. The data qubits which support the logical observable is contained the `qec::code` class as well. - -To do a logical `Z(X)` measurement, measure out all of the data qubits in the `Z(X)` basis. Then check support on the appropriate -`Z(x)` observable. - - -Memory Circuit Experiments -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Memory circuit experiments test a QEC code's ability to preserve quantum information over time by: - -1. Preparing an initial logical state -2. Performing multiple rounds of stabilizer measurements -3. Measuring data qubits to verify state preservation -4. Optionally applying noise during the process - -Function Variants -~~~~~~~~~~~~~~~~~ - -.. tab:: Python - - .. code-block:: python - - import cudaq - import cudaq_qec as qec - - # Use the stim backend for performance in QEC settings - cudaq.set_target("stim") - - # Get a code instance - code = qec.get_code("steane") - - # Basic memory circuit with |0⟩ state - syndromes, measurements = qec.sample_memory_circuit( - code, # QEC code instance - numShots=1000, # Number of circuit executions - numRounds=1 # Number of stabilizer rounds - ) - - # Memory circuit with custom initial state - syndromes, measurements = qec.sample_memory_circuit( - code, # QEC code instance - op=qec.operation.prep1, # Initial state - numShots=1000, # Number of shots - numRounds=1 # Number of rounds - ) - - # Memory circuit with noise model - noise = cudaq.NoiseModel() - # Configure noise - noise.add_all_qubit_channel("x", cudaq.Depolarization2(0.01), 1) - syndromes, measurements = qec.sample_memory_circuit( - code, # QEC code instance - numShots=1000, # Number of shots - numRounds=1, # Number of rounds - noise=noise # Noise model - ) - -.. tab:: C++ - - .. code-block:: cpp - - // Basic memory circuit with |0⟩ state - auto [syndromes, measurements] = qec::sample_memory_circuit( - code, // QEC code instance - numShots, // Number of circuit executions - numRounds // Number of stabilizer rounds - ); - - // Memory circuit with custom initial state - auto [syndromes, measurements] = qec::sample_memory_circuit( - code, // QEC code instance - operation::prep1, // Initial state preparation - numShots, // Number of circuit executions - numRounds // Number of stabilizer rounds - ); - - // Memory circuit with noise model - auto noise_model = cudaq::noise_model(); - noise_model.add_channel(...); // Configure noise - auto [syndromes, measurements] = qec::sample_memory_circuit( - code, // QEC code instance - numShots, // Number of circuit executions - numRounds, // Number of stabilizer rounds - noise_model // Noise model to apply - ); - -Return Values -~~~~~~~~~~~~~ - -The functions return a tuple containing: - -1. **Syndrome Measurements** (:code:`tensor`): - - * Shape: :code:`(num_shots, num_rounds * syndrome_size)` - * Contains stabilizer measurement results - * Values are 0 or 1 representing measurement outcomes - -2. **Data Measurements** (:code:`tensor`): - - * Shape: :code:`(num_shots, block_size)` - * Contains final data qubit measurements - * Used to verify logical state preservation - -Example Usage -~~~~~~~~~~~~~ - -Example of running a memory experiment: - -.. tab:: Python - - .. code-block:: python - - import cudaq - import cudaq_qec as qec - - # Use the stim backend for performance in QEC settings - cudaq.set_target("stim") - - # Create code and decoder - code = qec.get_code('steane') - decoder = qec.get_decoder('single_error_lut', - code.get_parity()) - - # Configure noise - noise = cudaq.NoiseModel() - noise.add_all_qubit_channel("x", cudaq.Depolarization2(0.01), 1) - - # Run memory experiment - syndromes, measurements = qec.sample_memory_circuit( - code, - op=qec.operation.prep0, - numShots=1000, - numRounds=10, - noise=noise - ) - - # Analyze results - for shot in range(1000): - # Get syndrome for this shot - syndrome = syndromes[shot].tolist() - - # Decode syndrome - result = decoder.decode(syndrome) - if result.converged: - # Process correction - pass - -.. tab:: C++ - - .. code-block:: cpp - - // Compile and run with: - // nvq++ --enable-mlir --target=stim -lcudaq-qec example.cpp - // ./a.out - - #include "cudaq.h" - #include "cudaq/qec/decoder.h" - #include "cudaq/qec/experiments.h" - #include "cudaq/qec/noise_model.h" - - int main(){ - // Create a Steane code instance - auto code = cudaq::qec::get_code("steane"); - - // Configure noise model - cudaq::noise_model noise; - noise.add_all_qubit_channel("x", cudaq::depolarization2(0.1), - /*num_controls=*/1); - - // Run memory experiment - auto [syndromes, data] = cudaq::qec::sample_memory_circuit( - *code, // Code instance - cudaq::qec::operation::prep0, // Prepare |0⟩ state - 1000, // 1000 shots - 1, // 1 rounds - noise // Apply noise - ); - - // Analyze results - auto decoder = cudaq::qec::get_decoder("single_error_lut", code->get_parity()); - for (std::size_t shot = 0; shot < 1000; shot++) { - // Get syndrome for this shot - std::vector syndrome(syndromes.shape()[1]); - for (std::size_t i = 0; i < syndrome.size(); i++) - syndrome[i] = syndromes.at({shot, i}); - - // Decode syndrome - auto [converged, v_result] = decoder->decode(syndrome); - // Process correction - // ... - } - } - -Additional Noise Models -~~~~~~~~~~~~~~~~~~~~~~~ - -.. tab:: Python - - .. code-block:: python - - noise = cudaq.NoiseModel() - - # Add multiple error channels - noise.add_all_qubit_channel('h', cudaq.BitFlipChannel(0.001)) - - # Specify two qubit errors - noise.add_all_qubit_channel("x", cudaq.Depolarization2(p), 1) - -.. tab:: C++ - - .. code-block:: cpp - - cudaq::noise_model noise; - - // Add multiple error channels - noise.add_all_qubit_channel( - "x", cudaq::bit_flip_channel(/*probability*/ 0.01)); - - // Specify two qubit errors - noise.add_all_qubit_channel( - "x", cudaq::depolarization2(/*probability*/ 0.01), - /*numControls*/ 1); - diff --git a/docs/sphinx/components/solvers/introduction.rst b/docs/sphinx/components/solvers/introduction.rst deleted file mode 100644 index 3bd0c869..00000000 --- a/docs/sphinx/components/solvers/introduction.rst +++ /dev/null @@ -1,553 +0,0 @@ -CUDA-Q Solvers Library -======================= - -Overview --------- -The CUDA-Q Solvers library provides high-level quantum-classical hybrid -algorithms and supporting infrastructure for quantum chemistry and -optimization problems. It features implementations of VQE, ADAPT-VQE, -and supporting utilities for Hamiltonian generation and operator pool management. - -Core Components ------------------ - -1. **Variational Algorithms**: - - * Variational Quantum Eigensolver (VQE) - * Adaptive Derivative-Assembled Pseudo-Trotter VQE (ADAPT-VQE) - -2. **Quantum Chemistry Tools**: - - * Molecular Hamiltonian Generation - * One-Particle Operator Creation - * Geometry Management - -3. **Operator Infrastructure**: - - * Operator Pool Generation - * Fermion-to-Qubit Mappings - * Gradient Computation - -Operator Infrastructure ------------------------- - -Molecular Hamiltonian Options -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The :code:`molecule_options` structure provides extensive configuration for molecular calculations in CUDA-QX. - -+---------------------+---------------+------------------+------------------------------------------+ -| Option | Type | Default | Description | -+=====================+===============+==================+==========================================+ -| driver | string | "RESTPySCFDriver"| Quantum chemistry driver backend | -+---------------------+---------------+------------------+------------------------------------------+ -| fermion_to_spin | string | "jordan_wigner" | Fermionic to qubit operator mapping | -+---------------------+---------------+------------------+------------------------------------------+ -| type | string | "gas_phase" | Type of molecular system | -+---------------------+---------------+------------------+------------------------------------------+ -| symmetry | bool | false | Use molecular symmetry | -+---------------------+---------------+------------------+------------------------------------------+ -| memory | double | 4000.0 | Memory allocation (MB) | -+---------------------+---------------+------------------+------------------------------------------+ -| cycles | size_t | 100 | Maximum SCF cycles | -+---------------------+---------------+------------------+------------------------------------------+ -| initguess | string | "minao" | Initial SCF guess method | -+---------------------+---------------+------------------+------------------------------------------+ -| UR | bool | false | Enable unrestricted calculations | -+---------------------+---------------+------------------+------------------------------------------+ -| nele_cas | optional | nullopt | Number of electrons in active space | -| | | | | -+---------------------+---------------+------------------+------------------------------------------+ -| norb_cas | optional | nullopt | Number of spatial orbitals in | -| | | | in active space | -+---------------------+---------------+------------------+------------------------------------------+ -| MP2 | bool | false | Enable MP2 calculations | -+---------------------+---------------+------------------+------------------------------------------+ -| natorb | bool | false | Use natural orbitals | -+---------------------+---------------+------------------+------------------------------------------+ -| casci | bool | false | Perform CASCI calculations | -+---------------------+---------------+------------------+------------------------------------------+ -| ccsd | bool | false | Perform CCSD calculations | -+---------------------+---------------+------------------+------------------------------------------+ -| casscf | bool | false | Perform CASSCF calculations | -+---------------------+---------------+------------------+------------------------------------------+ -| integrals_natorb | bool | false | Use natural orbitals for integrals | -+---------------------+---------------+------------------+------------------------------------------+ -| integrals_casscf | bool | false | Use CASSCF orbitals for integrals | -+---------------------+---------------+------------------+------------------------------------------+ -| potfile | optional | nullopt | Path to external potential file | -| | | | | -+---------------------+---------------+------------------+------------------------------------------+ -| verbose | bool | false | Enable detailed output logging | -+---------------------+---------------+------------------+------------------------------------------+ - -Example Usage -^^^^^^^^^^^^^ - -.. tab:: Python - - .. code-block:: python - - import cudaq_solvers as solvers - - # Configure molecular options - options = { - 'fermion_to_spin': 'jordan_wigner', - 'casci': True, - 'memory': 8000.0, - 'verbose': True - } - - # Create molecular Hamiltonian - molecule = solvers.create_molecule( - geometry=[('H', (0., 0., 0.)), - ('H', (0., 0., 0.7474))], - basis='sto-3g', - spin=0, - charge=0, - **options - ) - -.. tab:: C++ - - .. code-block:: cpp - - using namespace cudaq::solvers; - - // Configure molecular options - molecule_options options; - options.fermion_to_spin = "jordan_wigner"; - options.casci = true; - options.memory = 8000.0; - options.verbose = true; - - // Create molecular geometry - auto geometry = molecular_geometry({ - atom{"H", {0.0, 0.0, 0.0}}, - atom{"H", {0.0, 0.0, 0.7474}} - }); - - // Create molecular Hamiltonian - auto molecule = create_molecule( - geometry, - "sto-3g", - 0, // spin - 0, // charge - options - ); - -Variational Quantum Eigensolver (VQE) --------------------------------------- - -The VQE algorithm finds the minimum eigenvalue of a -Hamiltonian using a hybrid quantum-classical approach. - -VQE Examples -------------- - -The VQE implementation supports multiple usage patterns with different levels of customization. - -Basic Usage -^^^^^^^^^^^ - -.. tab:: Python - - .. code-block:: python - - import cudaq - from cudaq import spin - import cudaq_solvers as solvers - - # Define quantum kernel (ansatz) - @cudaq.kernel - def ansatz(theta: float): - q = cudaq.qvector(2) - x(q[0]) - ry(theta, q[1]) - x.ctrl(q[1], q[0]) - - # Define Hamiltonian - H = 5.907 - 2.1433 * spin.x(0) * spin.x(1) - \ - 2.1433 * spin.y(0) * spin.y(1) + \ - 0.21829 * spin.z(0) - 6.125 * spin.z(1) - - # Run VQE with defaults (cobyla optimizer) - energy, parameters, data = solvers.vqe( - lambda thetas: ansatz(thetas[0]), - H, - initial_parameters=[0.0], - verbose=True - ) - print(f"Ground state energy: {energy}") - -.. tab:: C++ - - .. code-block:: cpp - - #include "cudaq.h" - - #include "cudaq/solvers/operators.h" - #include "cudaq/solvers/vqe.h" - - // Define quantum kernel - struct ansatz { - void operator()(std::vector theta) __qpu__ { - cudaq::qvector q(2); - x(q[0]); - ry(theta[0], q[1]); - x(q[1], q[0]); - } - }; - - // Create Hamiltonian - auto H = 5.907 - 2.1433 * x(0) * x(1) - - 2.1433 * y(0) * y(1) + - 0.21829 * z(0) - 6.125 * z(1); - - // Run VQE with default optimizer - auto result = cudaq::solvers::vqe( - ansatz{}, - H, - {0.0}, // Initial parameters - {{"verbose", true}} - ); - printf("Ground state energy: %lf\n", result.energy); - -Custom Optimization -^^^^^^^^^^^^^^^^^^^ - -.. tab:: Python - - .. code-block:: python - - # Using L-BFGS-B optimizer with parameter-shift gradients - energy, parameters, data = solvers.vqe( - lambda thetas: ansatz(thetas[0]), - H, - initial_parameters=[0.0], - optimizer='lbfgs', - gradient='parameter_shift', - verbose=True - ) - - # Using SciPy optimizer directly - from scipy.optimize import minimize - - def callback(xk): - exp_val = cudaq.observe(ansatz, H, xk[0]).expectation() - print(f"Energy at iteration: {exp_val}") - - energy, parameters, data = solvers.vqe( - lambda thetas: ansatz(thetas[0]), - H, - initial_parameters=[0.0], - optimizer=minimize, - callback=callback, - method='L-BFGS-B', - jac='3-point', - tol=1e-4, - options={'disp': True} - ) - -.. tab:: C++ - - .. code-block:: cpp - - // Using L-BFGS optimizer with central difference gradients - auto optimizer = cudaq::optim::optimizer::get("lbfgs"); - auto gradient = cudaq::observe_gradient::get( - "central_difference", - ansatz{}, - H - ); - - auto result = cudaq::solvers::vqe( - ansatz{}, - H, - *optimizer, - *gradient, - {0.0}, // Initial parameters - {{"verbose", true}} - ); - -Shot-based Simulation -^^^^^^^^^^^^^^^^^^^^^ - -.. tab:: Python - - .. code-block:: python - - # Run VQE with finite shots - energy, parameters, data = solvers.vqe( - lambda thetas: ansatz(thetas[0]), - H, - initial_parameters=[0.0], - shots=10000, - max_iterations=10, - verbose=True - ) - - # Analyze measurement data - for iteration in data: - counts = iteration.result.counts() - print("\nMeasurement counts:") - print("XX basis:", counts.get_register_counts('XX')) - print("YY basis:", counts.get_register_counts('YY')) - print("ZI basis:", counts.get_register_counts('ZI')) - print("IZ basis:", counts.get_register_counts('IZ')) - -.. tab:: C++ - - .. code-block:: cpp - - // Run VQE with finite shots - auto optimizer = cudaq::optim::optimizer::get("lbfgs"); - auto gradient = cudaq::observe_gradient::get( - "parameter_shift", - ansatz{}, - H - ); - - auto result = cudaq::solvers::vqe( - ansatz{}, - H, - *optimizer, - *gradient, - {0.0}, - { - {"shots", 10000}, - {"verbose", true} - } - ); - - // Analyze measurement data - for (auto& iteration : result.iteration_data) { - std::cout << "Iteration type: " - << (iteration.type == observe_execution_type::gradient - ? "gradient" : "function") - << "\n"; - iteration.result.dump(); - } - -ADAPT-VQE ---------- - -The Adaptive Derivative-Assembled Pseudo-Trotter Variational Quantum Eigensolver (ADAPT-VQE) -is an advanced quantum algorithm that dynamically builds a problem-tailored ansatz -based on operator gradients. - -Key Features -^^^^^^^^^^^^ - -* Dynamic ansatz construction -* Gradient-based operator selection -* Automatic termination criteria -* Support for various operator pools -* Compatible with multiple optimizers - -Basic Usage -^^^^^^^^^^^^ - -.. tab:: Python - - .. code-block:: python - - import cudaq - import cudaq_solvers as solvers - - # Define molecular geometry - geometry = [ - ('H', (0., 0., 0.)), - ('H', (0., 0., 0.7474)) - ] - - # Create molecular Hamiltonian - molecule = solvers.create_molecule( - geometry, - 'sto-3g', - spin=0, - charge=0, - casci=True - ) - - # Generate operator pool - operators = solvers.get_operator_pool( - "spin_complement_gsd", - num_orbitals=molecule.n_orbitals - ) - - numElectrons = molecule.n_electrons - - # Define initial state preparation - @cudaq.kernel - def initial_state(q: cudaq.qview): - for i in range(numElectrons): - x(q[i]) - - # Run ADAPT-VQE - energy, parameters, operators = solvers.adapt_vqe( - initial_state, - molecule.hamiltonian, - operators, - verbose=True - ) - print(f"Ground state energy: {energy}") - -.. tab:: C++ - - .. code-block:: cpp - - #include "cudaq/solvers/adapt.h" - #include "cudaq/solvers/operators.h" - - // compile with - // nvq++ adaptEx.cpp --enable-mlir -lcudaq-solvers - // ./a.out - - int main() { - // Define initial state preparation - auto initial_state = [](cudaq::qvector<>& q) __qpu__ { - for (std::size_t i = 0; i < 2; ++i) - x(q[i]); - }; - - // Create Hamiltonian (H2 molecule example) - cudaq::solvers::molecular_geometry geometry{{"H", {0., 0., 0.}}, - {"H", {0., 0., .7474}}}; - auto molecule = cudaq::solvers::create_molecule( - geometry, "sto-3g", 0, 0, {.casci = true, .verbose = true}); - - auto h = molecule.hamiltonian; - - // Generate operator pool - auto operators = cudaq::solvers::get_operator_pool( - "spin_complement_gsd", { - {"num-orbitals", h.num_qubits() / 2} - }); - - // Run ADAPT-VQE - auto [energy, parameters, selected_ops] = - cudaq::solvers::adapt_vqe( - initial_state, - h, - operators, - { - {"grad_norm_tolerance", 1e-3}, - {"verbose", true} - } - ); - } - -Advanced Usage -^^^^^^^^^^^^^^^ - -Custom Optimization Settings -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. tab:: Python - - .. code-block:: python - - # Using L-BFGS-B optimizer with central difference gradients - energy, parameters, operators = solvers.adapt_vqe( - initial_state, - molecule.hamiltonian, - operators, - optimizer='lbfgs', - gradient='central_difference', - verbose=True - ) - - # Using SciPy optimizer directly - from scipy.optimize import minimize - energy, parameters, operators = solvers.adapt_vqe( - initial_state, - molecule.hamiltonian, - operators, - optimizer=minimize, - method='L-BFGS-B', - jac='3-point', - tol=1e-8, - options={'disp': True} - ) - -.. tab:: C++ - - .. code-block:: cpp - - // Using L-BFGS optimizer with central difference gradients - auto optimizer = cudaq::optim::optimizer::get("lbfgs"); - auto [energy, parameters, operators] = - cudaq::solvers::adapt_vqe( - initial_state{}, - h, - operators, - *optimizer, - "central_difference", - { - {"grad_norm_tolerance", 1e-3}, - {"verbose", true} - } - ); - -Available Operator Pools -^^^^^^^^^^^^^^^^^^^^^^^^^ - -CUDA-QX provides several pre-built operator pools for ADAPT-VQE: - -* **spin_complement_gsd**: Spin-complemented generalized singles and doubles -* **uccsd**: UCCSD operators -* **qaoa**: QAOA mixer excitation operators - -.. code-block:: python - - # Generate different operator pools - gsd_ops = solvers.get_operator_pool( - "spin_complement_gsd", - num_orbitals=molecule.n_orbitals - ) - - uccsd_ops = solvers.get_operator_pool( - "uccsd", - num_orbitals=molecule.n_orbitals, - num_electrons=molecule.n_electrons - ) - -Algorithm Parameters -^^^^^^^^^^^^^^^^^^^^^^ - -ADAPT-VQE supports various configuration options: - -* **grad_norm_tolerance**: Convergence threshold for operator gradients -* **max_iterations**: Maximum number of ADAPT iterations -* **verbose**: Enable detailed output -* **shots**: Number of measurements for shot-based simulation - -.. code-block:: python - - energy, parameters, operators = solvers.adapt_vqe( - initial_state, - hamiltonian, - operators, - grad_norm_tolerance=1e-3, - max_iterations=20, - verbose=True, - shots=10000 - ) - -Results Analysis -^^^^^^^^^^^^^^^^^ - -The algorithm returns three components: - -1. **energy**: Final ground state energy -2. **parameters**: Optimized parameters for each selected operator -3. **operators**: List of selected operators in order of application - -.. code-block:: python - - # Analyze results - print(f"Final energy: {energy}") - print("\nSelected operators and parameters:") - for param, op in zip(parameters, operators): - print(f"θ = {param:.6f} : {op}") \ No newline at end of file diff --git a/docs/sphinx/conf.py.in b/docs/sphinx/conf.py.in deleted file mode 100644 index 21cdb072..00000000 --- a/docs/sphinx/conf.py.in +++ /dev/null @@ -1,217 +0,0 @@ -# ============================================================================ # -# Copyright (c) 2024 - 2025 NVIDIA Corporation & Affiliates. # -# All rights reserved. # -# # -# This source code and the accompanying materials are made available under # -# the terms of the Apache License 2.0 which accompanies this distribution. # -# ============================================================================ # - -# Configuration file for the Sphinx documentation builder. -# -# This file only contains a selection of the most common options. For a full -# list see the documentation: -# https://www.sphinx-doc.org/en/master/usage/configuration.html - -# -- Path setup -------------------------------------------------------------- - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. - -import os -import sys - -sys.path.insert(0, os.path.abspath('@CUDAQ_INSTALL_DIR@')) -sys.path.insert(0, os.path.abspath('@CMAKE_BINARY_DIR@/python')) - -# -- Project information ----------------------------------------------------- - -project = 'NVIDIA CUDA-QX' -copyright = '2025, NVIDIA Corporation & Affiliates' -author = 'NVIDIA Corporation & Affiliates' - -# The version info for the project you're documenting, acts as replacement for -# |version| used in various places throughout the docs. - -# The short X.Y version. -version = os.getenv("CUDAQX_VERSION", "latest") - -# -- General configuration --------------------------------------------------- - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - # 'sphinx.ext.imgmath', - 'sphinx.ext.ifconfig', - 'sphinx.ext.autodoc', # to get documentation from python doc comments - 'sphinx.ext.autosummary', - 'sphinx.ext.autosectionlabel', - 'sphinx.ext.doctest', # test example codes in docs - 'sphinx.ext.extlinks', - 'sphinx.ext.intersphinx', - 'sphinx.ext.mathjax', - 'sphinx.ext.napoleon', # support google/numpy style docstrings - #'sphinx.ext.linkcode', - 'sphinx_reredirects', - 'breathe', - 'enum_tools.autoenum', # for pretty-print Python enums - 'myst_parser', # for including markdown files - 'sphinx_inline_tabs', # showing code blocks in multiple languages - 'nbsphinx', # for supporting jupyter notebooks - 'sphinx_copybutton', # allows for copy/paste of code cells - "sphinx_gallery.load_style", - "IPython.sphinxext.ipython_console_highlighting", -] - -nbsphinx_allow_errors = False -nbsphinx_thumbnails = { - # Default thumbnail if the notebook does not define a cell tag to specify the thumbnail. - # See also: https://nbsphinx.readthedocs.io/en/latest/subdir/gallery.html - '**': '@SPHINX_SOURCE@/_static/cuda_quantum_icon.svg' -} - -imgmath_latex_preamble = r'\usepackage{braket}' - -imgmath_image_format = 'svg' -imgmath_font_size = 14 -#imgmath_dvipng_args = ['-gamma', '1.5', '-D', '110', '-bg', 'Transparent'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['@SPHINX_SOURCE@/_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -source_suffix = { - '.rst': 'restructuredtext', - '.md': 'markdown', -} - -# The master toctree document. -master_doc = 'index' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = ['_templates'] - -# The reST default role (used for this markup: `text`) to use for all documents. -default_role = 'code' # NOTE: the following may be a better choice to error on the side of flagging anything that is referenced but but not declared -#default_role = 'cpp:any' # see https://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#cross-referencing - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'lightbulb' - -# autosummary is buggy: this must be py instead of cpp so that the domain setting -# can be propagated to the autogen'd rst files. -# primary_domain = 'py' - -# -- Options for HTML output ------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. - -html_theme = 'sphinx_rtd_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -html_theme_options = { - "collapse_navigation": False, - "sticky_navigation": False, - "prev_next_buttons_location": "both", - "style_nav_header_background": - "#76b900" # Set upper left search bar to NVIDIA green -} - -html_css_files = ['@SPHINX_SOURCE@/_static/cudaq_override.css'] - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['@SPHINX_SOURCE@/_static'] - -# Output file base name for HTML help builder. -htmlhelp_basename = 'cudaqxDoc' - -# A table of string replacements to apply to docs. These strings are unnecessary -# implementation details that add noise to the documentation. -doc_replacements = ( - ('_pycudaqx_qec_the_suffix_matters_cudaq_qec.qecrt.', ''), - ('mlir._mlir_libs._quakeDialects.cudaq_runtime.', ''), - ('*', '\*'), -) - -def fix_string(input): - """Fixes a string by applying the replacements in `doc_replacements`""" - if not isinstance(input, str): - return input - for old, new in doc_replacements: - input = input.replace(old, new) - return input - -def process_signature(app, what, name, obj, options, signature, return_annotation): - return (fix_string(signature), fix_string(return_annotation)) - -def process_docstring(app, what, name, obj, options, lines): - for i, line in enumerate(lines): - lines[i] = fix_string(line) - -def setup(app): - app.connect('autodoc-process-signature', process_signature) - app.connect('autodoc-process-docstring', process_docstring) - app.add_css_file('cudaq_override.css') - - -# -- Options for BREATHE ------------------------------------------------- - -breathe_default_project = "cudaqx" - -breathe_show_enumvalue_initializer = True - -# -- Other options ------------------------------------------------- - -autosummary_generate = True - -intersphinx_mapping = { - 'python': ('https://docs.python.org/3/', None), - 'numpy': ('https://numpy.org/doc/stable/', None), - 'cudaq': ('https://nvidia.github.io/cuda-quantum/latest', None) -} - -redirects = {"versions": "../latest/releases.html"} - -nitpick_ignore = [ - ('cpp:identifier', 'pid_t'), - ('cpp:identifier', 'uint8_t'), - ('cpp:identifier', 'uint32_t'), - ('cpp:identifier', 'vec2d'), - ('cpp:identifier', 'details::tensor_impl::scalar_type'), - ('cpp:identifier', 'cudaqx'), - ('cpp:identifier', 'size_t'), - ('cpp:identifier', 'details'), - ('cpp:identifier', 'spin_op'), - ('cpp:identifier', 'heterogeneous_map'), - ('cpp:identifier', 'cudaq::qkernel&)>'), - ('cpp:identifier', 'cudaq::qkernel'), - ('cpp:identifier', 'cudaq::qkernel'), - ('cpp:identifier', - 'cudaq::qkernel(patch, const std::vector&, const std::vector&)>' - ), - ('cpp:identifier', 'cudaq::qvector<>'), - ('cpp:identifier', 'cudaq::qview<>'), - ('cpp:identifier', 'cudaq::measure_result'), - ('py:class', 'cudaq.qview'), - ('py:class', 'cudaq.SpinOperator'), - ('py:class', 'SpinOperator'), - ('py:class', 'numpy.int32'), - ('py:class', 'numpy.uint8'), - ('py:class', 'cudaq.mlir._mlir_libs._quakeDialects.cudaq_runtime.qview') -] - -napoleon_google_docstring = True -napoleon_numpy_docstring = False -autosectionlabel_prefix_document = True -autosectionlabel_maxdepth = 2 -copybutton_copy_empty_lines = False -pybind11_compatibility = True diff --git a/docs/sphinx/examples/qec/cpp/circuit_level_noise.cpp b/docs/sphinx/examples/qec/cpp/circuit_level_noise.cpp deleted file mode 100644 index bf79984c..00000000 --- a/docs/sphinx/examples/qec/cpp/circuit_level_noise.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ -// [Begin Documentation] -// Compile and run with: -// nvq++ --enable-mlir --target=stim -lcudaq-qec circuit_level_noise.cpp -// ./a.out - -#include "cudaq.h" -#include "cudaq/qec/decoder.h" -#include "cudaq/qec/experiments.h" -#include "cudaq/qec/noise_model.h" - -int main() { - // Choose a QEC code - auto steane = cudaq::qec::get_code("steane"); - - // Access the parity check matrix - auto H = steane->get_parity(); - std::cout << "H:\n"; - H.dump(); - - // Access the logical observables - auto observables = steane->get_pauli_observables_matrix(); - auto Lz = steane->get_observables_z(); - - // Data qubits the logical Z observable is supported on - std::cout << "Lz:\n"; - Lz.dump(); - - // Observables are stacked as Z over X for mat-vec multiplication - std::cout << "Obs:\n"; - observables.dump(); - - // How many shots to run the experiment - int nShots = 3; - // For each shot, how many rounds of stabilizer measurements - int nRounds = 4; - - // can set seed for reproducibility - // cudaq::set_random_seed(1337); - cudaq::noise_model noise; - - // Add a depolarization noise channel after each cx gate - noise.add_all_qubit_channel("x", cudaq::depolarization2(/*probability*/ 0.01), - /*numControls*/ 1); - - // Perform a noisy z-basis memory circuit experiment - auto [syndromes, data] = cudaq::qec::sample_memory_circuit( - *steane, cudaq::qec::operation::prep0, nShots, nRounds, noise); - - // With noise, many syndromes will flip each QEC cycle, these are the - // syndrome differences from the previous cycle. - std::cout << "syndromes:\n"; - syndromes.dump(); - - // With noise, Lz will sometimes be flipped - std::cout << "data:\n"; - data.dump(); - - // Use z-measurements on data qubits to determine the logical mz - // In an x-basis experiment, use Lx. - auto logical_mz = Lz.dot(data.transpose()) % 2; - std::cout << "logical_mz each shot:\n"; - logical_mz.dump(); - - // Select a decoder - auto decoder = cudaq::qec::get_decoder("single_error_lut", H); - - // Initialize a pauli_frame to track the logical errors - cudaqx::tensor pauli_frame({observables.shape()[0]}); - - // Start a loop to count the number of logical errors - size_t numLerrors = 0; - for (size_t shot = 0; shot < nShots; ++shot) { - std::cout << "shot: " << shot << "\n"; - - for (size_t round = 0; round < nRounds; ++round) { - std::cout << "round: " << round << "\n"; - - // Access one row of the syndrome tensor - size_t count = shot * nRounds + round; - size_t stride = syndromes.shape()[1]; - cudaqx::tensor syndrome({stride}); - syndrome.borrow(syndromes.data() + stride * count); - std::cout << "syndrome:\n"; - syndrome.dump(); - - // Decode the syndrome - auto [converged, v_result] = decoder->decode(syndrome); - cudaqx::tensor result_tensor; - cudaq::qec::convert_vec_soft_to_tensor_hard(v_result, result_tensor); - std::cout << "decode result:\n"; - result_tensor.dump(); - - // See if the decoded result anti-commutes with observables - auto decoded_observables = observables.dot(result_tensor); - std::cout << "decoded observable:\n"; - decoded_observables.dump(); - - // update from previous stabilizer round - pauli_frame = (pauli_frame + decoded_observables) % 2; - std::cout << "pauli frame:\n"; - pauli_frame.dump(); - } - - // prep0 means we expected to measure out 0. - uint8_t expected_mz = 0; - // Apply the pauli frame correction to our logical measurement - uint8_t corrected_mz = (logical_mz.at({0, shot}) + pauli_frame.at({0})) % 2; - - // Check if Logical_mz + pauli_frame_X = 0? - std::cout << "Corrected readout: " << +corrected_mz << "\n"; - std::cout << "Expected readout: " << +expected_mz << "\n"; - if (corrected_mz != expected_mz) - numLerrors++; - std::cout << "\n"; - } - - std::cout << "numLogicalErrors: " << numLerrors << "\n"; -} diff --git a/docs/sphinx/examples/qec/cpp/code_capacity_noise.cpp b/docs/sphinx/examples/qec/cpp/code_capacity_noise.cpp deleted file mode 100644 index fbe89491..00000000 --- a/docs/sphinx/examples/qec/cpp/code_capacity_noise.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ -// [Begin Documentation] -// This example shows the primary cudaq::qec types: -// decoder, code -// -// Compile and run with -// nvq++ --enable-mlir --target=stim -lcudaq-qec code_capacity_noise.cpp -// ./a.out - -#include -#include -#include - -#include "cudaq.h" -#include "cudaq/qec/decoder.h" -#include "cudaq/qec/experiments.h" - -int main() { - auto steane = cudaq::qec::get_code("steane"); - auto Hz = steane->get_parity_z(); - std::vector t_shape = Hz.shape(); - - std::cout << "Hz.shape():\n"; - for (size_t elem : t_shape) - std::cout << elem << " "; - std::cout << "\n"; - - std::cout << "Hz:\n"; - Hz.dump(); - - auto Lz = steane->get_observables_x(); - std::cout << "Lz:\n"; - Lz.dump(); - - double p = 0.2; - size_t nShots = 5; - auto lut_decoder = cudaq::qec::get_decoder("single_error_lut", Hz); - - std::cout << "nShots: " << nShots << "\n"; - - // May want a order-2 tensor of syndromes - // access tensor by stride to write in an entire syndrome - cudaqx::tensor syndrome({Hz.shape()[0]}); - - int nErrors = 0; - for (size_t shot = 0; shot < nShots; ++shot) { - std::cout << "shot: " << shot << "\n"; - auto shot_data = cudaq::qec::generate_random_bit_flips(Hz.shape()[1], p); - std::cout << "shot data\n"; - shot_data.dump(); - - auto observable_z_data = Lz.dot(shot_data); - observable_z_data = observable_z_data % 2; - std::cout << "Data Lz state:\n"; - observable_z_data.dump(); - - auto syndrome = Hz.dot(shot_data); - syndrome = syndrome % 2; - std::cout << "syndrome:\n"; - syndrome.dump(); - - auto [converged, v_result] = lut_decoder->decode(syndrome); - cudaqx::tensor result_tensor; - // v_result is a std::vector, of soft information. We'll convert - // this to hard information and store as a tensor. - cudaq::qec::convert_vec_soft_to_tensor_hard(v_result, result_tensor); - std::cout << "decode result:\n"; - result_tensor.dump(); - - // check observable result - auto decoded_observable_z = Lz.dot(result_tensor); - std::cout << "decoded observable:\n"; - decoded_observable_z.dump(); - - // check how many observable operators were decoded correctly - // observable_z_data == decoded_observable_z This maps onto element wise - // addition (mod 2) - auto observable_flips = decoded_observable_z + observable_z_data; - observable_flips = observable_flips % 2; - std::cout << "Logical errors:\n"; - observable_flips.dump(); - std::cout << "\n"; - - // shot counts as a observable error unless all observables are correct - if (observable_flips.any()) { - nErrors++; - } - } - std::cout << "Total logical errors: " << nErrors << "\n"; - - // Full data gen in function call - auto [syn, data] = cudaq::qec::sample_code_capacity(Hz, nShots, p); - std::cout << "Numerical experiment:\n"; - std::cout << "Data:\n"; - data.dump(); - std::cout << "Syn:\n"; - syn.dump(); -} diff --git a/docs/sphinx/examples/qec/python/circuit_level_noise.py b/docs/sphinx/examples/qec/python/circuit_level_noise.py deleted file mode 100644 index a0202ac5..00000000 --- a/docs/sphinx/examples/qec/python/circuit_level_noise.py +++ /dev/null @@ -1,93 +0,0 @@ -# ============================================================================ # -# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # -# All rights reserved. # -# # -# This source code and the accompanying materials are made available under # -# the terms of the Apache License 2.0 which accompanies this distribution. # -# ============================================================================ # -# [Begin Documentation] -import numpy as np -import cudaq -import cudaq_qec as qec - -# Get a QEC code -cudaq.set_target("stim") -steane = qec.get_code("steane") - -# Get the parity check matrix of a code -# Can get the full code, or for CSS codes -# just the X or Z component -H = steane.get_parity() -print(f"H:\n{H}") -observables = steane.get_pauli_observables_matrix() -Lz = steane.get_observables_z() -print(f"observables:\n{observables}") -print(f"Lz:\n{Lz}") - -nShots = 3 -nRounds = 4 - -# error probabily -p = 0.01 -noise = cudaq.NoiseModel() -noise.add_all_qubit_channel("x", cudaq.Depolarization2(p), 1) - -# prepare logical |0> state, tells the sampler to do z-basis experiment -statePrep = qec.operation.prep0 -# our expected measurement in this state is 0 -expected_value = 0 - -# sample the steane memory circuit with noise on each cx gate -# reading out the syndromes after each stabilizer round (xor'd against the previous) -# and readout out the data qubits at the end of the experiment -syndromes, data = qec.sample_memory_circuit(steane, statePrep, nShots, nRounds, - noise) -print("From sample function:\n") -print("syndromes:\n", syndromes) -print("data:\n", data) - -# Get a decoder -decoder = qec.get_decoder("single_error_lut", H) -nLogicalErrors = 0 - -# Logical Mz each shot (use Lx if preparing in X-basis) -logical_measurements = (Lz @ data.transpose()) % 2 -# only one logical qubit, so do not need the second axis -logical_measurements = logical_measurements.flatten() -print("LMz:\n", logical_measurements) - -# organize data by shot and round if desired -syndromes = syndromes.reshape((nShots, nRounds, syndromes.shape[1])) - -# initialize a Pauli frame to track logical flips -# through the stabilizer rounds -pauli_frame = np.array([0, 0], dtype=np.uint8) -for shot in range(0, nShots): - print("shot:", shot) - for syndrome in syndromes[shot]: - print("syndrome:", syndrome) - # decode the syndrome - convergence, result = decoder.decode(syndrome) - data_prediction = np.array(result, dtype=np.uint8) - - # see if the decoded result anti-commutes with the observables - print("decode result:", data_prediction) - decoded_observables = (observables @ data_prediction) % 2 - print("decoded_observables:", decoded_observables) - - # update pauli frame - pauli_frame = (pauli_frame + decoded_observables) % 2 - print("pauli frame:", pauli_frame) - - # after pauli frame has tracked corrections through the rounds - # apply the pauli frame correction to the measurement, and see - # if this matches the state we intended to prepare - # We prepared |0>, so we check if logical measurement Mz + Pf_X = 0 - corrected_mz = (logical_measurements[shot] + pauli_frame[0]) % 2 - print("Expected value:", expected_value) - print("Corrected value:", corrected_mz) - if (corrected_mz != expected_value): - nLogicalErrors += 1 - -# Count how many shots the decoder failed to correct the errors -print("Number of logical errors:", nLogicalErrors) diff --git a/docs/sphinx/examples/qec/python/code_capacity_noise.py b/docs/sphinx/examples/qec/python/code_capacity_noise.py deleted file mode 100644 index e25be036..00000000 --- a/docs/sphinx/examples/qec/python/code_capacity_noise.py +++ /dev/null @@ -1,65 +0,0 @@ -# ============================================================================ # -# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # -# All rights reserved. # -# # -# This source code and the accompanying materials are made available under # -# the terms of the Apache License 2.0 which accompanies this distribution. # -# ============================================================================ # - -# [Begin Documentation] -import numpy as np -import cudaq_qec as qec - -# Get a QEC code -steane = qec.get_code("steane") - -# Get the parity check matrix of a code -# Can get the full code, or for CSS codes -# just the X or Z component -Hz = steane.get_parity_z() -print(f"Hz:\n{Hz}") -observable = steane.get_observables_z() -print(f"observable:\n{observable}") - -# error probabily -p = 0.1 -# Get a decoder -decoder = qec.get_decoder("single_error_lut", Hz) - -# Perform a code capacity noise model numerical experiment -nShots = 10 -nLogicalErrors = 0 -for i in range(nShots): - print(f"shot: {i}") - - # Generate noisy data - data = qec.generate_random_bit_flips(Hz.shape[1], p) - print(f"data: {data}") - - # Calculate which syndromes are flagged. - syndrome = Hz @ data % 2 - print(f"syndrome: {syndrome}") - - # Decode the syndrome to predict what happen to the data - convergence, result = decoder.decode(syndrome) - data_prediction = np.array(result, dtype=np.uint8) - print(f"data_prediction: {data_prediction}") - - # See if this prediction flipped the observable - predicted_observable = observable @ data_prediction % 2 - print(f"predicted_observable: {predicted_observable}") - - # See if the observable was actually flipped - actual_observable = observable @ data % 2 - print(f"actual_observable: {actual_observable}") - if (predicted_observable != actual_observable): - nLogicalErrors += 1 - -# Count how many shots the decoder failed to correct the errors -print(f"{nLogicalErrors} logical errors in {nShots} shots\n") - -# Can also generate syndromes and data from a single line with: -syndromes, data = qec.sample_code_capacity(Hz, nShots, p) -print("From sample function:") -print("syndromes:\n", syndromes) -print("data:\n", data) diff --git a/docs/sphinx/examples/qec/python/nv-qldpc-decoder.py b/docs/sphinx/examples/qec/python/nv-qldpc-decoder.py deleted file mode 100644 index 42d4a1f0..00000000 --- a/docs/sphinx/examples/qec/python/nv-qldpc-decoder.py +++ /dev/null @@ -1,227 +0,0 @@ -# ============================================================================ # -# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # -# All rights reserved. # -# # -# This source code and the accompanying materials are made available under # -# the terms of the Apache License 2.0 which accompanies this distribution. # -# ============================================================================ # -# [Begin Documentation] - -import numpy as np -from scipy.sparse import csr_matrix -import cudaq_qec as qec -import json -import time - -# For fetching data -import requests -import bz2 -import os - -# Note: running this script will automatically download data if necessary. - -### Helper functions ### - - -def parse_csr_mat(j, dims, mat_name): - """ - Parse a CSR-style matrix from a JSON file using SciPy's sparse matrix utilities. - """ - assert len(dims) == 2, "dims must be a tuple of two integers" - - # Extract indptr and indices from the JSON. - indptr = np.array(j[f"{mat_name}_indptr"], dtype=int) - indices = np.array(j[f"{mat_name}_indices"], dtype=int) - - # Check that the CSR structure is consistent. - assert len(indptr) == dims[0] + 1, "indptr length must equal dims[0] + 1" - assert np.all( - indices < dims[1]), "All column indices must be less than dims[1]" - - # Create a data array of ones. - data = np.ones(indptr[-1], dtype=np.uint8) - - # Build the CSR matrix and return it as a dense numpy array. - csr = csr_matrix((data, indices, indptr), shape=dims, dtype=np.uint8) - return csr.toarray() - - -def parse_H_csr(j, dims): - """ - Parse a CSR-style parity check matrix from an input file in JSON format" - """ - return parse_csr_mat(j, dims, "H") - - -def parse_obs_csr(j, dims): - """ - Parse a CSR-style observable matrix from an input file in JSON format" - """ - return parse_csr_mat(j, dims, "obs_mat") - - -### Main decoder loop ### - - -def run_decoder(filename, num_shots, run_as_batched): - """ - Load a JSON file and decode "num_shots" syndromes. - """ - t_load_begin = time.time() - with open(filename, "r") as f: - j = json.load(f) - - dims = j["shape"] - assert len(dims) == 2 - - # Read the Parity Check Matrix - H = parse_H_csr(j, dims) - syndrome_length, block_length = dims - t_load_end = time.time() - - print(f"{filename} parsed in {1e3 * (t_load_end-t_load_begin)} ms") - - error_rate_vec = np.array(j["error_rate_vec"]) - assert len(error_rate_vec) == block_length - obs_mat_dims = j["obs_mat_shape"] - obs_mat = parse_obs_csr(j, obs_mat_dims) - assert dims[1] == obs_mat_dims[0] - file_num_trials = j["num_trials"] - num_shots = min(num_shots, file_num_trials) - print( - f'Your JSON file has {file_num_trials} shots. Running {num_shots} now.') - - # osd_method: 0=Off, 1=OSD-0, 2=Exhaustive, 3=Combination Sweep - osd_method = 1 - - # When osd_method is: - # 2) there are 2^osd_order additional error mechanisms checked. - # 3) there are an additional k + osd_order*(osd_order-1)/2 error - # mechanisms checked. - # Ref: https://arxiv.org/pdf/2005.07016 - osd_order = 0 - - # Maximum number of BP iterations before attempting OSD (if necessary) - max_iter = 50 - - nv_dec_args = { - "max_iterations": max_iter, - "error_rate_vec": error_rate_vec, - "use_sparsity": True, - "use_osd": osd_method > 0, - "osd_order": osd_order, - "osd_method": osd_method - } - - if run_as_batched: - # Perform BP processing for up to 1000 syndromes per batch. If there - # are more than 1000 syndromes, the decoder will chunk them up and - # process each batch sequentially under the hood. - nv_dec_args['bp_batch_size'] = min(1000, num_shots) - - try: - nv_dec_gpu_and_cpu = qec.get_decoder("nv-qldpc-decoder", H, - **nv_dec_args) - except Exception as e: - print( - 'The nv-qldpc-decoder is not available with your current CUDA-Q ' + - 'QEC installation.') - exit(0) - decoding_time = 0 - bp_converged_flags = [] - num_logical_errors = 0 - - # Batched API - if run_as_batched: - syndrome_list = [] - obs_truth_list = [] - for i in range(num_shots): - syndrome = j["trials"][i]["syndrome_truth"] - obs_truth = j["trials"][i]["obs_truth"] - syndrome_list.append(syndrome) - obs_truth_list.append(obs_truth) - t0 = time.time() - results = nv_dec_gpu_and_cpu.decode_batch(syndrome_list) - t1 = time.time() - decoding_time += t1 - t0 - for r, obs_truth in zip(results, obs_truth_list): - bp_converged_flags.append(r.converged) - dec_result = np.array(r.result, dtype=np.uint8) - - # See if this prediction flipped the observable - predicted_observable = obs_mat.T @ dec_result % 2 - print(f"predicted_observable: {predicted_observable}") - - # See if the observable was actually flipped according to the truth - # data - actual_observable = np.array(obs_truth, dtype=np.uint8) - print(f"actual_observable: {actual_observable}") - - if np.sum(predicted_observable != actual_observable) > 0: - num_logical_errors += 1 - - # Non-batched API - else: - for i in range(num_shots): - syndrome = j["trials"][i]["syndrome_truth"] - obs_truth = j["trials"][i]["obs_truth"] - - t0 = time.time() - bp_converged, dec_result = nv_dec_gpu_and_cpu.decode(syndrome) - t1 = time.time() - trial_diff = t1 - t0 - decoding_time += trial_diff - - dec_result = np.array(dec_result, dtype=np.uint8) - bp_converged_flags.append(bp_converged) - - # See if this prediction flipped the observable - predicted_observable = obs_mat.T @ dec_result % 2 - print(f"predicted_observable: {predicted_observable}") - - # See if the observable was actually flipped according to the truth - # data - actual_observable = np.array(obs_truth, dtype=np.uint8) - print(f"actual_observable: {actual_observable}") - - if np.sum(predicted_observable != actual_observable) > 0: - num_logical_errors += 1 - - # Count how many shots the decoder failed to correct the errors - print(f"{num_logical_errors} logical errors in {num_shots} shots") - print( - f"Number of shots that converged with BP processing: {np.sum(np.array(bp_converged_flags))}" - ) - print( - f"Average decoding time for {num_shots} shots was {1e3 * decoding_time / num_shots} ms per shot" - ) - - -if __name__ == "__main__": - # See other test data options in https://github.com/NVIDIA/cudaqx/releases/tag/0.2.0 - filename = 'osd_1008_8785_0.001.json' - bz2filename = filename + '.bz2' - if not os.path.exists(filename): - url = f"https://github.com/NVIDIA/cudaqx/releases/download/0.2.0/{bz2filename}" - - print(f'Downloading data from {url}') - - # Download the file - response = requests.get(url, stream=True) - response.raise_for_status() # Raise an error if download fails - with open(bz2filename, "wb") as f: - for chunk in response.iter_content(chunk_size=8192): - f.write(chunk) - - print(f'Decompressing {bz2filename} into {filename}') - - # Decompress the file - with bz2.BZ2File(bz2filename, "rb") as f_in, open(filename, - "wb") as f_out: - f_out.write(f_in.read()) - - print(f"Decompressed file saved as {filename}") - - num_shots = 100 - run_as_batched = True - run_decoder(filename, num_shots, run_as_batched) diff --git a/docs/sphinx/examples/qec/python/pseudo_threshold.py b/docs/sphinx/examples/qec/python/pseudo_threshold.py deleted file mode 100644 index fddf9ed5..00000000 --- a/docs/sphinx/examples/qec/python/pseudo_threshold.py +++ /dev/null @@ -1,68 +0,0 @@ -# ============================================================================ # -# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # -# All rights reserved. # -# # -# This source code and the accompanying materials are made available under # -# the terms of the Apache License 2.0 which accompanies this distribution. # -# ============================================================================ # - -# [Begin Documentation] -import numpy as np -import cudaq_qec as qec -import matplotlib.pyplot as plt - -# Get a QEC code -# steane = qec.get_code("repetition", distance=9) -steane = qec.get_code("steane") - -# Get the parity check matrix of a code -# Can get the full code, or for CSS codes -# just the X or Z component -Hz = steane.get_parity_z() -observable = steane.get_observables_z() - -# Get a decoder -decoder = qec.get_decoder("single_error_lut", Hz) - -# Perform a code capacity noise model numerical experiment -nShots = 100000 -LERates = [] -# PERates = np.linspace(0.1, 0.50, num=20) -PERates = np.logspace(-2.0, -0.5, num=25) - -for p in PERates: - nLogicalErrors = 0 - for i in range(nShots): - data = qec.generate_random_bit_flips(Hz.shape[1], p) - # Calculate which syndromes are flagged. - syndrome = Hz @ data % 2 - - convergence, result = decoder.decode(syndrome) - data_prediction = np.array(result) - - predicted_observable = observable @ data_prediction % 2 - - actual_observable = observable @ data % 2 - if (predicted_observable != actual_observable): - nLogicalErrors += 1 - LERates.append(nLogicalErrors / nShots) - -# Count how many shots the decoder failed to correct the errors -print("PERates:", PERates) -print("LERates:", LERates) - -# Create a figure and an axes object -fig, ax = plt.subplots() - -# Plot the data -ax.loglog(PERates, LERates) -ax.loglog(PERates, PERates, 'r--', label='y=x') - -# Add a title and labels -ax.set_title("Steane Code") -ax.set_xlabel("Physical Error Rate") -ax.set_ylabel("Logical Error Rate") - -# Show the plot -# plt.show() -# plt.savefig("myplot.png") diff --git a/docs/sphinx/examples/qec/python/repetition_code_fine_grain_noise.py b/docs/sphinx/examples/qec/python/repetition_code_fine_grain_noise.py deleted file mode 100644 index af06ce41..00000000 --- a/docs/sphinx/examples/qec/python/repetition_code_fine_grain_noise.py +++ /dev/null @@ -1,184 +0,0 @@ -# ============================================================================ # -# Copyright (c) 2025 NVIDIA Corporation & Affiliates. # -# All rights reserved. # -# # -# This source code and the accompanying materials are made available under # -# the terms of the Apache License 2.0 which accompanies this distribution. # -# ============================================================================ # - -# [Begin Documentation] -import cudaq -import cudaq_qec as qec -import numpy as np - -nRounds = 3 -nShots = 500 -# Physical error rate -p_per_round = 0.01 -p_per_mz = 0.01 - - -# Construct the measurement error syndrome matrix based on the distance and number of rounds -def construct_measurement_error_syndrome(distance, nRounds): - num_stabilizers = distance - 1 - num_mea_q = num_stabilizers * nRounds - - syndrome_rows = [] - - # In this scheme, need two rounds for each measurement syndrome - for i in range(nRounds - 1): - for j in range(num_stabilizers): - syndrome = np.zeros((num_mea_q,), dtype=np.uint8) - - # The error on ancilla (j) in round (i) affects stabilizer checks at two positions: - # First occurrence in round i - pos1 = i * num_stabilizers + j - # Second occurrence in round i+1 - pos2 = (i + 1) * num_stabilizers + j - - # Mark the syndrome - syndrome[pos1] = 1 - syndrome[pos2] = 1 - - syndrome_rows.append(syndrome) - - return np.array(syndrome_rows).T - - -# Generate the parity check matrix for n-rounds by duplicating the input parity check matrix Hz -# and appending the measurement error syndrome matrix. -def get_circuit_level_pcm(distance, nRounds, Hz): - if nRounds < 2: - raise ValueError("nRounds must be greater than or equal to 2") - if distance < 3: - raise ValueError("distance must be greater than or equal to 3") - - # Parity check matrix for a single round - H = np.array(Hz) - - # Extends H to nRounds - rows, cols = H.shape - H_nrounds = np.zeros((rows * nRounds, cols * nRounds), dtype=np.uint8) - for i in range(nRounds): - H_nrounds[i * rows:(i + 1) * rows, i * cols:(i + 1) * cols] = H - print("H_nrounds\n", H_nrounds) - - # Construct the measurement error syndrome matrix for Z errors - H_mz = construct_measurement_error_syndrome(distance, nRounds) - print("H_mz\n", H_mz) - assert H_nrounds.shape[0] == H_mz.shape[ - 0], "Dimensions of H_nrounds and H_mz do not match" - - # Append columns for measurement errors to H - H_pcm = np.concatenate((H_nrounds, H_mz), axis=1) - print(f"H_pcm:\n{H_pcm}") - - return H_pcm - - -# Example of how to construct a repetition code with a distance of 3 and random -# bit flip errors applied to the data qubits -@cudaq.kernel -def three_qubit_repetition_code(): - data_qubits = cudaq.qvector(3) - ancilla_qubits = cudaq.qvector(2) - - # Initialize the logical |1> state as |111> - x(data_qubits) - - for i in range(nRounds): - # Random Bit Flip Errors - for j in range(3): - cudaq.apply_noise(cudaq.XError, p_per_round, data_qubits[j]) - - # Extract Syndromes - h(ancilla_qubits) - - # First Parity Check - z.ctrl(ancilla_qubits[0], data_qubits[0]) - z.ctrl(ancilla_qubits[0], data_qubits[1]) - - # Second Parity Check - z.ctrl(ancilla_qubits[1], data_qubits[1]) - z.ctrl(ancilla_qubits[1], data_qubits[2]) - - h(ancilla_qubits) - - # Measure the ancilla qubits - s0 = mz(ancilla_qubits[0]) - s1 = mz(ancilla_qubits[1]) - reset(ancilla_qubits[0]) - reset(ancilla_qubits[1]) - - # Final measurement to get the data qubits - mz(data_qubits) - - -# Create a noise model -noise_model = cudaq.NoiseModel() -# Add measurement noise -noise_model.add_all_qubit_channel("mz", cudaq.BitFlipChannel(p_per_mz)) - -# Run the kernel and observe results -# The percent of samples that are 000 corresponds to the logical error rate -cudaq.set_target("stim") -result = cudaq.sample(three_qubit_repetition_code, - shots_count=nShots, - noise_model=noise_model, - explicit_measurements=True) - -# The following section will demonstrate how to decode the results -# Get the parity check matrix for n-rounds of the repetition code -Hz = [[1, 1, 0], [0, 1, 1]] # Parity check matrix for 1 round -H_pcm = get_circuit_level_pcm(3, nRounds, Hz) - -# Get observables -observables = np.array([1, 0, 0, 0, 0, 0], dtype=np.uint8) -Lz = np.array([1, 0, 0], dtype=np.uint8) -print(f"observables:\n{observables}") -print(f"Lz:\n{Lz}") -# Pad the observables to be the same dimension as the decoded observable -Lz_nrounds = np.tile(Lz, nRounds) -pad_size = max(0, H_pcm.shape[1] - Lz_nrounds.shape[0]) -Lz_nround_mz = np.pad(Lz_nrounds, (0, pad_size), mode='constant') -print(f"Lz_nround_mz\n{Lz_nround_mz}") - -# Get a decoder -decoder = qec.get_decoder("single_error_lut", H_pcm) -nLogicalErrors = 0 - -# initialize a Pauli frame to track logical flips -# through the stabilizer rounds. Only need the Z component for the repetition code. -pauli_frame = np.array([0, 0], dtype=np.uint8) -expected_value = 1 -for shot, outcome in enumerate(result.get_sequential_data()): - outcome_array = np.array([int(bit) for bit in outcome], dtype=np.uint8) - syndrome = outcome_array[:len(outcome_array) - 3] - data = outcome_array[len(outcome_array) - 3:] - print("\nshot:", shot) - print("syndrome:", syndrome) - - # Decode the syndrome - convergence, result = decoder.decode(syndrome) - data_prediction = np.array(result, dtype=np.uint8) - - # See if the decoded result anti-commutes with the observables - print("decode result:", data_prediction) - decoded_observables = (Lz_nround_mz @ data_prediction) % 2 - print("decoded_observables:", decoded_observables) - - # update pauli frame - pauli_frame[0] = (pauli_frame[0] + decoded_observables) % 2 - print("pauli frame:", pauli_frame) - - logical_measurements = (Lz @ data.transpose()) % 2 - print("LMz:", logical_measurements) - - corrected_mz = (logical_measurements + pauli_frame[0]) % 2 - print("Expected value:", expected_value) - print("Corrected value:", corrected_mz) - if (corrected_mz != expected_value): - nLogicalErrors += 1 - -# Count how many shots the decoder failed to correct the errors -print("\nNumber of logical errors:", nLogicalErrors) diff --git a/docs/sphinx/examples/solvers/cpp/adapt_h2.cpp b/docs/sphinx/examples/solvers/cpp/adapt_h2.cpp deleted file mode 100644 index 527bb4f5..00000000 --- a/docs/sphinx/examples/solvers/cpp/adapt_h2.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ -// [Begin Documentation] -#include "cudaq.h" -#include "cudaq/solvers/adapt.h" -#include "cudaq/solvers/operators.h" - -// Compile and run with -// nvq++ --enable-mlir -lcudaq-solvers adapt_h2.cpp -o adapt_ex -// ./adapt_ex - -int main() { - // Create the molecular hamiltonian - cudaq::solvers::molecular_geometry geometry{{"H", {0., 0., 0.}}, - {"H", {0., 0., .7474}}}; - auto molecule = cudaq::solvers::create_molecule( - geometry, "sto-3g", 0, 0, {.casci = true, .verbose = true}); - - // Get the spin operator - auto h = molecule.hamiltonian; - - // Create the operator pool - std::vector opPool = cudaq::solvers::get_operator_pool( - "spin_complement_gsd", {{"num-orbitals", h.num_qubits() / 2}}); - - // Run ADAPT - auto [energy, thetas, ops] = cudaq::solvers::adapt_vqe( - [](cudaq::qvector<> &q) __qpu__ { - x(q[0]); - x(q[1]); - }, - h, opPool, {{"grad_norm_tolerance", 1e-3}}); - - printf("Final = %.12lf\n", energy); -} diff --git a/docs/sphinx/examples/solvers/cpp/molecular_docking_qaoa.cpp b/docs/sphinx/examples/solvers/cpp/molecular_docking_qaoa.cpp deleted file mode 100644 index 3b115cfe..00000000 --- a/docs/sphinx/examples/solvers/cpp/molecular_docking_qaoa.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ -// [Begin Documentation] -#include "cudaq.h" -#include "cudaq/solvers/operators.h" -#include "cudaq/solvers/qaoa.h" - -// Compile and run with -// nvq++ --enable-mlir -lcudaq-solvers molecular_docking_qaoa.cpp -// ./a.out - -int main() { - - // Create the ligand-configuration graph - cudaqx::graph g; - std::vector weights{0.6686, 0.6686, 0.6686, 0.1453, 0.1453, 0.1453}; - std::vector> edges{{0, 1}, {0, 2}, {0, 4}, {0, 5}, - {1, 2}, {1, 3}, {1, 5}, {2, 3}, - {2, 4}, {3, 4}, {3, 5}, {4, 5}}; - for (std::size_t node = 0; auto weight : weights) - g.add_node(node++, weight); - - for (auto &edge : edges) - g.add_edge(edge.first, edge.second); - - // Set some parameters we'll need - double penalty = 6.0; - std::size_t numLayers = 3; - - // Create the Clique Hamiltonian - auto H = cudaq::solvers::get_clique_hamiltonian(g, penalty); - - // Get the number of required variational parameters - auto numParams = cudaq::solvers::get_num_qaoa_parameters( - H, numLayers, - {{"full_parameterization", true}, {"counterdiabatic", true}}); - - // Create the initial parameters to begin optimization - auto initParams = cudaq::random_vector(-M_PI / 8., M_PI / 8., numParams); - - // Run QAOA, specify full parameterization and counterdiabatic - // Full parameterization uses an optimization parameter for - // every term in the clique Hamiltonian and the mixer hamiltonian. - // Specifying counterdiabatic adds extra Ry rotations at the - // end of each layer. - auto [opt_value, opt_params, opt_config] = cudaq::solvers::qaoa( - H, numLayers, initParams, - {{"full_parameterization", true}, {"counterdiabatic", true}}); - - // Print out the results - std::cout << "Optimal energy: " << opt_value << "\n"; - std::cout << "Sampled states: "; - opt_config.dump(); - std::cout << "Optimal configuraiton: " << opt_config.most_probable() << "\n"; -} diff --git a/docs/sphinx/examples/solvers/cpp/uccsd_vqe.cpp b/docs/sphinx/examples/solvers/cpp/uccsd_vqe.cpp deleted file mode 100644 index 513b6996..00000000 --- a/docs/sphinx/examples/solvers/cpp/uccsd_vqe.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2024 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ -// [Begin Documentation] -#include "cudaq.h" -#include "cudaq/solvers/operators.h" -#include "cudaq/solvers/stateprep/uccsd.h" -#include "cudaq/solvers/vqe.h" - -// Compile and run with -// nvq++ --enable-mlir -lcudaq-solvers uccsd_vqe.cpp -o uccsd_vqe -// ./uccsd_vqe - -int main() { - // Create the molecular hamiltonian - cudaq::solvers::molecular_geometry geometry{{"H", {0., 0., 0.}}, - {"H", {0., 0., .7474}}}; - auto molecule = cudaq::solvers::create_molecule( - geometry, "sto-3g", 0, 0, {.casci = true, .verbose = true}); - - // Get the spin operator - auto h = molecule.hamiltonian; - - // Get the number of electrons and qubits - auto numElectrons = molecule.n_electrons; - auto numQubits = molecule.n_orbitals * 2; - - // Create an initial set of parameters for the optimization - auto numParams = cudaq::solvers::stateprep::get_num_uccsd_parameters( - numElectrons, numQubits); - std::vector init(numParams, -2.); - - // Run VQE - auto [energy, thetas, ops] = cudaq::solvers::vqe( - [&](std::vector params, std::size_t numQubits, - std::size_t numElectrons) __qpu__ { - cudaq::qvector q(numQubits); - for (auto i : cudaq::range(numElectrons)) - x(q[i]); - - cudaq::solvers::stateprep::uccsd(q, params, numElectrons); - }, - molecule.hamiltonian, init, - [&](std::vector x) { - return std::make_tuple(x, numQubits, numElectrons); - }, - {{"verbose", true}}); - - printf("Final = %.12lf\n", energy); -} diff --git a/docs/sphinx/examples/solvers/python/adapt_h2.py b/docs/sphinx/examples/solvers/python/adapt_h2.py deleted file mode 100644 index f40bafa8..00000000 --- a/docs/sphinx/examples/solvers/python/adapt_h2.py +++ /dev/null @@ -1,55 +0,0 @@ -# ============================================================================ # -# Copyright (c) 2024 NVIDIA Corporation & Affiliates. # -# All rights reserved. # -# # -# This source code and the accompanying materials are made available under # -# the terms of the Apache License 2.0 which accompanies this distribution. # -# ============================================================================ # - -# [Begin Documentation] -import cudaq, cudaq_solvers as solvers - -# Run this script with -# python3 adapt_h2.py -# -# In order to leverage CUDA-Q MQPU and distribute the work across -# multiple QPUs (thereby observing a speed-up), set the target and -# use MPI: -# -# cudaq.set_target('nvidia', mqpu=True) -# cudaq.mpi.initialize() -# -# run with -# -# mpiexec -np N and vary N to see the speedup... -# e.g. mpiexec -np 2 python3 adapt_h2_mqpu.py -# -# End the script with -# cudaq.mpi.finalize() - -# Create the molecular hamiltonian -geometry = [('H', (0., 0., 0.)), ('H', (0., 0., .7474))] -molecule = solvers.create_molecule(geometry, 'sto-3g', 0, 0, casci=True) - -# Create the ADAPT operator pool -operators = solvers.get_operator_pool("spin_complement_gsd", - num_orbitals=molecule.n_orbitals) - -# Get the number of electrons so we can -# capture it in the initial state kernel -numElectrons = molecule.n_electrons - - -# Define the initial Hartree Fock state -@cudaq.kernel -def initState(q: cudaq.qview): - for i in range(numElectrons): - x(q[i]) - - -# Run ADAPT-VQE -energy, thetas, ops = solvers.adapt_vqe(initState, molecule.hamiltonian, - operators) - -# Print the result. -print(" = ", energy) diff --git a/docs/sphinx/examples/solvers/python/generate_molecular_hamiltonians.py b/docs/sphinx/examples/solvers/python/generate_molecular_hamiltonians.py deleted file mode 100644 index c5e719c2..00000000 --- a/docs/sphinx/examples/solvers/python/generate_molecular_hamiltonians.py +++ /dev/null @@ -1,85 +0,0 @@ -# ============================================================================ # -# Copyright (c) 2024 NVIDIA Corporation & Affiliates. # -# All rights reserved. # -# # -# This source code and the accompanying materials are made available under # -# the terms of the Apache License 2.0 which accompanies this distribution. # -# ============================================================================ # - -# [Begin Documentation] -import cudaq_solvers as solvers - -# Generate active space Hamiltonian using HF molecular orbitals - -geometry = [('N', (0.0, 0.0, 0.5600)), ('N', (0.0, 0.0, -0.5600))] -molecule = solvers.create_molecule(geometry, - 'sto-3g', - 0, - 0, - nele_cas=2, - norb_cas=3, - verbose=True) - -print('N2 HF Hamiltonian') -print('Energies : ', molecule.energies) -print('No. of orbitals: ', molecule.n_orbitals) -print('No. of electrons: ', molecule.n_electrons) - -# Generate active space Hamiltonian using natural orbitals from MP2 - -geometry = [('N', (0.0, 0.0, 0.5600)), ('N', (0.0, 0.0, -0.5600))] -molecule = solvers.create_molecule(geometry, - 'sto-3g', - 0, - 0, - nele_cas=2, - norb_cas=3, - MP2=True, - integrals_natorb=True, - verbose=True) - -print('N2 Natural Orbitals from MP2 Hamiltonian') -print('Energies: ', molecule.energies) -print('No. of orbitals: ', molecule.n_orbitals) -print('No. of electrons: ', molecule.n_electrons) - -# Generate active space Hamiltonian using casscf orbitals, -# where the active space of the casscf was defined from HF molecular orbitals - -geometry = [('N', (0.0, 0.0, 0.5600)), ('N', (0.0, 0.0, -0.5600))] -molecule = solvers.create_molecule(geometry, - 'sto-3g', - 0, - 0, - nele_cas=2, - norb_cas=3, - casscf=True, - integrals_casscf=True, - verbose=True) - -print('N2 Active Space Hamiltonian Using CASSF Orbitals - HF orbitals') -print('Energies: ', molecule.energies) -print('No. of orbitals: ', molecule.n_orbitals) -print('No. of electrons: ', molecule.n_electrons) - -# Generate active space Hamiltonian using casscf orbitals, -# where the active space of the casscf was defined from the MP2 natural orbitals. - -geometry = [('N', (0.0, 0.0, 0.5600)), ('N', (0.0, 0.0, -0.5600))] -molecule = solvers.create_molecule(geometry, - 'sto-3g', - 0, - 0, - nele_cas=2, - norb_cas=3, - MP2=True, - natorb=True, - casscf=True, - integrals_casscf=True, - verbose=True) - -print('N2 Active Space Hamiltonian Using CASSF Orbitals - MP2 natural orbitals') -print('N2 HF Hamiltonian') -print('Energies: ', molecule.energies) -print('No. of orbitals: ', molecule.n_orbitals) -print('No. of electrons: ', molecule.n_electrons) diff --git a/docs/sphinx/examples/solvers/python/molecular_docking_qaoa.py b/docs/sphinx/examples/solvers/python/molecular_docking_qaoa.py deleted file mode 100644 index a9784ec0..00000000 --- a/docs/sphinx/examples/solvers/python/molecular_docking_qaoa.py +++ /dev/null @@ -1,53 +0,0 @@ -# ============================================================================ # -# Copyright (c) 2024 NVIDIA Corporation & Affiliates. # -# All rights reserved. # -# # -# This source code and the accompanying materials are made available under # -# the terms of the Apache License 2.0 which accompanies this distribution. # -# ============================================================================ # - -# [Begin Documentation] -import cudaq, cudaq_solvers as solvers -import networkx as nx, numpy as np - -# Create the ligand-configuration graph -G = nx.Graph() -edges = [[0, 1], [0, 2], [0, 4], [0, 5], [1, 2], [1, 3], [1, 5], [2, 3], [2, 4], - [3, 4], [3, 5], [4, 5]] -weights = [0.6686, 0.6686, 0.6686, 0.1453, 0.1453, 0.1453] -for i, weight in enumerate(weights): - G.add_node(i, weight=weight) -G.add_edges_from(edges) - -# Set some parameters we'll need -penalty = 6.0 -num_layers = 3 - -# Create the Clique Hamiltonian -H = solvers.get_clique_hamiltonian(G, penalty=penalty) - -# Get the number of parameters we'll need -parameter_count = solvers.get_num_qaoa_parameters(H, - num_layers, - full_parameterization=True, - counterdiabatic=True) - -# Create the initial parameters to begin optimization -init_params = np.random.uniform(-np.pi / 8, np.pi / 8, parameter_count) - -# Run QAOA, specify full parameterization and counterdiabatic -# Full parameterization uses an optimization parameter for -# every term in the clique Hamiltonian and the mixer hamiltonian. -# Specifying counterdiabatic adds extra Ry rotations at the -# end of each layer. -opt_value, opt_params, opt_config = solvers.qaoa(H, - num_layers, - init_params, - full_parameterization=True, - counterdiabatic=True) - -# Print the results -print() -print('Optimal energy: ', opt_value) -print('Sampled states: ', opt_config) -print('Optimal Configuration: ', opt_config.most_probable()) diff --git a/docs/sphinx/examples/solvers/python/uccsd_vqe.py b/docs/sphinx/examples/solvers/python/uccsd_vqe.py deleted file mode 100644 index cf01de22..00000000 --- a/docs/sphinx/examples/solvers/python/uccsd_vqe.py +++ /dev/null @@ -1,43 +0,0 @@ -# ============================================================================ # -# Copyright (c) 2024 NVIDIA Corporation & Affiliates. # -# All rights reserved. # -# # -# This source code and the accompanying materials are made available under # -# the terms of the Apache License 2.0 which accompanies this distribution. # -# ============================================================================ # - -# [Begin Documentation] -import cudaq, cudaq_solvers as solvers -from scipy.optimize import minimize - -# Create the molecular hamiltonian -geometry = [('H', (0., 0., 0.)), ('H', (0., 0., .7474))] -molecule = solvers.create_molecule(geometry, 'sto-3g', 0, 0, casci=True) - -# Get the number of qubits and electrons -numQubits = molecule.n_orbitals * 2 -numElectrons = molecule.n_electrons -spin = 0 -initialX = [-.2] * solvers.stateprep.get_num_uccsd_parameters( - numElectrons, numQubits) - - -# Define the UCCSD ansatz -@cudaq.kernel -def ansatz(thetas: list[float]): - q = cudaq.qvector(numQubits) - for i in range(numElectrons): - x(q[i]) - solvers.stateprep.uccsd(q, thetas, numElectrons, spin) - - -# Run VQE -energy, params, all_data = solvers.vqe(ansatz, - molecule.hamiltonian, - initialX, - optimizer=minimize, - method='L-BFGS-B', - jac='3-point', - tol=1e-4, - options={'disp': True}) -print(f'Final = {energy}') diff --git a/docs/sphinx/examples_rst/qec/circuit_level_noise.rst b/docs/sphinx/examples_rst/qec/circuit_level_noise.rst deleted file mode 100644 index 860ef424..00000000 --- a/docs/sphinx/examples_rst/qec/circuit_level_noise.rst +++ /dev/null @@ -1,148 +0,0 @@ -Quantum Error Correction with Circuit-level Noise Modeling ----------------------------------------------------------- -This example builds upon the previous code-capacity noise model example. -In the circuit-level noise modeling experiment, we have many of the same components from the CUDA-Q QEC library: QEC codes, decoders, and noisy data. -The primary difference here, is that we can begin to run CUDA-Q kernels to generate noisy data, rather than just generating random bitstring to represent our errors. - -Along with the stabilizers, parity check matrices, and logical observables, the QEC code type also has an encoding map. -This map allows codes to define logical gates in terms of gates on the underlying physical qubits. -These encodings operate on the `qec.patch` type, which represents three registers of physical qubits making up a logical qubit. -A data qubit register, an X-stabilizer ancilla register, and a Z-stabilizer ancilla register. - -The most notable encoding stored in the QEC map, is how the `qec.operation.stabilizer_round`, which encodes a `cudaq.kernel` which stores the gate-level information for how to do a stabilizer measurement. -These stabilizer rounds are the gate-level way to encode the parity check matrix of a QEC code into quantum circuits. - -This example walks through how to use the CUDA-Q QEC library to perform a quantum memory experiment simulation. -These experiments model how well QEC cycles, or rounds of stabilizer measuments, can protect the information encoded in a logical qubit. -If noise is turned off, then the information is protected indefinitely. -Here, we will model depolarization noise after each CX gate, and track how many logical errors occur. - - -CUDA-Q QEC Implementation -+++++++++++++++++++++++++++++ -Here's how to use CUDA-Q QEC to perform a circuit-level noise model experiment in both Python and C++: - -.. tab:: Python - - .. literalinclude:: ../../examples/qec/python/circuit_level_noise.py - :language: python - :start-after: [Begin Documentation] - -.. tab:: C++ - - .. literalinclude:: ../../examples/qec/cpp/circuit_level_noise.cpp - :language: cpp - :start-after: [Begin Documentation] - - Compile and run with - - .. code-block:: bash - - nvq++ --enable-mlir --target=stim -lcudaq-qec circuit_level_noise.cpp -o circuit_level_noise - ./circuit_level_noise - - -1. QEC Code and Decoder types: - - As in the code capacity example, our central objects are the `qec.code` and `qec.decoder` types. - -2. Clifford simulation backend: - - As the size of QEC circuits can grow quite large, Clifford simulation is often the best tool for these simulations. - - `cudaq.set_target("stim")` selects the highly performant Stim simulator as the simulation backend. - -3. Noise model: - - To add noisy gates we use the `cudaq.NoiseModel` type. - - CUDA-Q supports the generation of arbitrary noise channels. Here we use a `cudaq.Depolarization2` channel to add a depolarization channel. - - This is added to the `CX` gate by adding it to the `X` gate with 1 control. - - This noisy gate is added to every qubit via that `noise.add_all_qubit_channel` function. - -4. Getting circuit-level noisy data: - - The `qec.code` is the first input parameter here, as the code's `stabilizer_round` determines the circuits executed. - - Each memory circuit runs for an input number of `nRounds`, which specifies how many `stabilizer_round` kernels are ran. - - After `nRounds` the data qubits are measured and the run is over. - - This is performed `nShots` number of times. - - During a shot, each stabilizer round's syndrome is `xor`'d against the preceding syndrome, so that we can track a sparser flow of data showing which round each parity check was violated. - - The first round returns the syndrome as is, as there is nothing preceding to `xor` against. - -5. Data qubit measurements: - - The data qubits are only read out after the end of each shot, so there are `nShots` worth of data readouts. - - The basis of the data qubit measurements depends on the state preparation used. - - Z-basis readout when preparing the logical `|0>` or logical `|1>` state with the `qec.operation.prep0` or `qec.operation.prep1` kernels. - - X-basis readout when preparing the logical `|+>` or logical `|->` state with the `qec.operation.prepp` or `qec.operation.prepm` kernels. - -6. Logical Errors: - - From here, the decoding procedure is again similar to the code capacity case, expect for we use a pauli frame to track errors that happen each QEC cycle. - - The final values of the pauli frame tell us how our logical state flipped during the experiment, and what needs to be done to correct it. - - We compare our known initial state (corrected by the Pauli frame), against our measured data qubits to determine if a logical error occurred. - - -The CUDA-Q QEC library thus provides a platform for numerical QEC experiments. The `qec.code` can be used to analyze a variety of QEC codes (both library or user provided), with a variety of decoders (both library or user provided). -The CUDA-Q QEC library also provides tools to speed up the automation of generating noisy data and syndromes. - -Addtionally, here's how to use CUDA-Q QEC to construct a multi-round parity check matrix and a custom error correction code for the circuit-level noise model experiment in Python: - -.. tab:: Python - - .. literalinclude:: ../../examples/qec/python/repetition_code_fine_grain_noise.py - :language: python - :start-after: [Begin Documentation] - -This example illustrates how to: - -1. Construct a multi-round parity check matrix – Users can extend a single-round parity check matrix across multiple rounds, -incorporating measurement errors to track syndrome evolution over time. This enables more accurate circuit-level noise modeling for decoders. - -2. Define custom error correction circuits with precise noise injection – Using `cudaq.apply_noise`, users can introduce specific error channels -at targeted locations within the QEC circuit. This fine-grained control allows for precise testing of how different noise sources affect logical error rates. - -In the previous example, we demonstrated how to introduce random X errors into each data qubit using `cudaq.apply_noise` during each round of syndrome extraction. -CUDA-Q allows users to inject a variety of error channels at different locations within their circuits, enabling fine-grained noise modeling. The example below showcases -additional ways to introduce errors into a quantum kernel: - - .. code-block:: python - - @cudaq.kernel - def inject_noise_example(): - q = cudaq.qvector(3) - - # Apply depolarization noise to the first qubit - cudaq.apply_noise(cudaq.DepolarizationChannel, 0.1, q[0]) - - # Perform gate operations - h(q[1]) - x.ctrl(q[1], q[2]) - - # Inject a Y error into the second qubit - cudaq.apply_noise(cudaq.YError, 0.1, q[1]) - - # Apply a general Pauli noise channel to the third qubit, where the 3 values indicate the probability of X, Y, and Z errors. - cudaq.apply_noise(cudaq.Pauli1, 0.1, 0.1, 0.1, q[2]) - - # Define and apply a noise model - noise = cudaq.NoiseModel() - counts = cudaq.sample(inject_noise_example, noise_model=noise) - -For a full list of supported noise models and their parameters, refer to the `CUDA-Q documentation `_. - -Getting Started with the NVIDIA QLDPC Decoder -+++++++++++++++++++++++++++++++++++++++++++++ - -Starting with CUDA-Q QEC v0.2, a GPU-accelerated decoder is included with the -CUDA-Q QEC library. The library follows the CUDA-Q decoder Python and C++ interfaces -(namely :class:`cudaq_qec.Decoder` for Python and -:cpp:class:`cudaq::qec::decoder` for C++), but as documented in the API sections -(:ref:`nv_qldpc_decoder_api_python` for Python and -:ref:`nv_qldpc_decoder_api_cpp` for C++), there are many configuration options -that can be passed to the constructor. The following example shows how to -exercise the decoder using non-trivial pre-generated test data. The test data -was generated using scripts originating from the GitHub repo for -`BivariateBicycleCodes -`_ [#f1]_; it includes parity -check matrices (PCMs) and test syndromes to exercise a decoder. - -.. literalinclude:: ../../examples/qec/python/nv-qldpc-decoder.py - :language: python - :start-after: [Begin Documentation] - -.. rubric:: Footnotes - -.. [#f1] [BCGMRY] Sergey Bravyi, Andrew Cross, Jay Gambetta, Dmitri Maslov, Patrick Rall, Theodore Yoder, High-threshold and low-overhead fault-tolerant quantum memory https://arxiv.org/abs/2308.07915 diff --git a/docs/sphinx/examples_rst/qec/code_capacity_noise.rst b/docs/sphinx/examples_rst/qec/code_capacity_noise.rst deleted file mode 100644 index 03bbee2f..00000000 --- a/docs/sphinx/examples_rst/qec/code_capacity_noise.rst +++ /dev/null @@ -1,86 +0,0 @@ -Quantum Error Correction with Code-Capacity Noise Modeling ----------------------------------------------------------- - -Quantum error correction (QEC) describes a set of tools used to detect and correct errors which occur to qubits on quantum computers. -This example will walk through how the CUDA-Q QEC library handles two of the most common objects in QEC: stabilizer codes, and decoders. -A stabilizer code is the quantum generalization of linear codes in classical error correction, which use parity checks to detect errors on noise bits. -In QEC, we'll perform stabilizer measurements on ancilla qubits to check the parity of our data qubits. -These stabilizer measurements are non-destructive, and thus allow us to check the relative parity of qubits without destroying our quantum information. - -For example, if we prepare two qubits in the state `\Psi = a|00> + b|11>`, we maybe want to check if a bit-flip error happened. -We can measure the stabilizer `ZZ`, which will return 0 if there are no errors or even number of errors, but will return 1 if either has flipped. -This is how we can perform parity checks in quantum computing, without performing destructive measurements which collapse our superposition. -How these measurements are physically performed can be seen in the circuit-level noise QEC example. - -We can specify a stabilizer code with either a list of stabilizer operators (like `ZZ` above), or equivalently, a parity check matrix. -We can think of the columns of a parity check matrix as the types of errors that can occur. In this case, each qubit can experience a bit flip `X` or a phase flip `Z` error, so the parity check matrix will have 2N columns where N is the number of data qubits. -Each row represents a stabilizer, or a parity check. -The values are either 0 or 1, where a 1 means that the corresponding column does participate in the parity check, and a 0 means it does not. -Therefore, if a single `X/Z` error happens to a qubit, the supported rows of the parity check matrix will trigger. -This is called the syndrome, a string of 0's and 1's corresponding to which parity checks were violated. -A special class of stabilizer codes are called CSS (Calderbank-Shor-Steane) codes, which means the `X` and `Z` components of their parity check matrix can be separated. - -This brings us to decoding. Decoding is the act of solving the problem: given a syndrome, which underlying errors are most likely? -There are many decoding algorithms, but this example will use a simple single-error look-up table. -This means that the decoder will enumerate for each single error bit string, what the resulting syndromes are. -Then given a syndrome, it will look up the error string and return that as a result. - -The last thing we need, is a way to generate errors. -This example will go through a code capacity noise model where we have an independent and identical chance that an `X` or `Z` error happens on each qubit with some probability `p`. - -CUDA-Q QEC Implementation -+++++++++++++++++++++++++++++ -Here's how to use CUDA-Q QEC to perform a code capacity noise model experiment in both Python and C++: - -.. tab:: Python - - .. literalinclude:: ../../examples/qec/python/code_capacity_noise.py - :language: python - :start-after: [Begin Documentation] - -.. tab:: C++ - - .. literalinclude:: ../../examples/qec/cpp/code_capacity_noise.cpp - :language: cpp - :start-after: [Begin Documentation] - - Compile and run with - - .. code-block:: bash - - nvq++ --enable-mlir --target=stim -lcudaq-qec code_capacity_noise.cpp -o code_capacity_noise - ./code_capacity_noise - - -Code Explanation -++++++++++++++++ - -1. QEC Code type: - - CUDA-Q QEC centers around the `qec.code` type, which contains the data relevant for a given code. - - In particular, this represents a collection of qubits which represent a single logical qubit. - - Here we get one of the most well known QEC codes, the Steane code, with the `qec.get_code` function. - - We can get the stabilizers from a code with the `code.get_stabilizers()` function. - - In this example, we get the parity check matrix of the code. Because the Steane code is a CSS code, we can extract just the `Z` components of the parity check matrix. - - Here, we see this matrix has 3 rows and 7 columns, which means there are 7 data qubits (7 possible single bit-flip errors) and 3 Z-stabilizers (parity checks). Note that `Z` stabilizers check for `X` type errors. - - Lastly, we get the logical `Z` observable for the code. This will allow us to see if the `Z` observable of our logical qubit has flipped. - -2. Decoder type: - - A single-error look-up table (LUT) decoder can be acquired with the `qec.get_decoder` call. - - Passing in the parity check matrix gives the decoder the required information to associated syndromes with underlying error mechanisms. - - Once the decode has been constructed, the `decoder.decode(syndrome)` member function is called, which returns a predicted error given the syndrome. - -3. Noise model: - - To generate noisy data, we call `qec.generate_random_bit_flips(nBits, p)` which will return an array of bits, where each bit has probability `p` to have been flipped into 1, and a `1-p` chance to have remained 0. - - Since we are using the `Z` parity check matrix `H_Z`, we want to simulate random `X` errors on our 7 data qubits. - -4. Logical Errors: - - Once we have noisy data, we see what the resulting syndromes are by multiplying our noisy data vector with our parity check matrix (mod 2). - - From this syndrome, we see what the decoder predicts what errors occurred in the data. - - To classify as a logical error, the decoder does not need to exactly guess what happened to the data, but if there was a flip in the logical observable or not. - - If the decoder guesses this successfully, we have corrected the quantum error. If not, we have incurred a logical error. - -5. Further automation: - - While this workflow is nice for seeing things step by step, the `qec.sample_code_capacity` API is provided to generate a batch of noisy data and their corresponding syndromes. - -The CUDA-Q QEC library thus provides a platform for numerical QEC experiments. The `qec.code` can be used to analyze a variety of QEC codes (both library or user provided), with a variety of decoders (both library or user provided). -The CUDA-Q QEC library also provides tools to speed up the automation of generating noisy data and syndromes. diff --git a/docs/sphinx/examples_rst/qec/examples.rst b/docs/sphinx/examples_rst/qec/examples.rst deleted file mode 100644 index 606f2fa6..00000000 --- a/docs/sphinx/examples_rst/qec/examples.rst +++ /dev/null @@ -1,11 +0,0 @@ -************************* -CUDA-Q QEC by Example -************************* - -Examples that illustrate how to use CUDA-QX for application development are available in C++ and Python. - -.. toctree:: - :maxdepth: 1 - - Code-Capacity-QEC - Circuit-Level-QEC diff --git a/docs/sphinx/examples_rst/solvers/adapt.rst b/docs/sphinx/examples_rst/solvers/adapt.rst deleted file mode 100644 index ba801c0f..00000000 --- a/docs/sphinx/examples_rst/solvers/adapt.rst +++ /dev/null @@ -1,41 +0,0 @@ -ADAPT-VQE ---------- - -ADAPT-VQE is an advanced quantum algorithm designed to improve upon the -standard Variational Quantum Eigensolver (VQE) approach for solving quantum -chemistry problems. It addresses key challenges faced by traditional VQE -methods by dynamically constructing a problem-specific ansatz, offering -several advantages: - -- Faster convergence: Adaptively selects the most impactful operators, potentially achieving convergence more quickly than fixed-ansatz VQE methods. -- Enhanced efficiency: Builds a compact ansatz tailored to the specific problem, potentially reducing overall circuit depth. -- Increased accuracy: Has demonstrated the ability to outperform standard VQE approaches in terms of accuracy for certain molecular systems. -- Adaptability: Automatically adjusts to different molecular systems without requiring significant user intervention or prior knowledge of the system's electronic structure. - -The ADAPT-VQE algorithm works by iteratively growing the quantum circuit -ansatz, selecting operators from a predefined pool based on their gradient -magnitudes. This adaptive approach allows the algorithm to focus -computational resources on the most relevant aspects of the problem, -potentially leading to more efficient and accurate simulations of molecular -systems on quantum computers. - -Here we demonstrate how to use the CUDA-Q Solvers library to execute the ADAPT-VQE algorithm. - -.. tab:: Python - - .. literalinclude:: ../../examples/solvers/python/adapt_h2.py - :language: python - :start-after: [Begin Documentation] - -.. tab:: C++ - - .. literalinclude:: ../../examples/solvers/cpp/adapt_h2.cpp - :language: cpp - :start-after: [Begin Documentation] - - Compile and run with - - .. code:: bash - - nvq++ --enable-mlir -lcudaq-solvers adapt_h2.cpp -o adapt_h2 - ./adapt_h2 diff --git a/docs/sphinx/examples_rst/solvers/examples.rst b/docs/sphinx/examples_rst/solvers/examples.rst deleted file mode 100644 index 75ec8d71..00000000 --- a/docs/sphinx/examples_rst/solvers/examples.rst +++ /dev/null @@ -1,13 +0,0 @@ -************************* -CUDA-Q Solvers by Example -************************* - -Examples that illustrate how to use CUDA-QX for application development are available in C++ and Python. - -.. toctree:: - :maxdepth: 1 - - Molecular-Hamiltonians - ADAPT-VQE - VQE - QAOA diff --git a/docs/sphinx/examples_rst/solvers/molecular_hamiltonians.rst b/docs/sphinx/examples_rst/solvers/molecular_hamiltonians.rst deleted file mode 100644 index 8ba2d754..00000000 --- a/docs/sphinx/examples_rst/solvers/molecular_hamiltonians.rst +++ /dev/null @@ -1,103 +0,0 @@ -Generating Molecular Hamiltonians ----------------------------------- - -The CUDA-Q Solvers library accelerates a wide range of applications in the domain of quantum chemistry. -To facilitate these calculations, CUDA-Q Solvers provides the `solver.create_molecule` function to allow users to generate basis sets and Hamiltonians for many systems of interest. -The molecule class contains basis set informations, and the Hamiltonian (`molecule.hamiltonian`) for the target systems. -These Hamiltonians can then be used as input into the hybrid quantum-classical solvers that the CUDA-Q Solvers API provides. - - -Molecular Orbitals and Hamiltonians -+++++++++++++++++++++++++++++++++++ - -First we define the atomic geometry of the molecule by specifying a array of atomic symbols as strings, and coordinates in 3D space. We then get a molecule object from the `solvers.create_molecule` call. -Here we create "default" Hamiltonian for the N2 system using complete active space molecular orbitals constructed from Hartree-Fock atomic orbitals. - -.. tab:: Python - - .. code-block:: python - - geometry = [('N', (0.0, 0.0, 0.5600)), ('N', (0.0, 0.0, -0.5600))] - molecule = solvers.create_molecule(geometry, - 'sto-3g', - 0, - 0, - nele_cas=2, - norb_cas=3, - verbose=True) - -We specify: - - The geometry previously created - - The single particle basis set (here STO-3G) - - The total spin - - The total charge - - The number of electrons in the complete active space - - The number of orbitals in the complete activate space - - A verbosity flag to help introspect on the data what was generated. - -Along with the orbitals and Hamiltonian, we can also view various properties like the Hartree-Fock energy, and the energy of the frozen core orbitals by printing `molecule.energies`. - -Natural Orbitals from MP2 -++++++++++++++++++++++++++ -Now we take our same N2 molecule, but generate natural orbitals from second order Møller–Plesset perturbation theory as the basis. - -.. tab:: Python - - .. code-block:: python - - geometry = [('N', (0.0, 0.0, 0.5600)), ('N', (0.0, 0.0, -0.5600))] - molecule = solvers.create_molecule(geometry, - 'sto-3g', - 0, - 0, - nele_cas=2, - norb_cas=3, - MP2=True, - integrals_natorb=True, - verbose=True) - -Note that we use the same API but,toggle `MP2=True` and `integrals_natorb=True`. - -CASSCF Orbitals -+++++++++++++++ - -Next, we can start from either Hartree-Fock or perturbation theory atomic orbitals and build complete active space self-consistent field (CASSCF) molecular orbitals. - -.. tab:: Python - - .. code-block:: python - - geometry = [('N', (0.0, 0.0, 0.5600)), ('N', (0.0, 0.0, -0.5600))] - molecule = solvers.create_molecule(geometry, - 'sto-3g', - 0, - 0, - nele_cas=2, - norb_cas=3, - casscf=True, - integrals_casscf=True, - verbose=True) - -For Hartree-Fock, or - -.. tab:: Python - - .. code-block:: python - - geometry = [('N', (0.0, 0.0, 0.5600)), ('N', (0.0, 0.0, -0.5600))] - molecule = solvers.create_molecule(geometry, - 'sto-3g', - 0, - 0, - nele_cas=2, - norb_cas=3, - MP2=True, - natorb=True, - casscf=True, - integrals_casscf=True, - verbose=True) - -for MP2. In these cases, printing the `molecule.energies` also shows the `R-CASSCF` energy for the system. - -Now that we have seen how to generate basis sets and Hamiltonians for quantum chemistry systems, we can use these as inputs to hybrid quantum-classical methods like VQE or adapt VQE via the CUDA-Q Solvers API. - diff --git a/docs/sphinx/examples_rst/solvers/qaoa.rst b/docs/sphinx/examples_rst/solvers/qaoa.rst deleted file mode 100644 index eb036228..00000000 --- a/docs/sphinx/examples_rst/solvers/qaoa.rst +++ /dev/null @@ -1,62 +0,0 @@ -Quantum Approximate Optimization Algorithm (QAOA) -------------------------------------------------- - -The Quantum Approximate Optimization Algorithm (QAOA) is a hybrid quantum-classical algorithm that solves combinatorial optimization problems. - -Key features of QAOA: - -- Hybrid approach: Utilizes both quantum and classical resources efficiently. -- Iterative optimization: Classical optimizer adjusts circuit parameters to minimize energy. -- NISQ compatibility: This algorithm is designed to run on the noisy quantum computers of today. -- Flexibility: Can be applied to various problems in quantum chemistry and optimization problems broadly. - -.. tab:: Python - - .. literalinclude:: ../../examples/solvers/python/molecular_docking_qaoa.py - :language: python - :start-after: [Begin Documentation] - -.. tab:: C++ - - .. literalinclude:: ../../examples/solvers/cpp/molecular_docking_qaoa.cpp - :language: cpp - :start-after: [Begin Documentation] - - Compile and run with - - .. code-block:: bash - - nvq++ --enable-mlir -lcudaq-solvers molecular_docking_qaoa.cpp -o molecular_docking_qaoa - ./molecular_docking_qaoa - -CUDA-Q Solvers Implementation -+++++++++++++++++++++++++++++ -Here's how to use CUDA-Q Solvers to solve the Maximum Clique Problem using QAOA: - -Code Explanation -++++++++++++++++ -1. Graph Creation: - - A NetworkX graph is created to represent the problem. - - Nodes and edges are added with specific weights. - -2. Clique Hamiltonian Generation: - - `solvers.get_clique_hamiltonian` is used to create the Hamiltonian for the Maximum Clique Problem. - - The penalty term and number of QAOA layers are defined. - -3. QAOA Parameter Setup: - - The number of required parameters is calculated using `solvers.get_num_qaoa_parameters`. - - Randomly generate initial parameters. - -4. QAOA Execution with `solvers.qaoa`: - - Call the solver with the Hamiltonian, number of QAOA layers, and whether you want full parametrization and counterdiabatic driving. - - Full parameterization: Uses an optimization parameter for every term in the clique Hamiltonian and the mixer Hamiltonian. - - Counterdiabatic driving: Adds extra Ry rotations at the end of each layer. - -5. Results Analysis: - - The optimal energy, sampled states, and most probable configuration are printed. - -This implementation showcases the power of CUDA-Q Solvers in solving combinatorial optimization problems using hybrid quantum-classical algorithms. -By using CUDA-Q Solvers with the networkx library, we very quickly set up and ran a QAOA application to compute optimal configurations for a molecular docking problem. - - - diff --git a/docs/sphinx/examples_rst/solvers/vqe.rst b/docs/sphinx/examples_rst/solvers/vqe.rst deleted file mode 100644 index 767132be..00000000 --- a/docs/sphinx/examples_rst/solvers/vqe.rst +++ /dev/null @@ -1,68 +0,0 @@ -Variational Quantum Eigensolver (VQE) -------------------------------------- - -The Variational Quantum Eigensolver (VQE) is a hybrid quantum-classical algorithm designed to find the ground state energy of a quantum system. It combines quantum computation with classical optimization to iteratively improve an approximation of the ground state. - -Key features of VQE: - -- Hybrid approach: Utilizes both quantum and classical resources efficiently. -- Variational method: Uses a parameterized quantum circuit (ansatz) to prepare trial states. -- Iterative optimization: Classical optimizer adjusts circuit parameters to minimize energy. -- Flexibility: Can be applied to various problems in quantum chemistry and materials science. - -VQE Algorithm Overview: - -1. Prepare an initial quantum state using a parameterized circuit (ansatz). -2. Measure the expectation value of the Hamiltonian. -3. Use a classical optimizer to adjust circuit parameters. -4. Repeat steps 1-3 until convergence or a stopping criterion is met. - -CUDA-Q Solvers Implementation -+++++++++++++++++++++++++++++ - -CUDA-Q Solvers provides a high-level interface for running VQE simulations. Here's how to use it in both Python and C++: - -.. tab:: Python - - .. literalinclude:: ../../examples/solvers/python/uccsd_vqe.py - :language: python - :start-after: [Begin Documentation] - -.. tab:: C++ - - .. literalinclude:: ../../examples/solvers/cpp/uccsd_vqe.cpp - :language: cpp - :start-after: [Begin Documentation] - - Compile and run with - - .. code-block:: bash - - nvq++ --enable-mlir -lcudaq-solvers uccsd_vqe.cpp -o uccsd_vqe - ./uccsd_vqe - -Code Explanation -++++++++++++++++ - -1. Molecule Creation: - - Both examples start by defining the molecular geometry (H2 molecule). - - The `create_molecule` function generates the molecular Hamiltonian. - -2. Ansatz Definition: - - The UCCSD (Unitary Coupled Cluster Singles and Doubles) ansatz is used. - - In Python, it's defined as a `cudaq.kernel`. - - In C++, it's defined as a lambda function within the VQE call. - -3. VQE Execution: - - The `solvers.vqe` function (Python) or `solvers::vqe` (C++) is called. - - It takes the ansatz, Hamiltonian, initial parameters, and optimization settings. - -4. Optimization: - - Python uses SciPy's `minimize` function with L-BFGS-B method. - - C++ uses CUDA-Q Solvers' built-in optimizer. - - Either language can make use of CUDA-QX builtin optimizers. - -5. Results: - - Both versions print the final ground state energy. - -The CUDA-Q Solvers implementation of VQE provides a high-level interface that handles the quantum-classical hybrid optimization loop, making it easy to apply VQE to molecular systems. Users can focus on defining the problem (molecule and ansatz) while CUDA-Q Solvers manages the complex interaction between quantum and classical resources. diff --git a/docs/sphinx/index.rst b/docs/sphinx/index.rst deleted file mode 100644 index 15153ebf..00000000 --- a/docs/sphinx/index.rst +++ /dev/null @@ -1,66 +0,0 @@ -CUDA-QX - The CUDA-Q Libraries Collection -========================================== - -CUDA-QX is a collection of libraries that build upon the CUDA-Q programming model -to enable the rapid development of hybrid quantum-classical application code leveraging -state-of-the-art CPUs, GPUs, and QPUs. It provides a collection of C++ -libraries and Python packages that enable research, development, and application -creation for use cases in quantum error correction and hybrid quantum-classical -solvers. - -.. toctree:: - :maxdepth: 2 - :caption: Getting Started - - quickstart/installation - -.. toctree:: - :maxdepth: 1 - :caption: Libraries - - components/qec/introduction - components/solvers/introduction - -.. toctree:: - :maxdepth: 2 - :caption: Examples - - examples_rst/qec/examples - examples_rst/solvers/examples - -.. toctree:: - :maxdepth: 1 - :caption: API Reference - - api/core/cpp_api - api/qec/cpp_api - api/qec/python_api - api/solvers/cpp_api - api/solvers/python_api - -Key Features -------------- - -CUDA-QX is composed of two distinct libraries that build upon CUDA-Q programming model. -The libraries provided are cudaq-qec, a library enabling performant research workflows -for quantum error correction, and cudaq-solvers, a library that provides high-level -APIs for common quantum-classical solver workflows. - -* **cudaq-qec**: Quantum Error Correction Library - * Extensible framework describing quantum error correcting codes as a collection of CUDA-Q kernels. - * Extensible framework for describing syndrome decoders - * State-of-the-art, performant decoder implementations on NVIDIA GPUs (coming soon) - * Pre-built numerical experiment APIs - -* **cudaq-solvers**: Performant Quantum-Classical Simulation Workflows - * Variational Quantum Eigensolver (VQE) - * ADAPT-VQE implementation that scales via CUDA-Q MQPU. - * Quantum Approximate Optimization Algorithm (QAOA) - * More to come... - -Indices and Tables ------------------- - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/docs/sphinx/quickstart/installation.rst b/docs/sphinx/quickstart/installation.rst deleted file mode 100644 index ff68e262..00000000 --- a/docs/sphinx/quickstart/installation.rst +++ /dev/null @@ -1,151 +0,0 @@ -Installation Guide -================== - -Installation Methods --------------------- - -CUDA-QX provides multiple installation methods to suit your needs: - -pip install -^^^^^^^^^^^^ - -The simplest way to install CUDA-QX is via pip. (If you're on Mac, your only -option is to use the Docker container as described below.) For pip, you can -install individual components: - -.. code-block:: bash - - # Install QEC library - pip install cudaq-qec - - # Install Solvers library - pip install cudaq-solvers - - # Install both libraries - pip install cudaq-qec cudaq-solvers - -.. note:: - - CUDA-Q Solvers will require the presence of :code:`libgfortran`, which is - not distributed with the Python wheel, for provided classical optimizers. If - :code:`libgfortran` is not installed, you will need to install it via your - distribution's package manager. On Debian based systems, you can install - this with :code:`apt-get install gfortran`. - -Docker Container -^^^^^^^^^^^^^^^^ - -CUDA-QX is available as a Docker container with all dependencies pre-installed: - -1. Pull the container: - -.. code-block:: bash - - docker pull ghcr.io/nvidia/cudaqx - -2. Run the container: - -.. code-block:: bash - - docker run --gpus all -it ghcr.io/nvidia/cudaqx - -.. note:: - - If your system does not have local GPUs (eg. a MacBook), omit the `--gpus all` - argument. - -The container includes: - * CUDA-Q compiler and runtime - * CUDA-QX libraries (QEC and Solvers) - * All required dependencies - * Example notebooks and tutorials - -Building from Source -^^^^^^^^^^^^^^^^^^^^ - -Prerequisites -~~~~~~~~~~~~~ - -Before building CUDA-QX from source, ensure your system meets the following requirements: - -* **CUDA-Q**: The NVIDIA quantum-classical programming model -* **CMake**: Version 3.28 or higher (``pip install "cmake<4"``), less than 4.0 -* **GCC**: Version 11 or higher -* **Python**: Version 3.10, 3.11, or 3.12 -* **NVIDIA GPU**: CUDA-capable GPU with compute capability 12.0 or higher -* **Git**: For cloning the repository - -Build Instructions -~~~~~~~~~~~~~~~~~~~ - -1. Clone the repository: - -.. code-block:: bash - - git clone https://github.com/nvidia/cudaqx - cd cudaqx - -2. Create and enter build directory: - -.. code-block:: bash - - mkdir build && cd build - -3. Configure with CMake: - -.. code-block:: bash - - cmake .. -G Ninja \ - -DCUDAQX_ENABLE_LIBS="all" \ - -DCUDAQX_INCLUDE_TESTS=ON \ - -DCUDAQX_BINDINGS_PYTHON=ON \ - -DCUDAQ_DIR=$HOME/.cudaq/lib/cmake/cudaq \ - -DCMAKE_CXX_FLAGS="-Wno-attributes" \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=$HOME/.cudaqx - -4. Build and install: - -.. code-block:: bash - - ninja install - -CMake Build Options -~~~~~~~~~~~~~~~~~~~~ - -* ``CUDAQX_ENABLE_LIBS``: Specify which libraries to build (``all``, ``qec``, ``solvers``) -* ``CUDAQX_INCLUDE_TESTS``: Enable building of tests -* ``CUDAQX_BINDINGS_PYTHON``: Enable Python bindings -* ``CUDAQ_DIR``: Path to CUDA-Q installation -* ``CMAKE_INSTALL_PREFIX``: Installation directory - -Verifying Installation ------------------------ - -To verify your installation, run the following Python code: - -.. code-block:: python - - import cudaq_qec as qec - import cudaq_solvers as solvers - - -Troubleshooting (Common Issues) --------------------------------- - -1. **CMake configuration fails**: - * Ensure CUDA-Q is properly installed - * Verify CMake version (``cmake --version``) - * Check GCC version (``gcc --version``) - -2. **CUDA device not found**: - * Verify NVIDIA driver installation - * Check CUDA toolkit installation - * Ensure GPU compute capability is supported - -3. **Python bindings not found**: - * Confirm ``CUDAQX_BINDINGS_PYTHON=ON`` during build - * Check Python environment activation - * Verify installation path is in ``PYTHONPATH`` - -For additional support, please visit our `GitHub Issues `_ page. diff --git a/scripts/build_docs.sh b/scripts/build_docs.sh index ab3ff61e..02438ffb 100755 --- a/scripts/build_docs.sh +++ b/scripts/build_docs.sh @@ -1,7 +1,7 @@ #!/bin/bash # ============================================================================ # -# Copyright (c) 2024 NVIDIA Corporation & Affiliates. # +# Copyright (c) 2024 - 2025 NVIDIA Corporation & Affiliates. # # All rights reserved. # # # # This source code and the accompanying materials are made available under # @@ -44,34 +44,10 @@ dialect_output_dir="$docs_build_output/Dialects" rm -rf "$docs_build_output" mkdir -p "$docs_build_output" -doxygen_exe=doxygen - -# Create the conf.py file needed by Sphinx -echo "Generating conf.py ..." -sphinx_conf_in="$repo_root/docs/sphinx/conf.py.in" -sphinx_conf="$repo_root/docs/sphinx/conf.py" - -# Verify that the input file exists before proceeding -if [ ! -f "$sphinx_conf_in" ]; then - echo "Error: Sphinx configuration template '$sphinx_conf_in' does not exist." >&2 - exit 1 -fi - -# Replace placeholders of the form @VAR@ in the template file with their variable values. -CUDAQ_INSTALL_DIR=${CUDAQ_INSTALL_DIR:-"$HOME/.cudaq"} -CMAKE_BINARY_DIR="$repo_root/build" -SPHINX_SOURCE="$repo_root/docs/sphinx" - -sed -e "s|@CUDAQ_INSTALL_DIR@|${CUDAQ_INSTALL_DIR}|g" \ - -e "s|@CMAKE_BINARY_DIR@|${CMAKE_BINARY_DIR}|g" \ - -e "s|@SPHINX_SOURCE@|${SPHINX_SOURCE}|g" \ - "$sphinx_conf_in" > "$sphinx_conf" - -echo "Configuration file created at: $sphinx_conf" - # Generate API documentation using Doxygen echo "Generating XML documentation using Doxygen..." mkdir -p "${doxygen_output_dir}" +doxygen_exe=doxygen doxygen_input="$repo_root/docs/Doxyfile.in" # Get all the headers @@ -97,40 +73,10 @@ if [ ! "$doxygen_exit_code" -eq "0" ]; then docs_exit_code=11 fi -echo "Building CUDA-QX documentation using Sphinx..." -cd "$repo_root/docs" -# The docs build so far is fast such that we do not care about the cached outputs. -# Revisit this when caching becomes necessary. - -rm -rf sphinx/_doxygen/ -rm -rf sphinx/_mdgen/ -cp -r "$doxygen_output_dir" sphinx/_doxygen/ -# cp -r "$dialect_output_dir" sphinx/_mdgen/ # uncomment once we use the content from those files - -rm -rf "$sphinx_output_dir" -sphinx-build -v -n --keep-going -b html \ - -Dbreathe_projects.cudaqx="${doxygen_output_dir}/xml" \ - sphinx "$sphinx_output_dir" -j auto #2> "$logs_dir/sphinx_error.txt" 1> "$logs_dir/sphinx_output.txt" - -sphinx_exit_code=$? -if [ ! "$sphinx_exit_code" -eq "0" ]; then - echo "Failed to generate documentation using sphinx-build." - echo "Sphinx exit code: $sphinx_exit_code" - echo "======== logs ========" - cat "$logs_dir/sphinx_output.txt" "$logs_dir/sphinx_error.txt" - echo "======================" - docs_exit_code=12 -fi - -rm -rf sphinx/_doxygen/ -rm -rf sphinx/_mdgen/ - mkdir -p "$DOCS_INSTALL_PREFIX" if [ "$docs_exit_code" -eq "0" ]; then - cp -r "$sphinx_output_dir"/* "$DOCS_INSTALL_PREFIX" - touch "$DOCS_INSTALL_PREFIX/.nojekyll" + cp -r "$doxygen_output_dir"/xml/* "$DOCS_INSTALL_PREFIX" echo "Documentation was generated in $DOCS_INSTALL_PREFIX." - echo "To browse it, open this url in a browser: file://$DOCS_INSTALL_PREFIX/index.html" else echo "Documentation generation failed with exit code $docs_exit_code." echo "Check the logs in $logs_dir, and the documentation build output in $docs_build_output."