From f054974492e2d41ccd360f41a7c7d395304ae11e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 Aug 2025 03:42:32 +0000 Subject: [PATCH 01/13] Initial plan From 41320c3dcc4c976cb32b1ab628e0df14ae0fbfb0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 Aug 2025 03:47:22 +0000 Subject: [PATCH 02/13] Phase 1: Use git mv to rename 38 markdown files to .rst preserving git history Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- cuda_bindings/docs/source/{conduct.md => conduct.rst} | 0 cuda_bindings/docs/source/{contribute.md => contribute.rst} | 0 .../{environment_variables.md => environment_variables.rst} | 0 cuda_bindings/docs/source/{install.md => install.rst} | 0 cuda_bindings/docs/source/{motivation.md => motivation.rst} | 0 cuda_bindings/docs/source/{overview.md => overview.rst} | 0 .../docs/source/release/{11.4.0-notes.md => 11.4.0-notes.rst} | 0 .../docs/source/release/{11.5.0-notes.md => 11.5.0-notes.rst} | 0 .../docs/source/release/{11.6.0-notes.md => 11.6.0-notes.rst} | 0 .../docs/source/release/{11.6.1-notes.md => 11.6.1-notes.rst} | 0 .../docs/source/release/{11.7.0-notes.md => 11.7.0-notes.rst} | 0 .../docs/source/release/{11.7.1-notes.md => 11.7.1-notes.rst} | 0 .../docs/source/release/{11.8.0-notes.md => 11.8.0-notes.rst} | 0 .../docs/source/release/{11.8.1-notes.md => 11.8.1-notes.rst} | 0 .../docs/source/release/{11.8.2-notes.md => 11.8.2-notes.rst} | 0 .../docs/source/release/{11.8.3-notes.md => 11.8.3-notes.rst} | 0 .../docs/source/release/{11.8.4-notes.md => 11.8.4-notes.rst} | 0 .../docs/source/release/{11.8.5-notes.md => 11.8.5-notes.rst} | 0 .../docs/source/release/{11.8.6-notes.md => 11.8.6-notes.rst} | 0 .../docs/source/release/{12.0.0-notes.md => 12.0.0-notes.rst} | 0 .../docs/source/release/{12.1.0-notes.md => 12.1.0-notes.rst} | 0 .../docs/source/release/{12.2.0-notes.md => 12.2.0-notes.rst} | 0 .../docs/source/release/{12.2.1-notes.md => 12.2.1-notes.rst} | 0 .../docs/source/release/{12.3.0-notes.md => 12.3.0-notes.rst} | 0 .../docs/source/release/{12.4.0-notes.md => 12.4.0-notes.rst} | 0 .../docs/source/release/{12.5.0-notes.md => 12.5.0-notes.rst} | 0 .../docs/source/release/{12.6.0-notes.md => 12.6.0-notes.rst} | 0 .../docs/source/release/{12.6.1-notes.md => 12.6.1-notes.rst} | 0 .../docs/source/release/{12.6.2-notes.md => 12.6.2-notes.rst} | 0 .../docs/source/release/{12.8.0-notes.md => 12.8.0-notes.rst} | 0 cuda_core/docs/source/{conduct.md => conduct.rst} | 0 cuda_core/docs/source/{getting-started.md => getting-started.rst} | 0 cuda_core/docs/source/{install.md => install.rst} | 0 cuda_python/docs/source/{release.md => release.rst} | 0 .../docs/source/release/{11.8.6-notes.md => 11.8.6-notes.rst} | 0 .../docs/source/release/{12.6.1-notes.md => 12.6.1-notes.rst} | 0 .../docs/source/release/{12.6.2-notes.md => 12.6.2-notes.rst} | 0 .../docs/source/release/{12.8.0-notes.md => 12.8.0-notes.rst} | 0 38 files changed, 0 insertions(+), 0 deletions(-) rename cuda_bindings/docs/source/{conduct.md => conduct.rst} (100%) rename cuda_bindings/docs/source/{contribute.md => contribute.rst} (100%) rename cuda_bindings/docs/source/{environment_variables.md => environment_variables.rst} (100%) rename cuda_bindings/docs/source/{install.md => install.rst} (100%) rename cuda_bindings/docs/source/{motivation.md => motivation.rst} (100%) rename cuda_bindings/docs/source/{overview.md => overview.rst} (100%) rename cuda_bindings/docs/source/release/{11.4.0-notes.md => 11.4.0-notes.rst} (100%) rename cuda_bindings/docs/source/release/{11.5.0-notes.md => 11.5.0-notes.rst} (100%) rename cuda_bindings/docs/source/release/{11.6.0-notes.md => 11.6.0-notes.rst} (100%) rename cuda_bindings/docs/source/release/{11.6.1-notes.md => 11.6.1-notes.rst} (100%) rename cuda_bindings/docs/source/release/{11.7.0-notes.md => 11.7.0-notes.rst} (100%) rename cuda_bindings/docs/source/release/{11.7.1-notes.md => 11.7.1-notes.rst} (100%) rename cuda_bindings/docs/source/release/{11.8.0-notes.md => 11.8.0-notes.rst} (100%) rename cuda_bindings/docs/source/release/{11.8.1-notes.md => 11.8.1-notes.rst} (100%) rename cuda_bindings/docs/source/release/{11.8.2-notes.md => 11.8.2-notes.rst} (100%) rename cuda_bindings/docs/source/release/{11.8.3-notes.md => 11.8.3-notes.rst} (100%) rename cuda_bindings/docs/source/release/{11.8.4-notes.md => 11.8.4-notes.rst} (100%) rename cuda_bindings/docs/source/release/{11.8.5-notes.md => 11.8.5-notes.rst} (100%) rename cuda_bindings/docs/source/release/{11.8.6-notes.md => 11.8.6-notes.rst} (100%) rename cuda_bindings/docs/source/release/{12.0.0-notes.md => 12.0.0-notes.rst} (100%) rename cuda_bindings/docs/source/release/{12.1.0-notes.md => 12.1.0-notes.rst} (100%) rename cuda_bindings/docs/source/release/{12.2.0-notes.md => 12.2.0-notes.rst} (100%) rename cuda_bindings/docs/source/release/{12.2.1-notes.md => 12.2.1-notes.rst} (100%) rename cuda_bindings/docs/source/release/{12.3.0-notes.md => 12.3.0-notes.rst} (100%) rename cuda_bindings/docs/source/release/{12.4.0-notes.md => 12.4.0-notes.rst} (100%) rename cuda_bindings/docs/source/release/{12.5.0-notes.md => 12.5.0-notes.rst} (100%) rename cuda_bindings/docs/source/release/{12.6.0-notes.md => 12.6.0-notes.rst} (100%) rename cuda_bindings/docs/source/release/{12.6.1-notes.md => 12.6.1-notes.rst} (100%) rename cuda_bindings/docs/source/release/{12.6.2-notes.md => 12.6.2-notes.rst} (100%) rename cuda_bindings/docs/source/release/{12.8.0-notes.md => 12.8.0-notes.rst} (100%) rename cuda_core/docs/source/{conduct.md => conduct.rst} (100%) rename cuda_core/docs/source/{getting-started.md => getting-started.rst} (100%) rename cuda_core/docs/source/{install.md => install.rst} (100%) rename cuda_python/docs/source/{release.md => release.rst} (100%) rename cuda_python/docs/source/release/{11.8.6-notes.md => 11.8.6-notes.rst} (100%) rename cuda_python/docs/source/release/{12.6.1-notes.md => 12.6.1-notes.rst} (100%) rename cuda_python/docs/source/release/{12.6.2-notes.md => 12.6.2-notes.rst} (100%) rename cuda_python/docs/source/release/{12.8.0-notes.md => 12.8.0-notes.rst} (100%) diff --git a/cuda_bindings/docs/source/conduct.md b/cuda_bindings/docs/source/conduct.rst similarity index 100% rename from cuda_bindings/docs/source/conduct.md rename to cuda_bindings/docs/source/conduct.rst diff --git a/cuda_bindings/docs/source/contribute.md b/cuda_bindings/docs/source/contribute.rst similarity index 100% rename from cuda_bindings/docs/source/contribute.md rename to cuda_bindings/docs/source/contribute.rst diff --git a/cuda_bindings/docs/source/environment_variables.md b/cuda_bindings/docs/source/environment_variables.rst similarity index 100% rename from cuda_bindings/docs/source/environment_variables.md rename to cuda_bindings/docs/source/environment_variables.rst diff --git a/cuda_bindings/docs/source/install.md b/cuda_bindings/docs/source/install.rst similarity index 100% rename from cuda_bindings/docs/source/install.md rename to cuda_bindings/docs/source/install.rst diff --git a/cuda_bindings/docs/source/motivation.md b/cuda_bindings/docs/source/motivation.rst similarity index 100% rename from cuda_bindings/docs/source/motivation.md rename to cuda_bindings/docs/source/motivation.rst diff --git a/cuda_bindings/docs/source/overview.md b/cuda_bindings/docs/source/overview.rst similarity index 100% rename from cuda_bindings/docs/source/overview.md rename to cuda_bindings/docs/source/overview.rst diff --git a/cuda_bindings/docs/source/release/11.4.0-notes.md b/cuda_bindings/docs/source/release/11.4.0-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/11.4.0-notes.md rename to cuda_bindings/docs/source/release/11.4.0-notes.rst diff --git a/cuda_bindings/docs/source/release/11.5.0-notes.md b/cuda_bindings/docs/source/release/11.5.0-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/11.5.0-notes.md rename to cuda_bindings/docs/source/release/11.5.0-notes.rst diff --git a/cuda_bindings/docs/source/release/11.6.0-notes.md b/cuda_bindings/docs/source/release/11.6.0-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/11.6.0-notes.md rename to cuda_bindings/docs/source/release/11.6.0-notes.rst diff --git a/cuda_bindings/docs/source/release/11.6.1-notes.md b/cuda_bindings/docs/source/release/11.6.1-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/11.6.1-notes.md rename to cuda_bindings/docs/source/release/11.6.1-notes.rst diff --git a/cuda_bindings/docs/source/release/11.7.0-notes.md b/cuda_bindings/docs/source/release/11.7.0-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/11.7.0-notes.md rename to cuda_bindings/docs/source/release/11.7.0-notes.rst diff --git a/cuda_bindings/docs/source/release/11.7.1-notes.md b/cuda_bindings/docs/source/release/11.7.1-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/11.7.1-notes.md rename to cuda_bindings/docs/source/release/11.7.1-notes.rst diff --git a/cuda_bindings/docs/source/release/11.8.0-notes.md b/cuda_bindings/docs/source/release/11.8.0-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/11.8.0-notes.md rename to cuda_bindings/docs/source/release/11.8.0-notes.rst diff --git a/cuda_bindings/docs/source/release/11.8.1-notes.md b/cuda_bindings/docs/source/release/11.8.1-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/11.8.1-notes.md rename to cuda_bindings/docs/source/release/11.8.1-notes.rst diff --git a/cuda_bindings/docs/source/release/11.8.2-notes.md b/cuda_bindings/docs/source/release/11.8.2-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/11.8.2-notes.md rename to cuda_bindings/docs/source/release/11.8.2-notes.rst diff --git a/cuda_bindings/docs/source/release/11.8.3-notes.md b/cuda_bindings/docs/source/release/11.8.3-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/11.8.3-notes.md rename to cuda_bindings/docs/source/release/11.8.3-notes.rst diff --git a/cuda_bindings/docs/source/release/11.8.4-notes.md b/cuda_bindings/docs/source/release/11.8.4-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/11.8.4-notes.md rename to cuda_bindings/docs/source/release/11.8.4-notes.rst diff --git a/cuda_bindings/docs/source/release/11.8.5-notes.md b/cuda_bindings/docs/source/release/11.8.5-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/11.8.5-notes.md rename to cuda_bindings/docs/source/release/11.8.5-notes.rst diff --git a/cuda_bindings/docs/source/release/11.8.6-notes.md b/cuda_bindings/docs/source/release/11.8.6-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/11.8.6-notes.md rename to cuda_bindings/docs/source/release/11.8.6-notes.rst diff --git a/cuda_bindings/docs/source/release/12.0.0-notes.md b/cuda_bindings/docs/source/release/12.0.0-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/12.0.0-notes.md rename to cuda_bindings/docs/source/release/12.0.0-notes.rst diff --git a/cuda_bindings/docs/source/release/12.1.0-notes.md b/cuda_bindings/docs/source/release/12.1.0-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/12.1.0-notes.md rename to cuda_bindings/docs/source/release/12.1.0-notes.rst diff --git a/cuda_bindings/docs/source/release/12.2.0-notes.md b/cuda_bindings/docs/source/release/12.2.0-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/12.2.0-notes.md rename to cuda_bindings/docs/source/release/12.2.0-notes.rst diff --git a/cuda_bindings/docs/source/release/12.2.1-notes.md b/cuda_bindings/docs/source/release/12.2.1-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/12.2.1-notes.md rename to cuda_bindings/docs/source/release/12.2.1-notes.rst diff --git a/cuda_bindings/docs/source/release/12.3.0-notes.md b/cuda_bindings/docs/source/release/12.3.0-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/12.3.0-notes.md rename to cuda_bindings/docs/source/release/12.3.0-notes.rst diff --git a/cuda_bindings/docs/source/release/12.4.0-notes.md b/cuda_bindings/docs/source/release/12.4.0-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/12.4.0-notes.md rename to cuda_bindings/docs/source/release/12.4.0-notes.rst diff --git a/cuda_bindings/docs/source/release/12.5.0-notes.md b/cuda_bindings/docs/source/release/12.5.0-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/12.5.0-notes.md rename to cuda_bindings/docs/source/release/12.5.0-notes.rst diff --git a/cuda_bindings/docs/source/release/12.6.0-notes.md b/cuda_bindings/docs/source/release/12.6.0-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/12.6.0-notes.md rename to cuda_bindings/docs/source/release/12.6.0-notes.rst diff --git a/cuda_bindings/docs/source/release/12.6.1-notes.md b/cuda_bindings/docs/source/release/12.6.1-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/12.6.1-notes.md rename to cuda_bindings/docs/source/release/12.6.1-notes.rst diff --git a/cuda_bindings/docs/source/release/12.6.2-notes.md b/cuda_bindings/docs/source/release/12.6.2-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/12.6.2-notes.md rename to cuda_bindings/docs/source/release/12.6.2-notes.rst diff --git a/cuda_bindings/docs/source/release/12.8.0-notes.md b/cuda_bindings/docs/source/release/12.8.0-notes.rst similarity index 100% rename from cuda_bindings/docs/source/release/12.8.0-notes.md rename to cuda_bindings/docs/source/release/12.8.0-notes.rst diff --git a/cuda_core/docs/source/conduct.md b/cuda_core/docs/source/conduct.rst similarity index 100% rename from cuda_core/docs/source/conduct.md rename to cuda_core/docs/source/conduct.rst diff --git a/cuda_core/docs/source/getting-started.md b/cuda_core/docs/source/getting-started.rst similarity index 100% rename from cuda_core/docs/source/getting-started.md rename to cuda_core/docs/source/getting-started.rst diff --git a/cuda_core/docs/source/install.md b/cuda_core/docs/source/install.rst similarity index 100% rename from cuda_core/docs/source/install.md rename to cuda_core/docs/source/install.rst diff --git a/cuda_python/docs/source/release.md b/cuda_python/docs/source/release.rst similarity index 100% rename from cuda_python/docs/source/release.md rename to cuda_python/docs/source/release.rst diff --git a/cuda_python/docs/source/release/11.8.6-notes.md b/cuda_python/docs/source/release/11.8.6-notes.rst similarity index 100% rename from cuda_python/docs/source/release/11.8.6-notes.md rename to cuda_python/docs/source/release/11.8.6-notes.rst diff --git a/cuda_python/docs/source/release/12.6.1-notes.md b/cuda_python/docs/source/release/12.6.1-notes.rst similarity index 100% rename from cuda_python/docs/source/release/12.6.1-notes.md rename to cuda_python/docs/source/release/12.6.1-notes.rst diff --git a/cuda_python/docs/source/release/12.6.2-notes.md b/cuda_python/docs/source/release/12.6.2-notes.rst similarity index 100% rename from cuda_python/docs/source/release/12.6.2-notes.md rename to cuda_python/docs/source/release/12.6.2-notes.rst diff --git a/cuda_python/docs/source/release/12.8.0-notes.md b/cuda_python/docs/source/release/12.8.0-notes.rst similarity index 100% rename from cuda_python/docs/source/release/12.8.0-notes.md rename to cuda_python/docs/source/release/12.8.0-notes.rst From 1c0eeebbab6fe7cb59a62e799467f2adf84abfc5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 Aug 2025 03:52:49 +0000 Subject: [PATCH 03/13] Phase 2: Convert markdown syntax to ReST syntax in all 38 files Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- cuda_bindings/docs/source/conduct.rst | 28 +- cuda_bindings/docs/source/contribute.rst | 7 +- .../docs/source/environment_variables.rst | 17 +- cuda_bindings/docs/source/install.rst | 93 +- cuda_bindings/docs/source/motivation.rst | 21 +- cuda_bindings/docs/source/overview.rst | 868 +++++++++--------- .../docs/source/release/11.4.0-notes.rst | 12 +- .../docs/source/release/11.5.0-notes.rst | 12 +- .../docs/source/release/11.6.0-notes.rst | 60 +- .../docs/source/release/11.6.1-notes.rst | 12 +- .../docs/source/release/11.7.0-notes.rst | 12 +- .../docs/source/release/11.7.1-notes.rst | 19 +- .../docs/source/release/11.8.0-notes.rst | 19 +- .../docs/source/release/11.8.1-notes.rst | 14 +- .../docs/source/release/11.8.2-notes.rst | 12 +- .../docs/source/release/11.8.3-notes.rst | 12 +- .../docs/source/release/11.8.4-notes.rst | 43 +- .../docs/source/release/11.8.5-notes.rst | 17 +- .../docs/source/release/11.8.6-notes.rst | 28 +- .../docs/source/release/12.0.0-notes.rst | 16 +- .../docs/source/release/12.1.0-notes.rst | 18 +- .../docs/source/release/12.2.0-notes.rst | 16 +- .../docs/source/release/12.2.1-notes.rst | 12 +- .../docs/source/release/12.3.0-notes.rst | 18 +- .../docs/source/release/12.4.0-notes.rst | 12 +- .../docs/source/release/12.5.0-notes.rst | 14 +- .../docs/source/release/12.6.0-notes.rst | 18 +- .../docs/source/release/12.6.1-notes.rst | 43 +- .../docs/source/release/12.6.2-notes.rst | 17 +- .../docs/source/release/12.8.0-notes.rst | 32 +- cuda_core/docs/source/conduct.rst | 28 +- cuda_core/docs/source/getting-started.rst | 152 +-- cuda_core/docs/source/install.rst | 59 +- cuda_python/docs/source/release.rst | 34 +- .../docs/source/release/11.8.6-notes.rst | 14 +- .../docs/source/release/12.6.1-notes.rst | 16 +- .../docs/source/release/12.6.2-notes.rst | 16 +- .../docs/source/release/12.8.0-notes.rst | 20 +- 38 files changed, 1014 insertions(+), 847 deletions(-) diff --git a/cuda_bindings/docs/source/conduct.rst b/cuda_bindings/docs/source/conduct.rst index 80f5032e86..61417b48da 100644 --- a/cuda_bindings/docs/source/conduct.rst +++ b/cuda_bindings/docs/source/conduct.rst @@ -1,10 +1,13 @@ -# Code of Conduct +Code of Conduct +=============== -## Overview +Overview +-------- -Define the code of conduct followed and enforced for the `cuda.bindings` project. +Define the code of conduct followed and enforced for the ``cuda.bindings`` project. -## Our Pledge +Our Pledge +---------- In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and @@ -13,7 +16,8 @@ size, disability, ethnicity, sex characteristics, gender identity and expression level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. -## Our Standards +Our Standards +------------- Examples of behavior that contributes to creating a positive environment include: @@ -35,7 +39,8 @@ Examples of unacceptable behavior by participants include: * Other conduct which could reasonably be considered inappropriate in a professional setting -## Our Responsibilities +Our Responsibilities +-------------------- Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in @@ -47,7 +52,8 @@ that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. -## Scope +Scope +----- This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of @@ -56,11 +62,12 @@ address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. -## Enforcement +Enforcement +----------- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at -[cuda-python-conduct@nvidia.com](mailto:cuda-python-conduct@nvidia.com) All +`cuda-python-conduct@nvidia.com `_ All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an @@ -71,7 +78,8 @@ Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. -## Attribution +Attribution +----------- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html diff --git a/cuda_bindings/docs/source/contribute.rst b/cuda_bindings/docs/source/contribute.rst index d26f117234..b50dca4aae 100644 --- a/cuda_bindings/docs/source/contribute.rst +++ b/cuda_bindings/docs/source/contribute.rst @@ -1,9 +1,10 @@ -# Contributing +Contributing +============ -Thank you for your interest in contributing to `cuda-bindings`! Based on the type of contribution, it will fall into two categories: +Thank you for your interest in contributing to ``cuda-bindings``! Based on the type of contribution, it will fall into two categories: 1. You want to report a bug, feature request, or documentation issue - - File an [issue](https://github.com/NVIDIA/cuda-python/issues/new/choose) + - File an `issue `_ describing what you encountered or what you want to see changed. - The NVIDIA team will evaluate the issues and triage them, scheduling them for a release. If you believe the issue needs priority attention diff --git a/cuda_bindings/docs/source/environment_variables.rst b/cuda_bindings/docs/source/environment_variables.rst index 7329e582cf..2f07c8d8bf 100644 --- a/cuda_bindings/docs/source/environment_variables.rst +++ b/cuda_bindings/docs/source/environment_variables.rst @@ -1,13 +1,16 @@ -# Environment Variables +Environment Variables +===================== -## Build-Time Environment Variables +Build-Time Environment Variables +-------------------------------- -- `CUDA_HOME` or `CUDA_PATH`: Specifies the location of the CUDA Toolkit. +- ``CUDA_HOME`` or ``CUDA_PATH``: Specifies the location of the CUDA Toolkit. -- `CUDA_PYTHON_PARSER_CACHING` : bool, toggles the caching of parsed header files during the cuda-bindings build process. If caching is enabled (`CUDA_PYTHON_PARSER_CACHING` is True), the cache path is set to ./cache_, where is derived from the cuda toolkit libraries used to build cuda-bindings. +- ``CUDA_PYTHON_PARSER_CACHING`` : bool, toggles the caching of parsed header files during the cuda-bindings build process. If caching is enabled (``CUDA_PYTHON_PARSER_CACHING`` is True), the cache path is set to ./cache_, where is derived from the cuda toolkit libraries used to build cuda-bindings. -- `CUDA_PYTHON_PARALLEL_LEVEL` (previously `PARALLEL_LEVEL`) : int, sets the number of threads used in the compilation of extension modules. Not setting it or setting it to 0 would disable parallel builds. +- ``CUDA_PYTHON_PARALLEL_LEVEL`` (previously ``PARALLEL_LEVEL``) : int, sets the number of threads used in the compilation of extension modules. Not setting it or setting it to 0 would disable parallel builds. -## Runtime Environment Variables +Runtime Environment Variables +----------------------------- -- `CUDA_PYTHON_CUDA_PER_THREAD_DEFAULT_STREAM` : When set to 1, the default stream is the per-thread default stream. When set to 0, the default stream is the legacy default stream. This defaults to 0, for the legacy default stream. See [Stream Synchronization Behavior](https://docs.nvidia.com/cuda/cuda-runtime-api/stream-sync-behavior.html) for an explanation of the legacy and per-thread default streams. +- ``CUDA_PYTHON_CUDA_PER_THREAD_DEFAULT_STREAM`` : When set to 1, the default stream is the per-thread default stream. When set to 0, the default stream is the legacy default stream. This defaults to 0, for the legacy default stream. See `Stream Synchronization Behavior `_ for an explanation of the legacy and per-thread default streams. diff --git a/cuda_bindings/docs/source/install.rst b/cuda_bindings/docs/source/install.rst index b7c693b9c2..66886c93b4 100644 --- a/cuda_bindings/docs/source/install.rst +++ b/cuda_bindings/docs/source/install.rst @@ -1,88 +1,91 @@ -# Installation +Installation +============ -## Runtime Requirements +Runtime Requirements +-------------------- -`cuda.bindings` supports the same platforms as CUDA. Runtime dependencies are: +``cuda.bindings`` supports the same platforms as CUDA. Runtime dependencies are: * Linux (x86-64, arm64) and Windows (x86-64) * Python 3.9 - 3.13 * Driver: Linux (580.65.06 or later) Windows (580.88 or later) * Optionally, NVRTC, nvJitLink, NVVM, and cuFile from CUDA Toolkit 13.x -```{note} -The optional CUDA Toolkit components are now installed via the `cuda-toolkit` metapackage from PyPI for improved dependency resolution. Components can also be installed via Conda, OS-specific package managers, or local installers (as described in the CUDA Toolkit [Windows](https://docs.nvidia.com/cuda/cuda-installation-guide-microsoft-windows/index.html) and [Linux](https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html) Installation Guides). -``` +.. note:: -Starting from v12.8.0, `cuda-python` becomes a meta package which currently depends only on `cuda-bindings`; in the future more sub-packages will be added to `cuda-python`. In the instructions below, we still use `cuda-python` as example to serve existing users, but everything is applicable to `cuda-bindings` as well. + The optional CUDA Toolkit components are now installed via the ``cuda-toolkit`` metapackage from PyPI for improved dependency resolution. Components can also be installed via Conda, OS-specific package managers, or local installers (as described in the CUDA Toolkit `Windows `_ and `Linux `_ Installation Guides). +Starting from v12.8.0, ``cuda-python`` becomes a meta package which currently depends only on ``cuda-bindings``; in the future more sub-packages will be added to ``cuda-python``. In the instructions below, we still use ``cuda-python`` as example to serve existing users, but everything is applicable to ``cuda-bindings`` as well. -## Installing from PyPI +Installing from PyPI +-------------------- -```console -$ pip install -U cuda-python -``` +.. code-block:: console + + $ pip install -U cuda-python Install all optional dependencies with: -```{code-block} shell -pip install -U cuda-python[all] -``` +.. code-block:: shell + + pip install -U cuda-python[all] Where the optional dependencies include: -* `nvidia-cuda-nvrtc` (NVRTC runtime compilation library) -* `nvidia-nvjitlink` (nvJitLink library) -* `nvidia-nvvm` (NVVM library) -* `nvidia-cufile` (cuFile library, Linux only) +* ``nvidia-cuda-nvrtc`` (NVRTC runtime compilation library) +* ``nvidia-nvjitlink`` (nvJitLink library) +* ``nvidia-nvvm`` (NVVM library) +* ``nvidia-cufile`` (cuFile library, Linux only) + +These are now installed through the ``cuda-toolkit`` metapackage for improved dependency resolution. -These are now installed through the `cuda-toolkit` metapackage for improved dependency resolution. +Installing from Conda +--------------------- +.. code-block:: console -## Installing from Conda + $ conda install -c conda-forge cuda-python -```console -$ conda install -c conda-forge cuda-python -``` +.. note:: -```{note} -When using conda, the `cuda-version` metapackage can be used to control the versions of CUDA Toolkit components that are installed to the conda environment. -``` + When using conda, the ``cuda-version`` metapackage can be used to control the versions of CUDA Toolkit components that are installed to the conda environment. For example: -```console -$ conda install -c conda-forge cuda-python cuda-version=13 -``` +.. code-block:: console + $ conda install -c conda-forge cuda-python cuda-version=13 -## Installing from Source +Installing from Source +---------------------- -### Requirements +Requirements +^^^^^^^^^^^^ * CUDA Toolkit headers[^1] * CUDA Runtime static library[^2] -[^1]: User projects that `cimport` CUDA symbols in Cython must also use CUDA Toolkit (CTK) types as provided by the `cuda.bindings` major.minor version. This results in CTK headers becoming a transitive dependency of downstream projects through CUDA Python. +[^1]: User projects that ``cimport`` CUDA symbols in Cython must also use CUDA Toolkit (CTK) types as provided by the ``cuda.bindings`` major.minor version. This results in CTK headers becoming a transitive dependency of downstream projects through CUDA Python. -[^2]: The CUDA Runtime static library (`libcudart_static.a` on Linux, `cudart_static.lib` on Windows) is part of the CUDA Toolkit. If using conda packages, it is contained in the `cuda-cudart-static` package. +[^2]: The CUDA Runtime static library (``libcudart_static.a`` on Linux, ``cudart_static.lib`` on Windows) is part of the CUDA Toolkit. If using conda packages, it is contained in the ``cuda-cudart-static`` package. -Source builds require that the provided CUDA headers are of the same major.minor version as the `cuda.bindings` you're trying to build. Despite this requirement, note that the minor version compatibility is still maintained. Use the `CUDA_HOME` (or `CUDA_PATH`) environment variable to specify the location of your headers. For example, if your headers are located in `/usr/local/cuda/include`, then you should set `CUDA_HOME` with: +Source builds require that the provided CUDA headers are of the same major.minor version as the ``cuda.bindings`` you're trying to build. Despite this requirement, note that the minor version compatibility is still maintained. Use the ``CUDA_HOME`` (or ``CUDA_PATH``) environment variable to specify the location of your headers. For example, if your headers are located in ``/usr/local/cuda/include``, then you should set ``CUDA_HOME`` with: -```console -$ export CUDA_HOME=/usr/local/cuda -``` +.. code-block:: console -See [Environment Variables](environment_variables.md) for a description of other build-time environment variables. + $ export CUDA_HOME=/usr/local/cuda -```{note} -Only `cydriver`, `cyruntime` and `cynvrtc` are impacted by the header requirement. -``` +See `Environment Variables `_ for a description of other build-time environment variables. +.. note:: -### Editable Install + Only ``cydriver``, ``cyruntime`` and ``cynvrtc`` are impacted by the header requirement. + +Editable Install +^^^^^^^^^^^^^^^^ You can use -```console -$ pip install -v -e . -``` +.. code-block:: console + + $ pip install -v -e . to install the module as editable in your current Python environment (e.g. for testing of porting other libraries to use the binding). diff --git a/cuda_bindings/docs/source/motivation.rst b/cuda_bindings/docs/source/motivation.rst index 5b8879f2bc..4fe4dcee39 100644 --- a/cuda_bindings/docs/source/motivation.rst +++ b/cuda_bindings/docs/source/motivation.rst @@ -1,7 +1,9 @@ -# Motivation -## What is CUDA Python? +Motivation +========== +What is CUDA Python? +-------------------- -NVIDIA’s CUDA Python provides [Cython](https://cython.org/) bindings and Python +NVIDIA’s CUDA Python provides `Cython `_ bindings and Python wrappers for the driver and runtime API for existing toolkits and libraries to simplify GPU-based accelerated processing. Python is one of the most popular programming languages for science, engineering, data analytics, and deep @@ -9,14 +11,15 @@ learning applications. The goal of CUDA Python is to unify the Python ecosystem with a single set of interfaces that provide full coverage of and access to the CUDA host APIs from Python. -## Why CUDA Python? +Why CUDA Python? +---------------- CUDA Python provides uniform APIs and bindings for inclusion into existing toolkits and libraries to simplify GPU-based parallel processing for HPC, data science, and AI. -[Numba](https://numba.pydata.org/), a Python compiler from -[Anaconda](https://www.anaconda.com/) that can compile Python code for execution +`Numba `_, a Python compiler from +`Anaconda `_ that can compile Python code for execution on CUDA-capable GPUs, provides Python developers with an easy entry into GPU-accelerated computing and a path for using increasingly sophisticated CUDA code with a minimum of new syntax and jargon. Numba has its own CUDA driver API @@ -24,9 +27,9 @@ bindings that can now be replaced with CUDA Python. With CUDA Python and Numba, you get the best of both worlds: rapid iterative development with Python and the speed of a compiled language targeting both CPUs and NVIDIA GPUs. -[CuPy](https://cupy.dev/) is a -[NumPy](https://numpy.org/)/[SciPy](https://www.scipy.org/) compatible Array -library, from [Preferred Networks](https://www.preferred.jp/en/), for +`CuPy `_ is a +`NumPy `_/`SciPy `_ compatible Array +library, from `Preferred Networks `_, for GPU-accelerated computing with Python. CUDA Python simplifies the CuPy build and allows for a faster and smaller memory footprint when importing the CuPy Python module. In the future, when more CUDA Toolkit libraries are supported, diff --git a/cuda_bindings/docs/source/overview.rst b/cuda_bindings/docs/source/overview.rst index 1168d926f5..08ca28f965 100644 --- a/cuda_bindings/docs/source/overview.rst +++ b/cuda_bindings/docs/source/overview.rst @@ -1,4 +1,5 @@ -# Overview +Overview +======== Python plays a key role within the science, engineering, data analytics, and deep learning application ecosystem. NVIDIA has long been committed to helping @@ -13,78 +14,80 @@ APIs from Python. We want to provide an ecosystem foundation to allow interoperability among different accelerated libraries. Most importantly, it should be easy for Python developers to use NVIDIA GPUs. -## `cuda.bindings` workflow +``cuda.bindings`` workflow +------------------------ Because Python is an interpreted language, you need a way to compile the device code into -[PTX](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html) and +`PTX `_ and then extract the function to be called at a later point in the application. You construct your device code in the form of a string and compile it with -[NVRTC](http://docs.nvidia.com/cuda/nvrtc/index.html), a runtime compilation -library for CUDA C++. Using the NVIDIA [Driver -API](http://docs.nvidia.com/cuda/cuda-driver-api/index.html), manually create a +`NVRTC `_, a runtime compilation +library for CUDA C++. Using the NVIDIA `Driver +API `_, manually create a CUDA context and all required resources on the GPU, then launch the compiled CUDA C++ code and retrieve the results from the GPU. Now that you have an overview, jump into a commonly used example for parallel programming: -[SAXPY](https://developer.nvidia.com/blog/six-ways-saxpy/). +`SAXPY `_. -The first thing to do is import the [Driver -API](https://docs.nvidia.com/cuda/cuda-driver-api/index.html) and -[NVRTC](https://docs.nvidia.com/cuda/nvrtc/index.html) modules from the `cuda.bindings` +The first thing to do is import the `Driver +API `_ and +`NVRTC `_ modules from the ``cuda.bindings`` package. Next, we consider how to store host data and pass it to the device. Different -approaches can be used to accomplish this and are described in [Preparing kernel -arguments](https://nvidia.github.io/cuda-python/cuda-bindings/latest/overview.html#preparing-kernel-arguments). +approaches can be used to accomplish this and are described in `Preparing kernel +arguments `_. In this example, we will use NumPy to store host data and pass it to the device, so let's import this dependency as well. -```python -from cuda.bindings import driver, nvrtc -import numpy as np -``` +.. code-block:: python + + from cuda.bindings import driver, nvrtc + import numpy as np Error checking is a fundamental best practice when working with low-level interfaces. The following code snippet lets us validate each API call and raise exceptions in case of error. -```python -def _cudaGetErrorEnum(error): - if isinstance(error, driver.CUresult): - err, name = driver.cuGetErrorName(error) - return name if err == driver.CUresult.CUDA_SUCCESS else "" - elif isinstance(error, nvrtc.nvrtcResult): - return nvrtc.nvrtcGetErrorString(error)[1] - else: - raise RuntimeError('Unknown error type: {}'.format(error)) - -def checkCudaErrors(result): - if result[0].value: - raise RuntimeError("CUDA error code={}({})".format(result[0].value, _cudaGetErrorEnum(result[0]))) - if len(result) == 1: - return None - elif len(result) == 2: - return result[1] - else: - return result[1:] -``` +.. code-block:: python + + def _cudaGetErrorEnum(error): + if isinstance(error, driver.CUresult): + err, name = driver.cuGetErrorName(error) + return name if err == driver.CUresult.CUDA_SUCCESS else "" + elif isinstance(error, nvrtc.nvrtcResult): + return nvrtc.nvrtcGetErrorString(error)[1] + else: + raise RuntimeError('Unknown error type: {}'.format(error)) + + def checkCudaErrors(result): + if result[0].value: + raise RuntimeError("CUDA error code={}({})".format(result[0].value, _cudaGetErrorEnum(result[0]))) + if len(result) == 1: + return None + elif len(result) == 2: + return result[1] + else: + return result[1:] It's common practice to write CUDA kernels near the top of a translation unit, so write it next. The entire kernel is wrapped in triple quotes to form a string. The string is compiled later using NVRTC. This is the only part of CUDA Python that requires some understanding of CUDA C++. For more information, see -[An Even Easier Introduction to -CUDA](https://developer.nvidia.com/blog/even-easier-introduction-cuda/). - -```python -saxpy = """\ -extern "C" __global__ -void saxpy(float a, float *x, float *y, float *out, size_t n) -{ - size_t tid = blockIdx.x * blockDim.x + threadIdx.x; - if (tid < n) { - out[tid] = a * x[tid] + y[tid]; - } -} -""" -``` +`An Even Easier Introduction to +CUDA `_. + +.. code-block:: python + + saxpy = """\ + extern "C" __global__ + void saxpy(float a, float *x, float *y, float *out, size_t n) + { + size_t tid = blockIdx.x * blockDim.x + threadIdx.x; + if (tid < n) { + out[tid] = a * x[tid] + y[tid]; + } + } + """ + Go ahead and compile the kernel into PTX. Remember that this is executed at runtime using NVRTC. There are three basic steps to NVRTC: - Create a program from the string. @@ -95,464 +98,485 @@ In the following code example, the Driver API is initialized so that the NVIDIA and GPU are accessible. Next, the GPU is queried for their compute capability. Finally, the program is compiled to target our local compute capability architecture with FMAD disabled. -```python -# Initialize CUDA Driver API -checkCudaErrors(driver.cuInit(0)) - -# Retrieve handle for device 0 -cuDevice = checkCudaErrors(driver.cuDeviceGet(0)) - -# Derive target architecture for device 0 -major = checkCudaErrors(driver.cuDeviceGetAttribute(driver.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevice)) -minor = checkCudaErrors(driver.cuDeviceGetAttribute(driver.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevice)) -arch_arg = bytes(f'--gpu-architecture=compute_{major}{minor}', 'ascii') - -# Create program -prog = checkCudaErrors(nvrtc.nvrtcCreateProgram(str.encode(saxpy), b"saxpy.cu", 0, [], [])) - -# Compile program -opts = [b"--fmad=false", arch_arg] -checkCudaErrors(nvrtc.nvrtcCompileProgram(prog, 2, opts)) - -# Get PTX from compilation -ptxSize = checkCudaErrors(nvrtc.nvrtcGetPTXSize(prog)) -ptx = b" " * ptxSize -checkCudaErrors(nvrtc.nvrtcGetPTX(prog, ptx)) -``` +.. code-block:: python + + Initialize CUDA Driver API + ========================== + checkCudaErrors(driver.cuInit(0)) + + Retrieve handle for device 0 + ============================ + cuDevice = checkCudaErrors(driver.cuDeviceGet(0)) + + Derive target architecture for device 0 + ======================================= + major = checkCudaErrors(driver.cuDeviceGetAttribute(driver.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevice)) + minor = checkCudaErrors(driver.cuDeviceGetAttribute(driver.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevice)) + arch_arg = bytes(f'--gpu-architecture=compute_{major}{minor}', 'ascii') + + Create program + ============== + prog = checkCudaErrors(nvrtc.nvrtcCreateProgram(str.encode(saxpy), b"saxpy.cu", 0, [], [])) + + Compile program + =============== + opts = [b"--fmad=false", arch_arg] + checkCudaErrors(nvrtc.nvrtcCompileProgram(prog, 2, opts)) + + Get PTX from compilation + ======================== + ptxSize = checkCudaErrors(nvrtc.nvrtcGetPTXSize(prog)) + ptx = b" " * ptxSize + checkCudaErrors(nvrtc.nvrtcGetPTX(prog, ptx)) Before you can use the PTX or do any work on the GPU, you must create a CUDA context. CUDA contexts are analogous to host processes for the device. In the following code example, a handle for compute device 0 is passed to -`cuCtxCreate` to designate that GPU for context creation. +``cuCtxCreate`` to designate that GPU for context creation. -```python -# Create context -context = checkCudaErrors(driver.cuCtxCreate(0, cuDevice)) -``` +.. code-block:: python + + Create context + ============== + context = checkCudaErrors(driver.cuCtxCreate(0, cuDevice)) With a CUDA context created on device 0, load the PTX generated earlier into a module. A module is analogous to dynamically loaded libraries for the device. After loading into the module, extract a specific kernel with -`cuModuleGetFunction`. It is not uncommon for multiple kernels to reside in PTX. +``cuModuleGetFunction``. It is not uncommon for multiple kernels to reside in PTX. + +.. code-block:: python -```python -# Load PTX as module data and retrieve function -ptx = np.char.array(ptx) -# Note: Incompatible --gpu-architecture would be detected here -module = checkCudaErrors(driver.cuModuleLoadData(ptx.ctypes.data)) -kernel = checkCudaErrors(driver.cuModuleGetFunction(module, b"saxpy")) -``` + Load PTX as module data and retrieve function + ============================================= + ptx = np.char.array(ptx) + Note: Incompatible --gpu-architecture would be detected here + ============================================================ + module = checkCudaErrors(driver.cuModuleLoadData(ptx.ctypes.data)) + kernel = checkCudaErrors(driver.cuModuleGetFunction(module, b"saxpy")) Next, get all your data prepared and transferred to the GPU. For increased application performance, you can input data on the device to eliminate data transfers. For completeness, this example shows how you would transfer data to and from the device. -```python -NUM_THREADS = 512 # Threads per block -NUM_BLOCKS = 32768 # Blocks per grid - -a = np.array([2.0], dtype=np.float32) -n = np.array(NUM_THREADS * NUM_BLOCKS, dtype=np.uint32) -bufferSize = n * a.itemsize - -hX = np.random.rand(n).astype(dtype=np.float32) -hY = np.random.rand(n).astype(dtype=np.float32) -hOut = np.zeros(n).astype(dtype=np.float32) -``` - -With the input data `a`, `x`, and `y` created for the SAXPY transform device, -resources must be allocated to store the data using `cuMemAlloc`. To allow for +.. code-block:: python + + NUM_THREADS = 512 # Threads per block + NUM_BLOCKS = 32768 # Blocks per grid + + a = np.array([2.0], dtype=np.float32) + n = np.array(NUM_THREADS * NUM_BLOCKS, dtype=np.uint32) + bufferSize = n * a.itemsize + + hX = np.random.rand(n).astype(dtype=np.float32) + hY = np.random.rand(n).astype(dtype=np.float32) + hOut = np.zeros(n).astype(dtype=np.float32) + +With the input data ``a``, ``x``, and ``y`` created for the SAXPY transform device, +resources must be allocated to store the data using ``cuMemAlloc``. To allow for more overlap between compute and data movement, use the asynchronous function -`cuMemcpyHtoDAsync`. It returns control to the CPU immediately following command +``cuMemcpyHtoDAsync``. It returns control to the CPU immediately following command execution. -Python doesn't have a natural concept of pointers, yet `cuMemcpyHtoDAsync` expects -`void*`. This is where we leverage NumPy's data types to retrieve each host data pointer -by calling `XX.ctypes.data` for the associated XX. - -```python -dXclass = checkCudaErrors(driver.cuMemAlloc(bufferSize)) -dYclass = checkCudaErrors(driver.cuMemAlloc(bufferSize)) -dOutclass = checkCudaErrors(driver.cuMemAlloc(bufferSize)) - -stream = checkCudaErrors(driver.cuStreamCreate(0)) - -checkCudaErrors(driver.cuMemcpyHtoDAsync( - dXclass, hX.ctypes.data, bufferSize, stream -)) -checkCudaErrors(driver.cuMemcpyHtoDAsync( - dYclass, hY.ctypes.data, bufferSize, stream -)) -``` +Python doesn't have a natural concept of pointers, yet ``cuMemcpyHtoDAsync`` expects +``void*``. This is where we leverage NumPy's data types to retrieve each host data pointer +by calling ``XX.ctypes.data`` for the associated XX. + +.. code-block:: python + + dXclass = checkCudaErrors(driver.cuMemAlloc(bufferSize)) + dYclass = checkCudaErrors(driver.cuMemAlloc(bufferSize)) + dOutclass = checkCudaErrors(driver.cuMemAlloc(bufferSize)) + + stream = checkCudaErrors(driver.cuStreamCreate(0)) + + checkCudaErrors(driver.cuMemcpyHtoDAsync( + dXclass, hX.ctypes.data, bufferSize, stream + )) + checkCudaErrors(driver.cuMemcpyHtoDAsync( + dYclass, hY.ctypes.data, bufferSize, stream + )) With data prep and resources allocation finished, the kernel is ready to be launched. To pass the location of the data on the device to the kernel execution configuration, you must retrieve the device pointer. In the following code -example, we call `int(XXclass)` to retrieve the device pointer value for the -associated XXclass as a Python `int` and wrap it in a `np.array` type. - -```python -dX = np.array([int(dXclass)], dtype=np.uint64) -dY = np.array([int(dYclass)], dtype=np.uint64) -dOut = np.array([int(dOutclass)], dtype=np.uint64) -``` - -The launch API `cuLaunchKernel` also expects a pointer input for the argument list -but this time it's of type `void**`. What this means is that our argument list needs to -be a contiguous array of `void*` elements, where each element is the pointer to a kernel -argument on either host or device. Since we already prepared each of our arguments into a `np.array` type, the -construction of our final contiguous array is done by retrieving the `XX.ctypes.data` +example, we call ``int(XXclass)`` to retrieve the device pointer value for the +associated XXclass as a Python ``int`` and wrap it in a ``np.array`` type. + +.. code-block:: python + + dX = np.array([int(dXclass)], dtype=np.uint64) + dY = np.array([int(dYclass)], dtype=np.uint64) + dOut = np.array([int(dOutclass)], dtype=np.uint64) + +The launch API ``cuLaunchKernel`` also expects a pointer input for the argument list +but this time it's of type ``void**``. What this means is that our argument list needs to +be a contiguous array of ``void*`` elements, where each element is the pointer to a kernel +argument on either host or device. Since we already prepared each of our arguments into a ``np.array`` type, the +construction of our final contiguous array is done by retrieving the ``XX.ctypes.data`` of each kernel argument. -```python -args = [a, dX, dY, dOut, n] -args = np.array([arg.ctypes.data for arg in args], dtype=np.uint64) -``` +.. code-block:: python + + args = [a, dX, dY, dOut, n] + args = np.array([arg.ctypes.data for arg in args], dtype=np.uint64) Now the kernel can be launched: -```python -checkCudaErrors(driver.cuLaunchKernel( - kernel, - NUM_BLOCKS, # grid x dim - 1, # grid y dim - 1, # grid z dim - NUM_THREADS, # block x dim - 1, # block y dim - 1, # block z dim - 0, # dynamic shared memory - stream, # stream - args.ctypes.data, # kernel arguments - 0, # extra (ignore) -)) - -checkCudaErrors(driver.cuMemcpyDtoHAsync( - hOut.ctypes.data, dOutclass, bufferSize, stream -)) -checkCudaErrors(driver.cuStreamSynchronize(stream)) -``` - -The `cuLaunchKernel` function takes the compiled module kernel and execution +.. code-block:: python + + checkCudaErrors(driver.cuLaunchKernel( + kernel, + NUM_BLOCKS, # grid x dim + 1, # grid y dim + 1, # grid z dim + NUM_THREADS, # block x dim + 1, # block y dim + 1, # block z dim + 0, # dynamic shared memory + stream, # stream + args.ctypes.data, # kernel arguments + 0, # extra (ignore) + )) + + checkCudaErrors(driver.cuMemcpyDtoHAsync( + hOut.ctypes.data, dOutclass, bufferSize, stream + )) + checkCudaErrors(driver.cuStreamSynchronize(stream)) + +The ``cuLaunchKernel`` function takes the compiled module kernel and execution configuration parameters. The device code is launched in the same stream as the data transfers. That ensures that the kernel's compute is performed only after the data has finished transfer, as all API calls and kernel launches within a stream are serialized. After the call to transfer data back to the host is -executed, `cuStreamSynchronize` is used to halt CPU execution until all operations +executed, ``cuStreamSynchronize`` is used to halt CPU execution until all operations in the designated stream are finished. -```python -# Assert values are same after running kernel -hZ = a * hX + hY -if not np.allclose(hOut, hZ): - raise ValueError("Error outside tolerance for host-device vectors") -``` +.. code-block:: python + + Assert values are same after running kernel + =========================================== + hZ = a * hX + hY + if not np.allclose(hOut, hZ): + raise ValueError("Error outside tolerance for host-device vectors") Perform verification of the data to ensure correctness and finish the code with memory clean up. -```python -checkCudaErrors(driver.cuStreamDestroy(stream)) -checkCudaErrors(driver.cuMemFree(dXclass)) -checkCudaErrors(driver.cuMemFree(dYclass)) -checkCudaErrors(driver.cuMemFree(dOutclass)) -checkCudaErrors(driver.cuModuleUnload(module)) -checkCudaErrors(driver.cuCtxDestroy(context)) -``` +.. code-block:: python + + checkCudaErrors(driver.cuStreamDestroy(stream)) + checkCudaErrors(driver.cuMemFree(dXclass)) + checkCudaErrors(driver.cuMemFree(dYclass)) + checkCudaErrors(driver.cuMemFree(dOutclass)) + checkCudaErrors(driver.cuModuleUnload(module)) + checkCudaErrors(driver.cuCtxDestroy(context)) -## Performance +Performance +----------- Performance is a primary driver in targeting GPUs in your application. So, how does the above code compare to its C++ version? Table 1 shows that the results -are nearly identical. [NVIDIA NSight -Systems](https://developer.nvidia.com/nsight-systems) was used to retrieve -kernel performance and [CUDA -Events](https://developer.nvidia.com/blog/how-implement-performance-metrics-cuda-cc/) +are nearly identical. `NVIDIA NSight +Systems `_ was used to retrieve +kernel performance and `CUDA +Events `_ was used for application performance. The following command was used to profile the applications: -```{code-block} shell -nsys profile -s none -t cuda --stats=true -``` - -```{list-table} Kernel and application performance comparison. -:header-rows: 1 - -* - - - C++ - - Python -* - Kernel execution - - 352µs - - 352µs -* - Application execution - - 1076ms - - 1080ms -``` - -`cuda.bindings` is also compatible with [NVIDIA Nsight -Compute](https://developer.nvidia.com/nsight-compute), which is an +.. code-block:: {code-block} shell + + nsys profile -s none -t cuda --stats=true + +.. code-block:: {list-table} Kernel and application performance comparison. + + :header-rows: 1 + + * - + - C++ + - Python + * - Kernel execution + - 352µs + - 352µs + * - Application execution + - 1076ms + - 1080ms + +``cuda.bindings`` is also compatible with `NVIDIA Nsight +Compute `_, which is an interactive kernel profiler for CUDA applications. It allows you to have detailed insights into kernel performance. This is useful when you're trying to -maximize performance ({numref}`Figure 1`). +maximize performance ({numref}``Figure 1``). -```{figure} _static/images/Nsight-Compute-CLI-625x473.png -:name: Figure 1 +.. code-block:: {figure} _static/images/Nsight-Compute-CLI-625x473.png -Screenshot of Nsight Compute CLI output of `cuda.bindings` example. -``` + :name: Figure 1 + + Screenshot of Nsight Compute CLI output of ``cuda.bindings`` example. -## Preparing kernel arguments +Preparing kernel arguments +-------------------------- -The `cuLaunchKernel` API bindings retain low-level CUDA argument preparation requirements: +The ``cuLaunchKernel`` API bindings retain low-level CUDA argument preparation requirements: -* Each kernel argument is a `void*` (i.e. pointer to the argument) -* `kernelParams` is a `void**` (i.e. pointer to a list of kernel arguments) -* `kernelParams` arguments are in contiguous memory +* Each kernel argument is a ``void*`` (i.e. pointer to the argument) +* ``kernelParams`` is a ``void**`` (i.e. pointer to a list of kernel arguments) +* ``kernelParams`` arguments are in contiguous memory These requirements can be met with two different approaches, using either NumPy or ctypes. -### Using NumPy +Using NumPy +^^^^^^^^^^^ -NumPy [Array objects](https://numpy.org/doc/stable/reference/arrays.html) can be used to fulfill each of these conditions directly. +NumPy `Array objects `_ can be used to fulfill each of these conditions directly. Let's use the following kernel definition as an example: -```python -kernel_string = """\ -typedef struct { - int value; -} testStruct; - -extern "C" __global__ -void testkernel(int i, int *pi, - float f, float *pf, - testStruct s, testStruct *ps) -{ - *pi = i; - *pf = f; - ps->value = s.value; -} -""" -``` +.. code-block:: python + + kernel_string = """\ + typedef struct { + int value; + } testStruct; + + extern "C" __global__ + void testkernel(int i, int *pi, + float f, float *pf, + testStruct s, testStruct *ps) + { + *pi = i; + *pf = f; + ps->value = s.value; + } + """ The first step is to create array objects with types corresponding to your kernel arguments. Primitive NumPy types have the following corresponding kernel types: -```{list-table} Correspondence between NumPy types and kernel types. -:header-rows: 1 - -* - NumPy type - - Corresponding kernel types - - itemsize (bytes) -* - bool - - bool - - 1 -* - int8 - - char, signed char, int8_t - - 1 -* - int16 - - short, signed short, int16_t - - 2 -* - int32 - - int, signed int, int32_t - - 4 -* - int64 - - long long, signed long long, int64_t - - 8 -* - uint8 - - unsigned char, uint8_t - - 1 -* - uint16 - - unsigned short, uint16_t - - 2 -* - uint32 - - unsigned int, uint32_t - - 4 -* - uint64 - - unsigned long long, uint64_t - - 8 -* - float16 - - half - - 2 -* - float32 - - float - - 4 -* - float64 - - double - - 8 -* - complex64 - - float2, cuFloatComplex, complex<float> - - 8 -* - complex128 - - double2, cuDoubleComplex, complex<double> - - 16 -``` +.. code-block:: {list-table} Correspondence between NumPy types and kernel types. + + :header-rows: 1 + + * - NumPy type + - Corresponding kernel types + - itemsize (bytes) + * - bool + - bool + - 1 + * - int8 + - char, signed char, int8_t + - 1 + * - int16 + - short, signed short, int16_t + - 2 + * - int32 + - int, signed int, int32_t + - 4 + * - int64 + - long long, signed long long, int64_t + - 8 + * - uint8 + - unsigned char, uint8_t + - 1 + * - uint16 + - unsigned short, uint16_t + - 2 + * - uint32 + - unsigned int, uint32_t + - 4 + * - uint64 + - unsigned long long, uint64_t + - 8 + * - float16 + - half + - 2 + * - float32 + - float + - 4 + * - float64 + - double + - 8 + * - complex64 + - float2, cuFloatComplex, complex<float> + - 8 + * - complex128 + - double2, cuDoubleComplex, complex<double> + - 16 Furthermore, custom NumPy types can be used to support both platform-dependent types and user-defined structures as kernel arguments. This example uses the following types: -* `int` is `np.uint32` -* `float` is `np.float32` -* `int*`, `float*` and `testStruct*` are `np.intp` -* `testStruct` is a custom user type `np.dtype([("value", np.int32)], align=True)` +* ``int`` is ``np.uint32`` +* ``float`` is ``np.float32`` +* ``int*``, ``float*`` and ``testStruct*`` are ``np.intp`` +* ``testStruct`` is a custom user type ``np.dtype([("value", np.int32)], align=True)`` -Note how all three pointers are `np.intp` since the pointer values are always a representation of an address space. +Note how all three pointers are ``np.intp`` since the pointer values are always a representation of an address space. Putting it all together: -```python -# Define a custom type -testStruct = np.dtype([("value", np.int32)], align=True) - -# Allocate device memory -pInt = checkCudaErrors(cudart.cudaMalloc(np.dtype(np.int32).itemsize)) -pFloat = checkCudaErrors(cudart.cudaMalloc(np.dtype(np.float32).itemsize)) -pStruct = checkCudaErrors(cudart.cudaMalloc(testStruct.itemsize)) - -# Collect all input kernel arguments into a single tuple for further processing -kernelValues = ( - np.array(1, dtype=np.uint32), - np.array([pInt], dtype=np.intp), - np.array(123.456, dtype=np.float32), - np.array([pFloat], dtype=np.intp), - np.array([5], testStruct), - np.array([pStruct], dtype=np.intp), -) -``` - -The final step is to construct a `kernelParams` argument that fulfills all of the launch API conditions. This is made easy because each array object comes -with a [ctypes](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.ctypes.html#numpy.ndarray.ctypes) data attribute that returns the underlying `void*` pointer value. +.. code-block:: python + + Define a custom type + ==================== + testStruct = np.dtype([("value", np.int32)], align=True) + + Allocate device memory + ====================== + pInt = checkCudaErrors(cudart.cudaMalloc(np.dtype(np.int32).itemsize)) + pFloat = checkCudaErrors(cudart.cudaMalloc(np.dtype(np.float32).itemsize)) + pStruct = checkCudaErrors(cudart.cudaMalloc(testStruct.itemsize)) + + Collect all input kernel arguments into a single tuple for further processing + ============================================================================= + kernelValues = ( + np.array(1, dtype=np.uint32), + np.array([pInt], dtype=np.intp), + np.array(123.456, dtype=np.float32), + np.array([pFloat], dtype=np.intp), + np.array([5], testStruct), + np.array([pStruct], dtype=np.intp), + ) + +The final step is to construct a ``kernelParams`` argument that fulfills all of the launch API conditions. This is made easy because each array object comes +with a `ctypes `_ data attribute that returns the underlying ``void*`` pointer value. By having the final array object contain all pointers, we fulfill the contiguous array requirement: -```python -kernelParams = np.array([arg.ctypes.data for arg in kernelValues], dtype=np.intp) -``` +.. code-block:: python + + kernelParams = np.array([arg.ctypes.data for arg in kernelValues], dtype=np.intp) + +The launch API supports `Buffer Protocol `_ objects, therefore we can pass the array object directly. -The launch API supports [Buffer Protocol](https://docs.python.org/3/c-api/buffer.html) objects, therefore we can pass the array object directly. +.. code-block:: python -```python -checkCudaErrors(cuda.cuLaunchKernel( - kernel, - 1, 1, 1, # grid dim - 1, 1, 1, # block dim - 0, stream, # shared mem and stream - kernelParams=kernelParams, - extra=0, -)) -``` + checkCudaErrors(cuda.cuLaunchKernel( + kernel, + 1, 1, 1, # grid dim + 1, 1, 1, # block dim + 0, stream, # shared mem and stream + kernelParams=kernelParams, + extra=0, + )) -### Using ctypes +Using ctypes +^^^^^^^^^^^^ -The [ctypes](https://docs.python.org/3/library/ctypes.html) approach relaxes the parameter preparation requirement by delegating the contiguous memory requirement to the API launch call. +The `ctypes `_ approach relaxes the parameter preparation requirement by delegating the contiguous memory requirement to the API launch call. Let's use the same kernel definition as the previous section for the example. -The ctypes approach treats the `kernelParams` argument as a pair of two tuples: `kernel_values` and `kernel_types`. +The ctypes approach treats the ``kernelParams`` argument as a pair of two tuples: ``kernel_values`` and ``kernel_types``. -* `kernel_values` contain Python values to be used as an input to your kernel -* `kernel_types` contain the data types that your kernel_values should be converted into +* ``kernel_values`` contain Python values to be used as an input to your kernel +* ``kernel_types`` contain the data types that your kernel_values should be converted into -The ctypes [fundamental data types](https://docs.python.org/3/library/ctypes.html#fundamental-data-types) documentation describes the compatibility between different Python types and C types. -Furthermore, [custom data types](https://docs.python.org/3/library/ctypes.html#calling-functions-with-your-own-custom-data-types) can be used to support kernels with custom types. +The ctypes `fundamental data types `_ documentation describes the compatibility between different Python types and C types. +Furthermore, `custom data types `_ can be used to support kernels with custom types. For this example the result becomes: -```python -# Define a custom type -class testStruct(ctypes.Structure): - _fields_ = [("value", ctypes.c_int)] - -# Allocate device memory -pInt = checkCudaErrors(cudart.cudaMalloc(ctypes.sizeof(ctypes.c_int))) -pFloat = checkCudaErrors(cudart.cudaMalloc(ctypes.sizeof(ctypes.c_float))) -pStruct = checkCudaErrors(cudart.cudaMalloc(ctypes.sizeof(testStruct))) - -# Collect all input kernel arguments into a single tuple for further processing -kernelValues = ( - 1, - pInt, - 123.456, - pFloat, - testStruct(5), - pStruct, -) -kernelTypes = ( - ctypes.c_int, - ctypes.c_void_p, - ctypes.c_float, - ctypes.c_void_p, - None, - ctypes.c_void_p, -) -``` - -Values that are set to `None` have a special meaning: - -1. The value supports a callable `getPtr` that returns the pointer address of the underlining C object address (e.g. all CUDA C types that are exposed to Python as Python classes) -2. The value is an instance of `ctypes.Structure` -3. The value is an `Enum` +.. code-block:: python + + Define a custom type + ==================== + class testStruct(ctypes.Structure): + _fields_ = [("value", ctypes.c_int)] + + Allocate device memory + ====================== + pInt = checkCudaErrors(cudart.cudaMalloc(ctypes.sizeof(ctypes.c_int))) + pFloat = checkCudaErrors(cudart.cudaMalloc(ctypes.sizeof(ctypes.c_float))) + pStruct = checkCudaErrors(cudart.cudaMalloc(ctypes.sizeof(testStruct))) + + Collect all input kernel arguments into a single tuple for further processing + ============================================================================= + kernelValues = ( + 1, + pInt, + 123.456, + pFloat, + testStruct(5), + pStruct, + ) + kernelTypes = ( + ctypes.c_int, + ctypes.c_void_p, + ctypes.c_float, + ctypes.c_void_p, + None, + ctypes.c_void_p, + ) + +Values that are set to ``None`` have a special meaning: + +1. The value supports a callable ``getPtr`` that returns the pointer address of the underlining C object address (e.g. all CUDA C types that are exposed to Python as Python classes) +2. The value is an instance of ``ctypes.Structure`` +3. The value is an ``Enum`` In all three cases, the API call will fetch the underlying pointer value and construct a contiguous array with other kernel parameters. With the setup complete, the kernel can be launched: -```python -checkCudaErrors(cuda.cuLaunchKernel( - kernel, - 1, 1, 1, # grid dim - 1, 1, 1, # block dim - 0, stream, # shared mem and stream - kernelParams=(kernelValues, kernelTypes), - extra=0, -)) -``` - -### CUDA objects - -Certain CUDA kernels use native CUDA types as their parameters such as `cudaTextureObject_t`. These types require special handling since they're neither a primitive ctype nor a custom user type. Since `cuda.bindings` exposes each of them as Python classes, they each implement `getPtr()` and `__int__()`. These two callables used to support the NumPy and ctypes approach. The difference between each call is further described under [Tips and Tricks](https://nvidia.github.io/cuda-python/cuda-bindings/latest/tips_and_tricks.html#). - -For this example, lets use the `transformKernel` from [examples/0_Introduction/simpleCubemapTexture_test.py](https://github.com/NVIDIA/cuda-python/blob/main/cuda_bindings/examples/0_Introduction/simpleCubemapTexture_test.py): - -```python -simpleCubemapTexture = """\ -extern "C" -__global__ void transformKernel(float *g_odata, int width, cudaTextureObject_t tex) -{ - ... -} -""" - -def main(): - ... - d_data = checkCudaErrors(cudart.cudaMalloc(size)) - width = 64 - tex = checkCudaErrors(cudart.cudaCreateTextureObject(texRes, texDescr, None)) - ... -``` - -For NumPy, we can convert these CUDA types by leveraging the `__int__()` call to fetch the address of the underlying `cudaTextureObject_t` C object and wrapping it in a NumPy object array of type `np.intp`: - -```python -kernelValues = ( - np.array([d_data], dtype=np.intp), - np.array(width, dtype=np.uint32), - np.array([int(tex)], dtype=np.intp), -) -kernelArgs = np.array([arg.ctypes.data for arg in kernelValues], dtype=np.intp) -``` - -For ctypes, we leverage the special handling of `None` type since each Python class already implements `getPtr()`: - -```python -kernelValues = ( - d_data, - width, - tex, -) -kernelTypes = ( - ctypes.c_void_p, - ctypes.c_int, - None, -) -kernelArgs = (kernelValues, kernelTypes) -``` +.. code-block:: python + + checkCudaErrors(cuda.cuLaunchKernel( + kernel, + 1, 1, 1, # grid dim + 1, 1, 1, # block dim + 0, stream, # shared mem and stream + kernelParams=(kernelValues, kernelTypes), + extra=0, + )) + +CUDA objects +^^^^^^^^^^^^ + +Certain CUDA kernels use native CUDA types as their parameters such as ``cudaTextureObject_t``. These types require special handling since they're neither a primitive ctype nor a custom user type. Since ``cuda.bindings`` exposes each of them as Python classes, they each implement ``getPtr()`` and ``__int__()``. These two callables used to support the NumPy and ctypes approach. The difference between each call is further described under `Tips and Tricks `_. + +For this example, lets use the ``transformKernel`` from `examples/0_Introduction/simpleCubemapTexture_test.py `_: + +.. code-block:: python + + simpleCubemapTexture = """\ + extern "C" + __global__ void transformKernel(float *g_odata, int width, cudaTextureObject_t tex) + { + ... + } + """ + + def main(): + ... + d_data = checkCudaErrors(cudart.cudaMalloc(size)) + width = 64 + tex = checkCudaErrors(cudart.cudaCreateTextureObject(texRes, texDescr, None)) + ... + +For NumPy, we can convert these CUDA types by leveraging the ``__int__()`` call to fetch the address of the underlying ``cudaTextureObject_t`` C object and wrapping it in a NumPy object array of type ``np.intp``: + +.. code-block:: python + + kernelValues = ( + np.array([d_data], dtype=np.intp), + np.array(width, dtype=np.uint32), + np.array([int(tex)], dtype=np.intp), + ) + kernelArgs = np.array([arg.ctypes.data for arg in kernelValues], dtype=np.intp) + +For ctypes, we leverage the special handling of ``None`` type since each Python class already implements ``getPtr()``: + +.. code-block:: python + + kernelValues = ( + d_data, + width, + tex, + ) + kernelTypes = ( + ctypes.c_void_p, + ctypes.c_int, + None, + ) + kernelArgs = (kernelValues, kernelTypes) diff --git a/cuda_bindings/docs/source/release/11.4.0-notes.rst b/cuda_bindings/docs/source/release/11.4.0-notes.rst index 9eaa4eff0a..c6a2972da1 100644 --- a/cuda_bindings/docs/source/release/11.4.0-notes.rst +++ b/cuda_bindings/docs/source/release/11.4.0-notes.rst @@ -1,18 +1,22 @@ -# CUDA Python 11.4.0 Release notes +CUDA Python 11.4.0 Release notes +================================ Released on August 16, 2021 -## Highlights +Highlights +---------- - Initial EA release for CUDA Python - Supports all platforms that CUDA is supported - Supports all CUDA 11.x releases - Low-level CUDA Cython bindings and Python wrappers -## Limitations +Limitations +----------- - Source code release only; Python packages coming in a future release. -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - cudaGetTextureReference - cudaGetSurfaceReference diff --git a/cuda_bindings/docs/source/release/11.5.0-notes.rst b/cuda_bindings/docs/source/release/11.5.0-notes.rst index 130cb17d07..f239df4528 100644 --- a/cuda_bindings/docs/source/release/11.5.0-notes.rst +++ b/cuda_bindings/docs/source/release/11.5.0-notes.rst @@ -1,8 +1,10 @@ -# CUDA Python 11.5.0 Release notes +CUDA Python 11.5.0 Release notes +================================ Released on October 18, 2021 -## Highlights +Highlights +---------- - PyPi support - Conda support - GA release for CUDA Python @@ -10,11 +12,13 @@ Released on October 18, 2021 - Supports all CUDA 11.x releases - Low-level CUDA Cython bindings and Python wrappers -## Limitations +Limitations +----------- - Changing default stream not supported; coming in future release -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - cudaGetTextureReference - cudaGetSurfaceReference diff --git a/cuda_bindings/docs/source/release/11.6.0-notes.rst b/cuda_bindings/docs/source/release/11.6.0-notes.rst index 664da16249..3c7b2cb01f 100644 --- a/cuda_bindings/docs/source/release/11.6.0-notes.rst +++ b/cuda_bindings/docs/source/release/11.6.0-notes.rst @@ -1,52 +1,58 @@ -# CUDA Python 11.6.0 Release notes +CUDA Python 11.6.0 Release notes +================================ Released on Januray 12, 2022 -## Highlights +Highlights +---------- - Support CUDA Toolkit 11.6 - Support Profiler APIs - Support Graphic APIs (EGL, GL, VDPAU) - Support changing default stream - Relaxed primitive interoperability -### Default stream +Default stream +^^^^^^^^^^^^^^ Changing default stream to Per-Thread-Default-Stream (PTDS) is done through environment variable before execution: -```{code-block} shell -export CUDA_PYTHON_CUDA_PER_THREAD_DEFAULT_STREAM=1 -``` +.. code-block:: shell -When set to 1, the default stream is the per-thread default stream. When set to 0, the default stream is the legacy default stream. This defaults to 0, for the legacy default stream. See [Stream Synchronization Behavior](https://docs.nvidia.com/cuda/cuda-runtime-api/stream-sync-behavior.html) for an explanation of the legacy and per-thread default streams. + export CUDA_PYTHON_CUDA_PER_THREAD_DEFAULT_STREAM=1 -### Primitive interoperability +When set to 1, the default stream is the per-thread default stream. When set to 0, the default stream is the legacy default stream. This defaults to 0, for the legacy default stream. See `Stream Synchronization Behavior `_ for an explanation of the legacy and per-thread default streams. + +Primitive interoperability +^^^^^^^^^^^^^^^^^^^^^^^^^^ APIs accepting classes that wrap a primitive value are now interoperable with the underlining value. Example 1: Structure member handles interoperability. -```{code-block} python ->>> waitParams = cuda.CUstreamMemOpWaitValueParams_st() ->>> waitParams.value64 = 1 ->>> waitParams.value64 - ->>> waitParams.value64 = cuda.cuuint64_t(2) ->>> waitParams.value64 - -``` +.. code-block:: python + + >>> waitParams = cuda.CUstreamMemOpWaitValueParams_st() + >>> waitParams.value64 = 1 + >>> waitParams.value64 + + >>> waitParams.value64 = cuda.cuuint64_t(2) + >>> waitParams.value64 + Example 2: Function signature handles interoperability. -```{code-block} python ->>> cudart.cudaStreamQuery(cudart.cudaStreamNonBlocking) -(,) ->>> cudart.cudaStreamQuery(cudart.cudaStream_t(cudart.cudaStreamNonBlocking)) -(,) -``` +.. code-block:: python -## Limitations + >>> cudart.cudaStreamQuery(cudart.cudaStreamNonBlocking) + (,) + >>> cudart.cudaStreamQuery(cudart.cudaStream_t(cudart.cudaStreamNonBlocking)) + (,) -### CUDA Functions Not Supported in this Release +Limitations +----------- + +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol @@ -69,5 +75,5 @@ Example 2: Function signature handles interoperability. - cudaSetValidDevices - cudaVDPAUSetVDPAUDevice -```{note} Deprecated APIs are removed from tracking -``` +.. note:: Deprecated APIs are removed from tracking + diff --git a/cuda_bindings/docs/source/release/11.6.1-notes.rst b/cuda_bindings/docs/source/release/11.6.1-notes.rst index ddd6ff5101..473982ba9e 100644 --- a/cuda_bindings/docs/source/release/11.6.1-notes.rst +++ b/cuda_bindings/docs/source/release/11.6.1-notes.rst @@ -1,13 +1,17 @@ -# CUDA Python 11.6.1 Release notes +CUDA Python 11.6.1 Release notes +================================ Released on March 18, 2022 -## Highlights +Highlights +---------- - Fix string decomposition for WSL library load -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/11.7.0-notes.rst b/cuda_bindings/docs/source/release/11.7.0-notes.rst index 22500c7a23..7ea9c64854 100644 --- a/cuda_bindings/docs/source/release/11.7.0-notes.rst +++ b/cuda_bindings/docs/source/release/11.7.0-notes.rst @@ -1,13 +1,17 @@ -# CUDA Python 11.7.0 Release notes +CUDA Python 11.7.0 Release notes +================================ Released on May 11, 2022 -## Highlights +Highlights +---------- - Support CUDA Toolkit 11.7 -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/11.7.1-notes.rst b/cuda_bindings/docs/source/release/11.7.1-notes.rst index 2997c9da56..c2c2c0f722 100644 --- a/cuda_bindings/docs/source/release/11.7.1-notes.rst +++ b/cuda_bindings/docs/source/release/11.7.1-notes.rst @@ -1,20 +1,24 @@ -# CUDA Python 11.7.1 Release notes +CUDA Python 11.7.1 Release notes +================================ Released on June 29, 2022 -## Highlights +Highlights +---------- - Fix error propagation in CUDA Runtime bindings -- Resolves [issue #22](https://github.com/NVIDIA/cuda-python/issues/22) +- Resolves `issue #22 `_ -## Limitations +Limitations +----------- -### Source builds +Source builds +^^^^^^^^^^^^^ CUDA Python no longer re-declares CUDA types, instead it uses the types from CUDA C headers. As such source builds now need to access to latest CTK headers. In particular: 1. "$CUDA_HOME/include" has latest CTK headers 2. CTK headers have all types defined -(2) Certain CUDA types are not declared on mobile platforms and may face a "has not been declared" error during source builds. A temporary workaround is to use the headers found in [https://gitlab.com/nvidia/headers/cuda](https://gitlab.com/nvidia/headers/cuda). In particular CUDA Python needs the following headers and their dependencies: +(2) Certain CUDA types are not declared on mobile platforms and may face a "has not been declared" error during source builds. A temporary workaround is to use the headers found in `https://gitlab.com/nvidia/headers/cuda `_. In particular CUDA Python needs the following headers and their dependencies: - cuda.h - cudaProfiler.h - driver_types.h @@ -23,7 +27,8 @@ CUDA Python no longer re-declares CUDA types, instead it uses the types from CUD This a short-term limitation and will be relaxed in a future release. -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/11.8.0-notes.rst b/cuda_bindings/docs/source/release/11.8.0-notes.rst index c5bf9f71c3..c730af2ca0 100644 --- a/cuda_bindings/docs/source/release/11.8.0-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.0-notes.rst @@ -1,22 +1,27 @@ -# CUDA Python 11.8.0 Release notes +CUDA Python 11.8.0 Release notes +================================ Released on October 3, 2022 -## Highlights +Highlights +---------- - Support CUDA Toolkit 11.8 - Source builds allow for missing types and APIs - Resolves source builds for mobile platforms -- Resolves [issue #24](https://github.com/NVIDIA/cuda-python/issues/24) +- Resolves `issue #24 `_ -### Source Builds +Source Builds +^^^^^^^^^^^^^ -CUDA Python source builds now parse CUDA headers located in $CUDA_HOME directory, enabling/disabling types and APIs if defined. Therefore this removes the need for CTK headers to have all types defined. By allowing minor variations, previous [11.7.1 mobile platform workaround](https://nvidia.github.io/cuda-python/release/11.7.1-notes.html#source-builds) is no longer needed. +CUDA Python source builds now parse CUDA headers located in $CUDA_HOME directory, enabling/disabling types and APIs if defined. Therefore this removes the need for CTK headers to have all types defined. By allowing minor variations, previous `11.7.1 mobile platform workaround `_ is no longer needed. It's still required that source builds use the latest CTK headers (i.e. “$CUDA_HOME/include” has latest CTK headers). -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/11.8.1-notes.rst b/cuda_bindings/docs/source/release/11.8.1-notes.rst index f7c2e7d450..0566b739fe 100644 --- a/cuda_bindings/docs/source/release/11.8.1-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.1-notes.rst @@ -1,14 +1,18 @@ -# CUDA Python 11.8.1 Release notes +CUDA Python 11.8.1 Release notes +================================ Released on November 4, 2022 -## Highlights -- Resolves [issue #27](https://github.com/NVIDIA/cuda-python/issues/27) +Highlights +---------- +- Resolves `issue #27 `_ - Update install instructions to use latest CTK -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/11.8.2-notes.rst b/cuda_bindings/docs/source/release/11.8.2-notes.rst index f9d1655652..bff6b1b2da 100644 --- a/cuda_bindings/docs/source/release/11.8.2-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.2-notes.rst @@ -1,13 +1,17 @@ -# CUDA Python 11.8.2 Release notes +CUDA Python 11.8.2 Release notes +================================ Released on May 18, 2023 -## Highlights +Highlights +---------- - Open libcuda.so.1 instead of libcuda.so -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/11.8.3-notes.rst b/cuda_bindings/docs/source/release/11.8.3-notes.rst index a8ff840c1e..2b101bbb47 100644 --- a/cuda_bindings/docs/source/release/11.8.3-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.3-notes.rst @@ -1,15 +1,19 @@ -# CUDA Python 11.8.3 Release notes +CUDA Python 11.8.3 Release notes +================================ Released on October 23, 2023 -## Highlights +Highlights +---------- - Compatability with Cython 3 - New API cudart.getLocalRuntimeVersion() - Modernize build config -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/11.8.4-notes.rst b/cuda_bindings/docs/source/release/11.8.4-notes.rst index 13767998f0..df6253e90e 100644 --- a/cuda_bindings/docs/source/release/11.8.4-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.4-notes.rst @@ -1,36 +1,41 @@ -# CUDA Python 11.8.4 Release notes +CUDA Python 11.8.4 Release notes +================================ Released on October 7, 2024 -## Highlights -- Resolve [Issue #89](https://github.com/NVIDIA/cuda-python/issues/89): Fix getLocalRuntimeVersion searching for wrong libcudart version -- Resolve [Issue #90](https://github.com/NVIDIA/cuda-python/issues/90): Use new layout in preperation for cuda-python becoming a metapackage +Highlights +---------- +- Resolve `Issue #89 `_: Fix getLocalRuntimeVersion searching for wrong libcudart version +- Resolve `Issue #90 `_: Use new layout in preperation for cuda-python becoming a metapackage -## CUDA namespace cleanup with a new module layout +CUDA namespace cleanup with a new module layout +----------------------------------------------- -[Issue #75](https://github.com/NVIDIA/cuda-python/issues/75) explains in detail what the new module layout is, what problem it fixes and how it impacts the users. However for the sake of completeness, this release notes will highlight key points of this change. +`Issue #75 `_ explains in detail what the new module layout is, what problem it fixes and how it impacts the users. However for the sake of completeness, this release notes will highlight key points of this change. -Before this change, `cuda-python` was tightly coupled to CUDA Toolkit releases and all new features would inherit this coupling regardless of their applicability. As we develop new features, this coupling was becoming overly restrictive and motivated a new solution: Convert `cuda-python` into a metapackage where we use `cuda` as a namespace with existing bindings code moved to a `cuda_bindings` subpackage. +Before this change, ``cuda-python`` was tightly coupled to CUDA Toolkit releases and all new features would inherit this coupling regardless of their applicability. As we develop new features, this coupling was becoming overly restrictive and motivated a new solution: Convert ``cuda-python`` into a metapackage where we use ``cuda`` as a namespace with existing bindings code moved to a ``cuda_bindings`` subpackage. This patch release applies the new module layout for the bindings as follows: -- `cuda.cuda` -> `cuda.bindings.driver` -- `cuda.ccuda` -> `cuda.bindings.cydriver` -- `cuda.cudart` -> `cuda.bindings.runtime` -- `cuda.ccudart` -> `cuda.bindings.cyruntime` -- `cuda.nvrtc` -> `cuda.bindings.nvrtc` -- `cuda.cnvrtc` -> `cuda.bindings.cynvrtc` +- ``cuda.cuda`` -> ``cuda.bindings.driver`` +- ``cuda.ccuda`` -> ``cuda.bindings.cydriver`` +- ``cuda.cudart`` -> ``cuda.bindings.runtime`` +- ``cuda.ccudart`` -> ``cuda.bindings.cyruntime`` +- ``cuda.nvrtc`` -> ``cuda.bindings.nvrtc`` +- ``cuda.cnvrtc`` -> ``cuda.bindings.cynvrtc`` Deprecation warnings are turned on as a notice to switch to the new module layout. -```{note} This is non-breaking, backwards compatible change. All old module path will continue work as they "forward" user calls towards the new layout. -``` +.. note:: This is non-breaking, backwards compatible change. All old module path will continue work as they "forward" user calls towards the new layout. -## Limitations +Limitations +----------- -### Know issues -- [Issue #215](https://github.com/NVIDIA/cuda-python/issues/215) +Know issues +^^^^^^^^^^^ +- `Issue #215 `_ -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/11.8.5-notes.rst b/cuda_bindings/docs/source/release/11.8.5-notes.rst index 37498b115f..b2216b97a2 100644 --- a/cuda_bindings/docs/source/release/11.8.5-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.5-notes.rst @@ -1,15 +1,18 @@ -# CUDA Python 11.8.5 Release notes +CUDA Python 11.8.5 Release notes +================================ Released on November 5, 2024. Post 1 rebuild released on November 12, 2024. -## Highlights -- Resolve [Issue #215](https://github.com/NVIDIA/cuda-python/issues/215): module `cuda.ccudart` has no attribute `__pyx_capi__` -- Resolve [Issue #226](https://github.com/NVIDIA/cuda-python/issues/226): top-level Cython source files not packaged +Highlights +---------- +- Resolve `Issue #215 `_: module ``cuda.ccudart`` has no attribute ``__pyx_capi__`` +- Resolve `Issue #226 `_: top-level Cython source files not packaged +Limitations +----------- -## Limitations - -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/11.8.6-notes.rst b/cuda_bindings/docs/source/release/11.8.6-notes.rst index cdbc82e3d2..1254f906bb 100644 --- a/cuda_bindings/docs/source/release/11.8.6-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.6-notes.rst @@ -1,29 +1,31 @@ -# `cuda-bindings` 11.8.6 Release notes +``cuda-bindings`` 11.8.6 Release notes +==================================== Released on January 24, 2025. - -## Highlights +Highlights +---------- - Support Python 3.13 - Add an optional dependency on the CUDA NVRTC wheel - Enable discovery and loading of shared libraries from CUDA wheels -- `cuda-python` is now a meta package, currently depending only on `cuda-bindings` ([see RFC](https://github.com/NVIDIA/cuda-python/issues/105)) - +- ``cuda-python`` is now a meta package, currently depending only on ``cuda-bindings`` (`see RFC `_) -## Wheels support for optional dependencies +Wheels support for optional dependencies +---------------------------------------- Optional dependencies are added for packages: - nvidia-cuda-nvrtc-cu12 -Installing these dependencies with `cuda-python` can be done using: -```{code-block} shell -pip install cuda-python[all] -``` -Same applies to `cuda-bindings`. +Installing these dependencies with ``cuda-python`` can be done using: +.. code-block:: shell + + pip install cuda-python[all] +Same applies to ``cuda-bindings``. -## Discovery and loading of shared library dependencies from wheels +Discovery and loading of shared library dependencies from wheels +---------------------------------------------------------------- -Shared library search paths for wheel builds are now extended to check site-packages. This allows `cuda-python`/`cuda-bindings` to seamlessly use the aforementioned CUDA Toolkit wheels installed in the user's Python environment. +Shared library search paths for wheel builds are now extended to check site-packages. This allows ``cuda-python``/``cuda-bindings`` to seamlessly use the aforementioned CUDA Toolkit wheels installed in the user's Python environment. diff --git a/cuda_bindings/docs/source/release/12.0.0-notes.rst b/cuda_bindings/docs/source/release/12.0.0-notes.rst index 9f2ae25871..69774492aa 100644 --- a/cuda_bindings/docs/source/release/12.0.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.0.0-notes.rst @@ -1,15 +1,19 @@ -# CUDA Python 12.0.0 Release notes +CUDA Python 12.0.0 Release notes +================================ Released on December 8, 2022 -## Highlights +Highlights +---------- - Rebase to CUDA Toolkit 12.0 -- Fix example from [MR28](https://github.com/NVIDIA/cuda-python/pull/28) -- Apply [MR35](https://github.com/NVIDIA/cuda-python/pull/35) +- Fix example from `MR28 `_ +- Apply `MR35 `_ -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/12.1.0-notes.rst b/cuda_bindings/docs/source/release/12.1.0-notes.rst index 94310bb513..84929d548e 100644 --- a/cuda_bindings/docs/source/release/12.1.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.1.0-notes.rst @@ -1,16 +1,20 @@ -# CUDA Python 12.1.0 Release notes +CUDA Python 12.1.0 Release notes +================================ Released on February 28, 2023 -## Highlights +Highlights +---------- - Rebase to CUDA Toolkit 12.1 -- Resolve [Issue #41](https://github.com/NVIDIA/cuda-python/issues/41): Add support for Python 3.11 -- Resolve [Issue #42](https://github.com/NVIDIA/cuda-python/issues/42): Dropping Python 3.7 -- Resolve [Issue #43](https://github.com/NVIDIA/cuda-python/issues/43): Trim Conda package dependencies +- Resolve `Issue #41 `_: Add support for Python 3.11 +- Resolve `Issue #42 `_: Dropping Python 3.7 +- Resolve `Issue #43 `_: Trim Conda package dependencies -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/12.2.0-notes.rst b/cuda_bindings/docs/source/release/12.2.0-notes.rst index 39e37b9a8d..dcd654ba1f 100644 --- a/cuda_bindings/docs/source/release/12.2.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.2.0-notes.rst @@ -1,15 +1,19 @@ -# CUDA Python 12.2.0 Release notes +CUDA Python 12.2.0 Release notes +================================ Released on June 28, 2023 -## Highlights +Highlights +---------- - Rebase to CUDA Toolkit 12.2 -- Resolve [Issue #44](https://github.com/NVIDIA/cuda-python/issues/44): nogil must be at the end of the function signature line -- Resolve [Issue #45](https://github.com/NVIDIA/cuda-python/issues/45): Error with pyparsing when no CUDA is found +- Resolve `Issue #44 `_: nogil must be at the end of the function signature line +- Resolve `Issue #45 `_: Error with pyparsing when no CUDA is found -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/12.2.1-notes.rst b/cuda_bindings/docs/source/release/12.2.1-notes.rst index 3a89af85c2..33fef08e3a 100644 --- a/cuda_bindings/docs/source/release/12.2.1-notes.rst +++ b/cuda_bindings/docs/source/release/12.2.1-notes.rst @@ -1,13 +1,17 @@ -# CUDA Python 12.2.1 Release notes +CUDA Python 12.2.1 Release notes +================================ Released on January 8, 2024 -## Highlights +Highlights +---------- - Compatibility with Cython 3 -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/12.3.0-notes.rst b/cuda_bindings/docs/source/release/12.3.0-notes.rst index 15bcdb9780..9508a8b60b 100644 --- a/cuda_bindings/docs/source/release/12.3.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.3.0-notes.rst @@ -1,17 +1,21 @@ -# CUDA Python 12.3.0 Release notes +CUDA Python 12.3.0 Release notes +================================ Released on October 19, 2023 -## Highlights +Highlights +---------- - Rebase to CUDA Toolkit 12.3 -- Resolve [Issue #16](https://github.com/NVIDIA/cuda-python/issues/16): cuda.cudart.cudaRuntimeGetVersion() hard-codes the runtime version, rather than querying the runtime +- Resolve `Issue #16 `_: cuda.cudart.cudaRuntimeGetVersion() hard-codes the runtime version, rather than querying the runtime - New API cudart.getLocalRuntimeVersion() -- Resolve [Issue #48](https://github.com/NVIDIA/cuda-python/issues/48): Dropping Python 3.8 -- Resolve [Issue #51](https://github.com/NVIDIA/cuda-python/issues/51): Dropping package releases for ppc64 on PYPI and conda-nvidia channel +- Resolve `Issue #48 `_: Dropping Python 3.8 +- Resolve `Issue #51 `_: Dropping package releases for ppc64 on PYPI and conda-nvidia channel -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/12.4.0-notes.rst b/cuda_bindings/docs/source/release/12.4.0-notes.rst index 191ecc644e..f847ca87d1 100644 --- a/cuda_bindings/docs/source/release/12.4.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.4.0-notes.rst @@ -1,14 +1,18 @@ -# CUDA Python 12.4.0 Release notes +CUDA Python 12.4.0 Release notes +================================ Released on March 5, 2024 -## Highlights +Highlights +---------- - Rebase to CUDA Toolkit 12.4 - Add PyPI/Conda support for Python 12 -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/12.5.0-notes.rst b/cuda_bindings/docs/source/release/12.5.0-notes.rst index b0e527a8a7..b74084bfd9 100644 --- a/cuda_bindings/docs/source/release/12.5.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.5.0-notes.rst @@ -1,14 +1,18 @@ -# CUDA Python 12.5.0 Release notes +CUDA Python 12.5.0 Release notes +================================ Released on May 21, 2024 -## Highlights +Highlights +---------- - Rebase to CUDA Toolkit 12.5 -- Resolve [Issue #58](https://github.com/NVIDIA/cuda-python/issues/58): Interop between CUdeviceptr and Runtime +- Resolve `Issue #58 `_: Interop between CUdeviceptr and Runtime -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/12.6.0-notes.rst b/cuda_bindings/docs/source/release/12.6.0-notes.rst index 466e2eec11..a3433bf84c 100644 --- a/cuda_bindings/docs/source/release/12.6.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.6.0-notes.rst @@ -1,16 +1,20 @@ -# CUDA Python 12.6.0 Release notes +CUDA Python 12.6.0 Release notes +================================ Released on August 1, 2024 -## Highlights +Highlights +---------- - Rebase to CUDA Toolkit 12.6 -- Resolve [Issue #32](https://github.com/NVIDIA/cuda-python/issues/32): Add 'pywin32' as Windows requirement -- Resolve [Issue #72](https://github.com/NVIDIA/cuda-python/issues/72): Allow both lists and tuples as parameter -- Resolve [Issue #73](https://github.com/NVIDIA/cuda-python/issues/73): Fix 'cuLibraryLoadData' processing of parameters +- Resolve `Issue #32 `_: Add 'pywin32' as Windows requirement +- Resolve `Issue #72 `_: Allow both lists and tuples as parameter +- Resolve `Issue #73 `_: Fix 'cuLibraryLoadData' processing of parameters -## Limitations +Limitations +----------- -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/12.6.1-notes.rst b/cuda_bindings/docs/source/release/12.6.1-notes.rst index 360047125e..0577314803 100644 --- a/cuda_bindings/docs/source/release/12.6.1-notes.rst +++ b/cuda_bindings/docs/source/release/12.6.1-notes.rst @@ -1,36 +1,41 @@ -# CUDA Python 12.6.1 Release notes +CUDA Python 12.6.1 Release notes +================================ Released on October 7, 2024 -## Highlights -- Resolve [Issue #90](https://github.com/NVIDIA/cuda-python/issues/90): Use new layout in preparation for cuda-python becoming a metapackage -- Resolve [Issue #75](https://github.com/NVIDIA/cuda-python/issues/75): CUDA namespace cleanup +Highlights +---------- +- Resolve `Issue #90 `_: Use new layout in preparation for cuda-python becoming a metapackage +- Resolve `Issue #75 `_: CUDA namespace cleanup -## CUDA namespace cleanup with a new module layout +CUDA namespace cleanup with a new module layout +----------------------------------------------- -[Issue #75](https://github.com/NVIDIA/cuda-python/issues/75) explains in detail what the new module layout is, what problem it fixes and how it impacts the users. However for the sake of completeness, this release notes will highlight key points of this change. +`Issue #75 `_ explains in detail what the new module layout is, what problem it fixes and how it impacts the users. However for the sake of completeness, this release notes will highlight key points of this change. -Before this change, `cuda-python` was tightly coupled to CUDA Toolkit releases and all new features would inherit this coupling regardless of their applicability. As we develop new features, this coupling was becoming overly restrictive and motivated a new solution: Convert `cuda-python` into a metapackage where we use `cuda` as a namespace with existing bindings code moved to a `cuda_bindings` subpackage. +Before this change, ``cuda-python`` was tightly coupled to CUDA Toolkit releases and all new features would inherit this coupling regardless of their applicability. As we develop new features, this coupling was becoming overly restrictive and motivated a new solution: Convert ``cuda-python`` into a metapackage where we use ``cuda`` as a namespace with existing bindings code moved to a ``cuda_bindings`` subpackage. This patch release applies the new module layout for the bindings as follows: -- `cuda.cuda` -> `cuda.bindings.driver` -- `cuda.ccuda` -> `cuda.bindings.cydriver` -- `cuda.cudart` -> `cuda.bindings.runtime` -- `cuda.ccudart` -> `cuda.bindings.cyruntime` -- `cuda.nvrtc` -> `cuda.bindings.nvrtc` -- `cuda.cnvrtc` -> `cuda.bindings.cynvrtc` +- ``cuda.cuda`` -> ``cuda.bindings.driver`` +- ``cuda.ccuda`` -> ``cuda.bindings.cydriver`` +- ``cuda.cudart`` -> ``cuda.bindings.runtime`` +- ``cuda.ccudart`` -> ``cuda.bindings.cyruntime`` +- ``cuda.nvrtc`` -> ``cuda.bindings.nvrtc`` +- ``cuda.cnvrtc`` -> ``cuda.bindings.cynvrtc`` Deprecation warnings are turned on as a notice to switch to the new module layout. -```{note} This is non-breaking, backwards compatible change. All old module path will continue work as they "forward" user calls towards the new layout. -``` +.. note:: This is non-breaking, backwards compatible change. All old module path will continue work as they "forward" user calls towards the new layout. -## Limitations +Limitations +----------- -### Know issues -- [Issue #215](https://github.com/NVIDIA/cuda-python/issues/215) +Know issues +^^^^^^^^^^^ +- `Issue #215 `_ -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/12.6.2-notes.rst b/cuda_bindings/docs/source/release/12.6.2-notes.rst index 938b9f5a61..e25dfb015c 100644 --- a/cuda_bindings/docs/source/release/12.6.2-notes.rst +++ b/cuda_bindings/docs/source/release/12.6.2-notes.rst @@ -1,15 +1,18 @@ -# CUDA Python 12.6.2 Release notes +CUDA Python 12.6.2 Release notes +================================ Released on November 5, 2024. Post 1 rebuild released on November 12, 2024. -## Highlights -- Resolve [Issue #215](https://github.com/NVIDIA/cuda-python/issues/215): module `cuda.ccudart` has no attribute `__pyx_capi__` -- Resolve [Issue #226](https://github.com/NVIDIA/cuda-python/issues/226): top-level Cython source files not packaged +Highlights +---------- +- Resolve `Issue #215 `_: module ``cuda.ccudart`` has no attribute ``__pyx_capi__`` +- Resolve `Issue #226 `_: top-level Cython source files not packaged +Limitations +----------- -## Limitations - -### CUDA Functions Not Supported in this Release +CUDA Functions Not Supported in this Release +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Symbol APIs - cudaGraphExecMemcpyNodeSetParamsFromSymbol diff --git a/cuda_bindings/docs/source/release/12.8.0-notes.rst b/cuda_bindings/docs/source/release/12.8.0-notes.rst index c93f2d9df9..3e7cd52f2a 100644 --- a/cuda_bindings/docs/source/release/12.8.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.8.0-notes.rst @@ -1,36 +1,38 @@ -# `cuda-bindings` 12.8.0 Release notes +``cuda-bindings`` 12.8.0 Release notes +==================================== Released on January 24, 2025. - -## Highlights +Highlights +---------- - Support Python 3.13 - Add bindings for nvJitLink (requires nvJitLink from CUDA 12.3 or above) - Add optional dependencies on CUDA NVRTC and nvJitLink wheels - Enable discovery and loading of shared libraries from CUDA wheels -- `cuda-python` is now a meta package, currently depending only on `cuda-bindings` ([see RFC](https://github.com/NVIDIA/cuda-python/issues/105)) - +- ``cuda-python`` is now a meta package, currently depending only on ``cuda-bindings`` (`see RFC `_) -## Wheels support for optional dependencies +Wheels support for optional dependencies +---------------------------------------- Optional dependencies are added for packages: - nvidia-cuda-nvrtc-cu12 - nvidia-nvjitlink-cu12 -Installing these dependencies with `cuda-python` can be done using: -```{code-block} shell -pip install cuda-python[all] -``` -Same applies to `cuda-bindings`. +Installing these dependencies with ``cuda-python`` can be done using: +.. code-block:: shell + pip install cuda-python[all] -## Discovery and loading of shared library dependencies from wheels +Same applies to ``cuda-bindings``. -Shared library search paths for wheel builds are now extended to check site-packages. This allows `cuda-python`/`cuda-bindings` to seamlessly use the aforementioned CUDA Toolkit wheels installed in the user's Python environment. +Discovery and loading of shared library dependencies from wheels +---------------------------------------------------------------- +Shared library search paths for wheel builds are now extended to check site-packages. This allows ``cuda-python``/``cuda-bindings`` to seamlessly use the aforementioned CUDA Toolkit wheels installed in the user's Python environment. -## Known issues +Known issues +------------ -- Updating from older versions (v12.6.2.post1 and below) via `pip install -U cuda-python` might not work. Please do a clean re-installation by uninstalling `pip uninstall -y cuda-python` followed by installing `pip install cuda-python`. +- Updating from older versions (v12.6.2.post1 and below) via ``pip install -U cuda-python`` might not work. Please do a clean re-installation by uninstalling ``pip uninstall -y cuda-python`` followed by installing ``pip install cuda-python``. diff --git a/cuda_core/docs/source/conduct.rst b/cuda_core/docs/source/conduct.rst index ccc1e4a43a..ae8f7be131 100644 --- a/cuda_core/docs/source/conduct.rst +++ b/cuda_core/docs/source/conduct.rst @@ -1,10 +1,13 @@ -# Code of Conduct +Code of Conduct +=============== -## Overview +Overview +-------- -Define the code of conduct followed and enforced for the `cuda.core` project. +Define the code of conduct followed and enforced for the ``cuda.core`` project. -## Our Pledge +Our Pledge +---------- In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and @@ -13,7 +16,8 @@ size, disability, ethnicity, sex characteristics, gender identity and expression level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. -## Our Standards +Our Standards +------------- Examples of behavior that contributes to creating a positive environment include: @@ -35,7 +39,8 @@ Examples of unacceptable behavior by participants include: * Other conduct which could reasonably be considered inappropriate in a professional setting -## Our Responsibilities +Our Responsibilities +-------------------- Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in @@ -47,7 +52,8 @@ that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. -## Scope +Scope +----- This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of @@ -56,11 +62,12 @@ address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. -## Enforcement +Enforcement +----------- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at -[cuda-python-conduct@nvidia.com](mailto:cuda-python-conduct@nvidia.com) All +`cuda-python-conduct@nvidia.com `_ All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an @@ -71,7 +78,8 @@ Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. -## Attribution +Attribution +----------- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html diff --git a/cuda_core/docs/source/getting-started.rst b/cuda_core/docs/source/getting-started.rst index 6fffa364e0..f23f4c379a 100644 --- a/cuda_core/docs/source/getting-started.rst +++ b/cuda_core/docs/source/getting-started.rst @@ -1,8 +1,10 @@ -# Overview +Overview +======== -## What is `cuda core`? +What is ``cuda core``? +-------------------- -`cuda.core` provides a Pythonic interface to the CUDA runtime and other functionality, +``cuda.core`` provides a Pythonic interface to the CUDA runtime and other functionality, including: - Compiling and launching CUDA kernels @@ -13,98 +15,102 @@ including: - and much more! Rather than providing 1:1 equivalents of the CUDA driver and runtime APIs -(for that, see [`cuda.bindings`][bindings]), `cuda.core` provides high-level constructs such as: +(for that, see [``cuda.bindings``][bindings]), ``cuda.core`` provides high-level constructs such as: -- {class}`Device ` class for GPU device operations and context management. -- {class}`Buffer ` and {class}`MemoryResource ` classes for memory allocation and management. -- {class}`Program ` for JIT compilation of CUDA kernels. -- {class}`GraphBuilder ` for building and executing CUDA graphs. -- {class}`Stream ` and {class}`Event ` for asynchronous execution and timing. +- {class}``Device `` class for GPU device operations and context management. +- {class}``Buffer `` and {class}``MemoryResource `` classes for memory allocation and management. +- {class}``Program `` for JIT compilation of CUDA kernels. +- {class}``GraphBuilder `` for building and executing CUDA graphs. +- {class}``Stream `` and {class}``Event `` for asynchronous execution and timing. -## Example: Compiling and Launching a CUDA kernel +Example: Compiling and Launching a CUDA kernel +---------------------------------------------- -To get a taste for `cuda.core`, let's walk through a simple example that compiles and launches a vector addition kernel. -You can find the complete example in [`vector_add.py`][vector_add_example]. +To get a taste for ``cuda.core``, let's walk through a simple example that compiles and launches a vector addition kernel. +You can find the complete example in [``vector_add.py``][vector_add_example]. First, we define a string containing the CUDA C++ kernel. Note that this is a templated kernel: -```python -# compute c = a + b -code = """ -template -__global__ void vector_add(const T* A, - const T* B, - T* C, - size_t N) { - const unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x; - for (size_t i=tid; i` object -and a corresponding {class}`Stream `. -Don't forget to use {meth}`Device.set_current() `! - -```python -import cupy as cp -from cuda.core.experimental import Device, LaunchConfig, Program, ProgramOptions, launch - -dev = Device() -dev.set_current() -s = dev.create_stream() -``` - -Next, we compile the CUDA C++ kernel from earlier using the {class}`Program ` class. +.. code-block:: python + + compute c = a + b + ================= + code = """ + template + __global__ void vector_add(const T* A, + const T* B, + T* C, + size_t N) { + const unsigned int tid = threadIdx.x + blockIdx.x * blockDim.x; + for (size_t i=tid; i`` object +and a corresponding {class}``Stream ``. +Don't forget to use {meth}``Device.set_current() ``! + +.. code-block:: python + + import cupy as cp + from cuda.core.experimental import Device, LaunchConfig, Program, ProgramOptions, launch + + dev = Device() + dev.set_current() + s = dev.create_stream() + +Next, we compile the CUDA C++ kernel from earlier using the {class}``Program `` class. The result of the compilation is saved as a CUBIN. -Note the use of the `name_expressions` parameter to the {meth}`Program.compile() ` method to specify which kernel template instantiations to compile: +Note the use of the ``name_expressions`` parameter to the {meth}``Program.compile() `` method to specify which kernel template instantiations to compile: -```python -arch = "".join(f"{i}" for i in dev.compute_capability) -program_options = ProgramOptions(std="c++17", arch=f"sm_{arch}") -prog = Program(code, code_type="c++", options=program_options) -mod = prog.compile("cubin", name_expressions=("vector_add",)) -``` +.. code-block:: python + + arch = "".join(f"{i}" for i in dev.compute_capability) + program_options = ProgramOptions(std="c++17", arch=f"sm_{arch}") + prog = Program(code, code_type="c++", options=program_options) + mod = prog.compile("cubin", name_expressions=("vector_add",)) Next, we retrieve the compiled kernel from the CUBIN and prepare the arguments and kernel configuration. We're using [CuPy][cupy] arrays as inputs for this example, but you can use PyTorch tensors too (we show how to do this in one of our [examples][examples]). -```python -ker = mod.get_kernel("vector_add") +.. code-block:: python -# Prepare input/output arrays (using CuPy) -size = 50000 -rng = cp.random.default_rng() -a = rng.random(size, dtype=cp.float32) -b = rng.random(size, dtype=cp.float32) -c = cp.empty_like(a) + ker = mod.get_kernel("vector_add") + + Prepare input/output arrays (using CuPy) + ======================================== + size = 50000 + rng = cp.random.default_rng() + a = rng.random(size, dtype=cp.float32) + b = rng.random(size, dtype=cp.float32) + c = cp.empty_like(a) + + Configure launch parameters + =========================== + block = 256 + grid = (size + block - 1) // block + config = LaunchConfig(grid=grid, block=block) -# Configure launch parameters -block = 256 -grid = (size + block - 1) // block -config = LaunchConfig(grid=grid, block=block) -``` +Finally, we use the {func}``launch `` function to execute our kernel on the specified stream with the given configuration and arguments. Note the use of ``.data.ptr`` to get the pointer to the array data. -Finally, we use the {func}`launch ` function to execute our kernel on the specified stream with the given configuration and arguments. Note the use of `.data.ptr` to get the pointer to the array data. +.. code-block:: python -```python -launch(s, config, ker, a.data.ptr, b.data.ptr, c.data.ptr, cp.uint64(size)) -s.sync() -``` + launch(s, config, ker, a.data.ptr, b.data.ptr, c.data.ptr, cp.uint64(size)) + s.sync() -This example demonstrates one of the core workflows enabled by `cuda.core`: compiling and launching CUDA code. +This example demonstrates one of the core workflows enabled by ``cuda.core``: compiling and launching CUDA code. Note the clean, Pythonic interface, and absence of any direct calls to the CUDA runtime/driver APIs. -## Examples and Recipes - -As we mentioned before, `cuda.core` can do much more than just compile and launch kernels. +Examples and Recipes +-------------------- -The best way to explore and learn the different features `cuda.core` is through -our [`examples`][examples]. Find one that matches your use-case, and modify it to fit your needs! +As we mentioned before, ``cuda.core`` can do much more than just compile and launch kernels. +The best way to explore and learn the different features ``cuda.core`` is through +our [``examples``][examples]. Find one that matches your use-case, and modify it to fit your needs! [bindings]: https://nvidia.github.io/cuda-python/cuda-bindings/latest/ [cai]: https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html diff --git a/cuda_core/docs/source/install.rst b/cuda_core/docs/source/install.rst index 4f66eeff13..7455eda056 100644 --- a/cuda_core/docs/source/install.rst +++ b/cuda_core/docs/source/install.rst @@ -1,8 +1,10 @@ -# Installation +Installation +============ -## Runtime Requirements +Runtime Requirements +-------------------- -`cuda.core` is supported on all platforms that CUDA is supported. Specific +``cuda.core`` is supported on all platforms that CUDA is supported. Specific dependencies are as follows: | | CUDA 11 | CUDA 12 | @@ -10,39 +12,42 @@ dependencies are as follows: | CUDA Toolkit [^1] | 11.2 - 11.8 | 12.x | | Driver | 450.80.02+ (Linux), 452.39+ (Windows) | 525.60.13+ (Linux), 527.41+ (Windows) | -[^1]: Including `cuda-python`. +[^1]: Including ``cuda-python``. -`cuda.core` supports Python 3.9 - 3.13, on Linux (x86-64, arm64) and Windows (x86-64). +``cuda.core`` supports Python 3.9 - 3.13, on Linux (x86-64, arm64) and Windows (x86-64). +Installing from PyPI +-------------------- -## Installing from PyPI +``cuda.core`` works with ``cuda.bindings`` (part of ``cuda-python``) 11 or 12. Test dependencies now use the ```cuda-toolkit``` metapackage for improved dependency resolution. For example with CUDA 12: +.. code-block:: console -`cuda.core` works with `cuda.bindings` (part of `cuda-python`) 11 or 12. Test dependencies now use the ``cuda-toolkit`` metapackage for improved dependency resolution. For example with CUDA 12: -```console -$ pip install cuda-core[cu12] -``` -and likewise use `[cu11]` for CUDA 11, or `[cu13]` for CUDA 13. + $ pip install cuda-core[cu12] -Note that using `cuda.core` with NVRTC installed from PyPI via `pip install` requires -`cuda.bindings` 12.8.0+ or 11.8.6+. Likewise, with nvJitLink it requires 12.8.0+. +and likewise use ``[cu11]`` for CUDA 11, or ``[cu13]`` for CUDA 13. +Note that using ``cuda.core`` with NVRTC installed from PyPI via ``pip install`` requires +``cuda.bindings`` 12.8.0+ or 11.8.6+. Likewise, with nvJitLink it requires 12.8.0+. -## Installing from Conda (conda-forge) +Installing from Conda (conda-forge) +----------------------------------- -Same as above, `cuda.core` can be installed in a CUDA 11 or 12 environment. For example with CUDA 12: -```console -$ conda install -c conda-forge cuda-core cuda-version=12 -``` -and likewise use `cuda-version=11` for CUDA 11. +Same as above, ``cuda.core`` can be installed in a CUDA 11 or 12 environment. For example with CUDA 12: +.. code-block:: console -Note that to use `cuda.core` with nvJitLink installed from conda-forge requires `cuda.bindings` 12.8.0+. + $ conda install -c conda-forge cuda-core cuda-version=12 +and likewise use ``cuda-version=11`` for CUDA 11. -## Installing from Source +Note that to use ``cuda.core`` with nvJitLink installed from conda-forge requires ``cuda.bindings`` 12.8.0+. -```console -$ git clone https://github.com/NVIDIA/cuda-python -$ cd cuda-python/cuda_core -$ pip install . -``` -`cuda-bindings` 11.x or 12.x is a required dependency. +Installing from Source +---------------------- + +.. code-block:: console + + $ git clone https://github.com/NVIDIA/cuda-python + $ cd cuda-python/cuda_core + $ pip install . + +``cuda-bindings`` 11.x or 12.x is a required dependency. diff --git a/cuda_python/docs/source/release.rst b/cuda_python/docs/source/release.rst index c73f21ef41..ce78393bc2 100644 --- a/cuda_python/docs/source/release.rst +++ b/cuda_python/docs/source/release.rst @@ -1,18 +1,20 @@ -# Release Notes +Release Notes +============= -```{toctree} ---- -maxdepth: 3 ---- +.. code-block:: {toctree} + + --- + maxdepth: 3 + --- + + 13.0.1 + 13.0.0 + 12.9.2 + 12.9.1 + 12.9.0 + 12.8.0 + 12.6.2 + 12.6.1 + 11.8.7 + 11.8.6 - 13.0.1 - 13.0.0 - 12.9.2 - 12.9.1 - 12.9.0 - 12.8.0 - 12.6.2 - 12.6.1 - 11.8.7 - 11.8.6 -``` diff --git a/cuda_python/docs/source/release/11.8.6-notes.rst b/cuda_python/docs/source/release/11.8.6-notes.rst index c67b71bcc2..9715c470ce 100644 --- a/cuda_python/docs/source/release/11.8.6-notes.rst +++ b/cuda_python/docs/source/release/11.8.6-notes.rst @@ -1,15 +1,17 @@ -# CUDA Python 11.8.6 Release notes +CUDA Python 11.8.6 Release notes +================================ Released on January 24, 2025. -## Included components +Included components +------------------- -- [`cuda.bindings` 11.8.6](https://nvidia.github.io/cuda-python/cuda-bindings/12.8.0/release/11.8.6-notes.html) +- ``cuda.bindings` 11.8.6 `_ - -## Highlights +Highlights +---------- - Support Python 3.13 - Add optional dependencies on the CUDA NVRTC wheel - Enable discovery and loading of shared libraries from CUDA wheels -- `cuda-python` is now a meta package, currently depending only on `cuda-bindings` ([see RFC](https://github.com/NVIDIA/cuda-python/issues/105)) +- ``cuda-python`` is now a meta package, currently depending only on ``cuda-bindings`` (`see RFC `_) diff --git a/cuda_python/docs/source/release/12.6.1-notes.rst b/cuda_python/docs/source/release/12.6.1-notes.rst index 9a812afc9f..1db2346bdd 100644 --- a/cuda_python/docs/source/release/12.6.1-notes.rst +++ b/cuda_python/docs/source/release/12.6.1-notes.rst @@ -1,12 +1,14 @@ -# CUDA Python Release notes +CUDA Python Release notes +========================= Released on Oct 7, 2024 -## Included components +Included components +------------------- -- [`cuda.bindings` 12.6.1](https://nvidia.github.io/cuda-python/cuda-bindings/12.6.1/release/12.6.1-notes.html) +- ``cuda.bindings` 12.6.1 `_ - -## Hightlights -- Internal layout refactoring to prepare for the `cuda-python` metapackage ([Issue #90](https://github.com/NVIDIA/cuda-python/issues/90), - [Issue #75](https://github.com/NVIDIA/cuda-python/issues/75)) +Hightlights +----------- +- Internal layout refactoring to prepare for the ``cuda-python`` metapackage (`Issue #90 `_, + `Issue #75 `_) diff --git a/cuda_python/docs/source/release/12.6.2-notes.rst b/cuda_python/docs/source/release/12.6.2-notes.rst index 96c90e2adb..eb4a86998e 100644 --- a/cuda_python/docs/source/release/12.6.2-notes.rst +++ b/cuda_python/docs/source/release/12.6.2-notes.rst @@ -1,12 +1,14 @@ -# CUDA Python Release notes +CUDA Python Release notes +========================= Released on November 5, 2024. Post 1 rebuild released on November 12, 2024. -## Included components +Included components +------------------- -- [`cuda.bindings` 12.6.2](https://nvidia.github.io/cuda-python/cuda-bindings/12.6.2/release/12.6.2-notes.html) +- ``cuda.bindings` 12.6.2 `_ - -## Hightlights -- Resolve [Issue #215](https://github.com/NVIDIA/cuda-python/issues/215): module `cuda.ccudart` has no attribute `__pyx_capi__` -- Resolve [Issue #226](https://github.com/NVIDIA/cuda-python/issues/226): top-level Cython source files not packaged +Hightlights +----------- +- Resolve `Issue #215 `_: module ``cuda.ccudart`` has no attribute ``__pyx_capi__`` +- Resolve `Issue #226 `_: top-level Cython source files not packaged diff --git a/cuda_python/docs/source/release/12.8.0-notes.rst b/cuda_python/docs/source/release/12.8.0-notes.rst index a5df49da2d..2c2a28bf8e 100644 --- a/cuda_python/docs/source/release/12.8.0-notes.rst +++ b/cuda_python/docs/source/release/12.8.0-notes.rst @@ -1,21 +1,23 @@ -# CUDA Python 12.8.0 Release notes +CUDA Python 12.8.0 Release notes +================================ Released on January 24, 2025. -## Included components +Included components +------------------- -- [`cuda.bindings` 12.8.0](https://nvidia.github.io/cuda-python/cuda-bindings/12.8.0/release/12.8.0-notes.html) +- ``cuda.bindings` 12.8.0 `_ - -## Highlights +Highlights +---------- - Support Python 3.13 - Add bindings for nvJitLink (requires nvJitLink from CUDA 12.3 or above) - Add optional dependencies on CUDA NVRTC and nvJitLink wheels - Enable discovery and loading of shared libraries from CUDA wheels -- `cuda-python` is now a meta package, currently depending only on `cuda-bindings` ([see RFC](https://github.com/NVIDIA/cuda-python/issues/105)) - +- ``cuda-python`` is now a meta package, currently depending only on ``cuda-bindings`` (`see RFC `_) -## Known issues +Known issues +------------ -- Updating from older versions (v12.6.2.post1 and below) via `pip install -U cuda-python` might not work. Please do a clean re-installation by uninstalling `pip uninstall -y cuda-python` followed by installing `pip install cuda-python`. +- Updating from older versions (v12.6.2.post1 and below) via ``pip install -U cuda-python`` might not work. Please do a clean re-installation by uninstalling ``pip uninstall -y cuda-python`` followed by installing ``pip install cuda-python``. From 547eea21e638e99039427882c78ad95806155fd5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 Aug 2025 03:54:53 +0000 Subject: [PATCH 04/13] Phase 3: Update toctree references from .md to .rst Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- cuda_bindings/docs/source/index.rst | 12 +++---- cuda_bindings/docs/source/install.rst | 2 +- cuda_bindings/docs/source/release.rst | 48 +++++++++++++-------------- cuda_python/docs/source/index.rst | 2 +- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/cuda_bindings/docs/source/index.rst b/cuda_bindings/docs/source/index.rst index 5fc9418514..3501b26a5c 100644 --- a/cuda_bindings/docs/source/index.rst +++ b/cuda_bindings/docs/source/index.rst @@ -9,15 +9,15 @@ :caption: Contents: release - install.md - overview.md - motivation.md - environment_variables.md + install + overview + motivation + environment_variables api tips_and_tricks support - contribute.md - conduct.md + contribute + conduct license diff --git a/cuda_bindings/docs/source/install.rst b/cuda_bindings/docs/source/install.rst index 66886c93b4..d1dade1449 100644 --- a/cuda_bindings/docs/source/install.rst +++ b/cuda_bindings/docs/source/install.rst @@ -73,7 +73,7 @@ Source builds require that the provided CUDA headers are of the same major.minor $ export CUDA_HOME=/usr/local/cuda -See `Environment Variables `_ for a description of other build-time environment variables. +See `Environment Variables `_ for a description of other build-time environment variables. .. note:: diff --git a/cuda_bindings/docs/source/release.rst b/cuda_bindings/docs/source/release.rst index 3f0323ccd6..7082d2b708 100644 --- a/cuda_bindings/docs/source/release.rst +++ b/cuda_bindings/docs/source/release.rst @@ -14,28 +14,28 @@ Release Notes 12.9.2 12.9.1 12.9.0 - 12.8.0 - 12.6.2 - 12.6.1 - 12.6.0 - 12.5.0 - 12.4.0 - 12.3.0 - 12.2.1 - 12.2.0 - 12.1.0 - 12.0.0 + 12.8.0 + 12.6.2 + 12.6.1 + 12.6.0 + 12.5.0 + 12.4.0 + 12.3.0 + 12.2.1 + 12.2.0 + 12.1.0 + 12.0.0 11.8.7 - 11.8.6 - 11.8.5 - 11.8.4 - 11.8.3 - 11.8.2 - 11.8.1 - 11.8.0 - 11.7.1 - 11.7.0 - 11.6.1 - 11.6.0 - 11.5.0 - 11.4.0 + 11.8.6 + 11.8.5 + 11.8.4 + 11.8.3 + 11.8.2 + 11.8.1 + 11.8.0 + 11.7.1 + 11.7.0 + 11.6.1 + 11.6.0 + 11.5.0 + 11.4.0 diff --git a/cuda_python/docs/source/index.rst b/cuda_python/docs/source/index.rst index 49a53b6499..d11cdbd7ed 100644 --- a/cuda_python/docs/source/index.rst +++ b/cuda_python/docs/source/index.rst @@ -32,7 +32,7 @@ be available, please refer to the `cuda.bindings`_ documentation for installatio :maxdepth: 2 :caption: Contents: - release.md + release cuda.core cuda.bindings cuda.pathfinder From f386286bd11a7f579ef6902535e86919943262ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 Aug 2025 03:56:50 +0000 Subject: [PATCH 05/13] Phase 4: Add pre-commit hook to prevent new markdown files in docs/source/ Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- .pre-commit-config.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 239f70dce8..7b80398d6c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,6 +30,14 @@ repos: language: python additional_dependencies: - https://files.pythonhosted.org/packages/cc/20/ff623b09d963f88bfde16306a54e12ee5ea43e9b597108672ff3a408aad6/pathspec-0.12.1-py3-none-any.whl + + - id: no-markdown-in-docs-source + name: Prevent markdown files in docs/source directories + entry: bash -c + args: ['if find . -path "*/docs/source/*.md" -not -path "./docs/README.md" | grep -q .; then echo "ERROR: Markdown files found in docs/source/ directories. Use reStructuredText (.rst) instead."; exit 1; fi'] + language: system + pass_filenames: false + always_run: true - repo: https://github.com/PyCQA/bandit rev: 2d0b675b04c80ae42277e10500db06a0a37bae17 # frozen: 1.8.6 From c1c0474213034e716ed8661c5773aee4e650f235 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 Aug 2025 04:01:38 +0000 Subject: [PATCH 06/13] Phase 5: Fix MyST syntax issues and validate conversion Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- cuda_bindings/docs/source/overview.rst | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/cuda_bindings/docs/source/overview.rst b/cuda_bindings/docs/source/overview.rst index 08ca28f965..772e148bdf 100644 --- a/cuda_bindings/docs/source/overview.rst +++ b/cuda_bindings/docs/source/overview.rst @@ -15,7 +15,7 @@ interoperability among different accelerated libraries. Most importantly, it should be easy for Python developers to use NVIDIA GPUs. ``cuda.bindings`` workflow ------------------------- +--------------------------- Because Python is an interpreted language, you need a way to compile the device code into @@ -286,17 +286,16 @@ was used for application performance. The following command was used to profile the applications: -.. code-block:: {code-block} shell +.. code-block:: shell nsys profile -s none -t cuda --stats=true -.. code-block:: {list-table} Kernel and application performance comparison. - +.. list-table:: Kernel and application performance comparison. :header-rows: 1 - + * - - C++ - - Python + - Python * - Kernel execution - 352µs - 352µs @@ -310,8 +309,7 @@ interactive kernel profiler for CUDA applications. It allows you to have detailed insights into kernel performance. This is useful when you're trying to maximize performance ({numref}``Figure 1``). -.. code-block:: {figure} _static/images/Nsight-Compute-CLI-625x473.png - +.. figure:: _static/images/Nsight-Compute-CLI-625x473.png :name: Figure 1 Screenshot of Nsight Compute CLI output of ``cuda.bindings`` example. @@ -353,8 +351,7 @@ Let's use the following kernel definition as an example: The first step is to create array objects with types corresponding to your kernel arguments. Primitive NumPy types have the following corresponding kernel types: -.. code-block:: {list-table} Correspondence between NumPy types and kernel types. - +.. list-table:: Correspondence between NumPy types and kernel types. :header-rows: 1 * - NumPy type From 2a3b6f3e6c226f0dcb6e1639fb42cbbcae1fe58a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 Aug 2025 04:23:07 +0000 Subject: [PATCH 07/13] Fix SPDX headers: Add missing copyright and license identifiers to converted .rst files Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- cuda_bindings/docs/source/conduct.rst | 3 +++ cuda_bindings/docs/source/contribute.rst | 3 +++ cuda_bindings/docs/source/environment_variables.rst | 3 +++ cuda_bindings/docs/source/install.rst | 3 +++ cuda_bindings/docs/source/motivation.rst | 3 +++ cuda_bindings/docs/source/overview.rst | 3 +++ cuda_bindings/docs/source/release/11.4.0-notes.rst | 3 +++ cuda_bindings/docs/source/release/11.5.0-notes.rst | 3 +++ cuda_bindings/docs/source/release/11.6.0-notes.rst | 3 +++ cuda_bindings/docs/source/release/11.6.1-notes.rst | 3 +++ cuda_bindings/docs/source/release/11.7.0-notes.rst | 3 +++ cuda_bindings/docs/source/release/11.7.1-notes.rst | 3 +++ cuda_bindings/docs/source/release/11.8.0-notes.rst | 3 +++ cuda_bindings/docs/source/release/11.8.1-notes.rst | 3 +++ cuda_bindings/docs/source/release/11.8.2-notes.rst | 3 +++ cuda_bindings/docs/source/release/11.8.3-notes.rst | 3 +++ cuda_bindings/docs/source/release/11.8.4-notes.rst | 3 +++ cuda_bindings/docs/source/release/11.8.5-notes.rst | 3 +++ cuda_bindings/docs/source/release/11.8.6-notes.rst | 3 +++ cuda_bindings/docs/source/release/12.0.0-notes.rst | 3 +++ cuda_bindings/docs/source/release/12.1.0-notes.rst | 3 +++ cuda_bindings/docs/source/release/12.2.0-notes.rst | 3 +++ cuda_bindings/docs/source/release/12.2.1-notes.rst | 3 +++ cuda_bindings/docs/source/release/12.3.0-notes.rst | 3 +++ cuda_bindings/docs/source/release/12.4.0-notes.rst | 3 +++ cuda_bindings/docs/source/release/12.5.0-notes.rst | 3 +++ cuda_bindings/docs/source/release/12.6.0-notes.rst | 3 +++ cuda_bindings/docs/source/release/12.6.1-notes.rst | 3 +++ cuda_bindings/docs/source/release/12.6.2-notes.rst | 3 +++ cuda_bindings/docs/source/release/12.8.0-notes.rst | 3 +++ cuda_core/docs/source/conduct.rst | 3 +++ cuda_core/docs/source/getting-started.rst | 3 +++ cuda_core/docs/source/install.rst | 3 +++ cuda_python/docs/source/release.rst | 3 +++ cuda_python/docs/source/release/11.8.6-notes.rst | 3 +++ cuda_python/docs/source/release/12.6.1-notes.rst | 3 +++ cuda_python/docs/source/release/12.6.2-notes.rst | 3 +++ cuda_python/docs/source/release/12.8.0-notes.rst | 3 +++ 38 files changed, 114 insertions(+) diff --git a/cuda_bindings/docs/source/conduct.rst b/cuda_bindings/docs/source/conduct.rst index 61417b48da..b76cc4968c 100644 --- a/cuda_bindings/docs/source/conduct.rst +++ b/cuda_bindings/docs/source/conduct.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + Code of Conduct =============== diff --git a/cuda_bindings/docs/source/contribute.rst b/cuda_bindings/docs/source/contribute.rst index b50dca4aae..0827690562 100644 --- a/cuda_bindings/docs/source/contribute.rst +++ b/cuda_bindings/docs/source/contribute.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + Contributing ============ diff --git a/cuda_bindings/docs/source/environment_variables.rst b/cuda_bindings/docs/source/environment_variables.rst index 2f07c8d8bf..03f85c14ff 100644 --- a/cuda_bindings/docs/source/environment_variables.rst +++ b/cuda_bindings/docs/source/environment_variables.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + Environment Variables ===================== diff --git a/cuda_bindings/docs/source/install.rst b/cuda_bindings/docs/source/install.rst index d1dade1449..06d328f8c8 100644 --- a/cuda_bindings/docs/source/install.rst +++ b/cuda_bindings/docs/source/install.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + Installation ============ diff --git a/cuda_bindings/docs/source/motivation.rst b/cuda_bindings/docs/source/motivation.rst index 4fe4dcee39..afbd3412d4 100644 --- a/cuda_bindings/docs/source/motivation.rst +++ b/cuda_bindings/docs/source/motivation.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + Motivation ========== What is CUDA Python? diff --git a/cuda_bindings/docs/source/overview.rst b/cuda_bindings/docs/source/overview.rst index 772e148bdf..77f98e7be1 100644 --- a/cuda_bindings/docs/source/overview.rst +++ b/cuda_bindings/docs/source/overview.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + Overview ======== diff --git a/cuda_bindings/docs/source/release/11.4.0-notes.rst b/cuda_bindings/docs/source/release/11.4.0-notes.rst index c6a2972da1..c019aedd95 100644 --- a/cuda_bindings/docs/source/release/11.4.0-notes.rst +++ b/cuda_bindings/docs/source/release/11.4.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 11.4.0 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/11.5.0-notes.rst b/cuda_bindings/docs/source/release/11.5.0-notes.rst index f239df4528..17cb02e0ca 100644 --- a/cuda_bindings/docs/source/release/11.5.0-notes.rst +++ b/cuda_bindings/docs/source/release/11.5.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 11.5.0 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/11.6.0-notes.rst b/cuda_bindings/docs/source/release/11.6.0-notes.rst index 3c7b2cb01f..d7907df847 100644 --- a/cuda_bindings/docs/source/release/11.6.0-notes.rst +++ b/cuda_bindings/docs/source/release/11.6.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 11.6.0 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/11.6.1-notes.rst b/cuda_bindings/docs/source/release/11.6.1-notes.rst index 473982ba9e..f136c94224 100644 --- a/cuda_bindings/docs/source/release/11.6.1-notes.rst +++ b/cuda_bindings/docs/source/release/11.6.1-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 11.6.1 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/11.7.0-notes.rst b/cuda_bindings/docs/source/release/11.7.0-notes.rst index 7ea9c64854..1f850c4283 100644 --- a/cuda_bindings/docs/source/release/11.7.0-notes.rst +++ b/cuda_bindings/docs/source/release/11.7.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 11.7.0 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/11.7.1-notes.rst b/cuda_bindings/docs/source/release/11.7.1-notes.rst index c2c2c0f722..0fbea248e3 100644 --- a/cuda_bindings/docs/source/release/11.7.1-notes.rst +++ b/cuda_bindings/docs/source/release/11.7.1-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 11.7.1 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/11.8.0-notes.rst b/cuda_bindings/docs/source/release/11.8.0-notes.rst index c730af2ca0..e24022142d 100644 --- a/cuda_bindings/docs/source/release/11.8.0-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 11.8.0 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/11.8.1-notes.rst b/cuda_bindings/docs/source/release/11.8.1-notes.rst index 0566b739fe..0df23c9299 100644 --- a/cuda_bindings/docs/source/release/11.8.1-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.1-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 11.8.1 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/11.8.2-notes.rst b/cuda_bindings/docs/source/release/11.8.2-notes.rst index bff6b1b2da..ec9f0324e5 100644 --- a/cuda_bindings/docs/source/release/11.8.2-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.2-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 11.8.2 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/11.8.3-notes.rst b/cuda_bindings/docs/source/release/11.8.3-notes.rst index 2b101bbb47..806f5eb1b3 100644 --- a/cuda_bindings/docs/source/release/11.8.3-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.3-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 11.8.3 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/11.8.4-notes.rst b/cuda_bindings/docs/source/release/11.8.4-notes.rst index df6253e90e..6bafd0b63c 100644 --- a/cuda_bindings/docs/source/release/11.8.4-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.4-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 11.8.4 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/11.8.5-notes.rst b/cuda_bindings/docs/source/release/11.8.5-notes.rst index b2216b97a2..7580d468b4 100644 --- a/cuda_bindings/docs/source/release/11.8.5-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.5-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 11.8.5 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/11.8.6-notes.rst b/cuda_bindings/docs/source/release/11.8.6-notes.rst index 1254f906bb..2c24c54944 100644 --- a/cuda_bindings/docs/source/release/11.8.6-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.6-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + ``cuda-bindings`` 11.8.6 Release notes ==================================== diff --git a/cuda_bindings/docs/source/release/12.0.0-notes.rst b/cuda_bindings/docs/source/release/12.0.0-notes.rst index 69774492aa..b61741a24c 100644 --- a/cuda_bindings/docs/source/release/12.0.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.0.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 12.0.0 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/12.1.0-notes.rst b/cuda_bindings/docs/source/release/12.1.0-notes.rst index 84929d548e..161b4596cb 100644 --- a/cuda_bindings/docs/source/release/12.1.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.1.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 12.1.0 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/12.2.0-notes.rst b/cuda_bindings/docs/source/release/12.2.0-notes.rst index dcd654ba1f..796aaa1e52 100644 --- a/cuda_bindings/docs/source/release/12.2.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.2.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 12.2.0 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/12.2.1-notes.rst b/cuda_bindings/docs/source/release/12.2.1-notes.rst index 33fef08e3a..3ccacdd30e 100644 --- a/cuda_bindings/docs/source/release/12.2.1-notes.rst +++ b/cuda_bindings/docs/source/release/12.2.1-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 12.2.1 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/12.3.0-notes.rst b/cuda_bindings/docs/source/release/12.3.0-notes.rst index 9508a8b60b..0a14aea9e6 100644 --- a/cuda_bindings/docs/source/release/12.3.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.3.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 12.3.0 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/12.4.0-notes.rst b/cuda_bindings/docs/source/release/12.4.0-notes.rst index f847ca87d1..b71a4ce7d4 100644 --- a/cuda_bindings/docs/source/release/12.4.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.4.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 12.4.0 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/12.5.0-notes.rst b/cuda_bindings/docs/source/release/12.5.0-notes.rst index b74084bfd9..0ac6a25ee0 100644 --- a/cuda_bindings/docs/source/release/12.5.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.5.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 12.5.0 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/12.6.0-notes.rst b/cuda_bindings/docs/source/release/12.6.0-notes.rst index a3433bf84c..9cd5bbff59 100644 --- a/cuda_bindings/docs/source/release/12.6.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.6.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 12.6.0 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/12.6.1-notes.rst b/cuda_bindings/docs/source/release/12.6.1-notes.rst index 0577314803..2571633445 100644 --- a/cuda_bindings/docs/source/release/12.6.1-notes.rst +++ b/cuda_bindings/docs/source/release/12.6.1-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 12.6.1 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/12.6.2-notes.rst b/cuda_bindings/docs/source/release/12.6.2-notes.rst index e25dfb015c..4ce87dd8b4 100644 --- a/cuda_bindings/docs/source/release/12.6.2-notes.rst +++ b/cuda_bindings/docs/source/release/12.6.2-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 12.6.2 Release notes ================================ diff --git a/cuda_bindings/docs/source/release/12.8.0-notes.rst b/cuda_bindings/docs/source/release/12.8.0-notes.rst index 3e7cd52f2a..7023c681d5 100644 --- a/cuda_bindings/docs/source/release/12.8.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.8.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + ``cuda-bindings`` 12.8.0 Release notes ==================================== diff --git a/cuda_core/docs/source/conduct.rst b/cuda_core/docs/source/conduct.rst index ae8f7be131..b088b10c8d 100644 --- a/cuda_core/docs/source/conduct.rst +++ b/cuda_core/docs/source/conduct.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: Apache-2.0 + Code of Conduct =============== diff --git a/cuda_core/docs/source/getting-started.rst b/cuda_core/docs/source/getting-started.rst index f23f4c379a..dd0d2da954 100644 --- a/cuda_core/docs/source/getting-started.rst +++ b/cuda_core/docs/source/getting-started.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: Apache-2.0 + Overview ======== diff --git a/cuda_core/docs/source/install.rst b/cuda_core/docs/source/install.rst index 7455eda056..493f62118c 100644 --- a/cuda_core/docs/source/install.rst +++ b/cuda_core/docs/source/install.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: Apache-2.0 + Installation ============ diff --git a/cuda_python/docs/source/release.rst b/cuda_python/docs/source/release.rst index ce78393bc2..53abf042fa 100644 --- a/cuda_python/docs/source/release.rst +++ b/cuda_python/docs/source/release.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + Release Notes ============= diff --git a/cuda_python/docs/source/release/11.8.6-notes.rst b/cuda_python/docs/source/release/11.8.6-notes.rst index 9715c470ce..bf850f5d5f 100644 --- a/cuda_python/docs/source/release/11.8.6-notes.rst +++ b/cuda_python/docs/source/release/11.8.6-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 11.8.6 Release notes ================================ diff --git a/cuda_python/docs/source/release/12.6.1-notes.rst b/cuda_python/docs/source/release/12.6.1-notes.rst index 1db2346bdd..144d8485a2 100644 --- a/cuda_python/docs/source/release/12.6.1-notes.rst +++ b/cuda_python/docs/source/release/12.6.1-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python Release notes ========================= diff --git a/cuda_python/docs/source/release/12.6.2-notes.rst b/cuda_python/docs/source/release/12.6.2-notes.rst index eb4a86998e..f76d213818 100644 --- a/cuda_python/docs/source/release/12.6.2-notes.rst +++ b/cuda_python/docs/source/release/12.6.2-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python Release notes ========================= diff --git a/cuda_python/docs/source/release/12.8.0-notes.rst b/cuda_python/docs/source/release/12.8.0-notes.rst index 2c2a28bf8e..45dca01530 100644 --- a/cuda_python/docs/source/release/12.8.0-notes.rst +++ b/cuda_python/docs/source/release/12.8.0-notes.rst @@ -1,3 +1,6 @@ +.. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +.. SPDX-License-Identifier: LicenseRef-NVIDIA-SOFTWARE-LICENSE + CUDA Python 12.8.0 Release notes ================================ From d576ddab0099e4361ab5f2d23eabf79b79a0cce6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 23 Aug 2025 20:18:38 +0000 Subject: [PATCH 08/13] Fix reStructuredText issues: correct cross-refs, links, and code blocks Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- cuda_bindings/docs/source/conduct.rst | 4 +- cuda_core/docs/source/conduct.rst | 4 +- cuda_core/docs/source/getting-started.rst | 50 ++++++++++------------- 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/cuda_bindings/docs/source/conduct.rst b/cuda_bindings/docs/source/conduct.rst index b76cc4968c..b70d9dd7ce 100644 --- a/cuda_bindings/docs/source/conduct.rst +++ b/cuda_bindings/docs/source/conduct.rst @@ -84,10 +84,8 @@ members of the project's leadership. Attribution ----------- -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +This Code of Conduct is adapted from the `Contributor Covenant `_, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html -[homepage]: https://www.contributor-covenant.org - For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq diff --git a/cuda_core/docs/source/conduct.rst b/cuda_core/docs/source/conduct.rst index b088b10c8d..1c00f5c343 100644 --- a/cuda_core/docs/source/conduct.rst +++ b/cuda_core/docs/source/conduct.rst @@ -84,10 +84,8 @@ members of the project's leadership. Attribution ----------- -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +This Code of Conduct is adapted from the `Contributor Covenant `_, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html -[homepage]: https://www.contributor-covenant.org - For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq diff --git a/cuda_core/docs/source/getting-started.rst b/cuda_core/docs/source/getting-started.rst index dd0d2da954..cd6c209c16 100644 --- a/cuda_core/docs/source/getting-started.rst +++ b/cuda_core/docs/source/getting-started.rst @@ -1,6 +1,8 @@ .. SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. .. SPDX-License-Identifier: Apache-2.0 +.. currentmodule:: cuda.core.experimental + Overview ======== @@ -18,26 +20,25 @@ including: - and much more! Rather than providing 1:1 equivalents of the CUDA driver and runtime APIs -(for that, see [``cuda.bindings``][bindings]), ``cuda.core`` provides high-level constructs such as: +(for that, see `cuda.bindings `_), ``cuda.core`` provides high-level constructs such as: -- {class}``Device `` class for GPU device operations and context management. -- {class}``Buffer `` and {class}``MemoryResource `` classes for memory allocation and management. -- {class}``Program `` for JIT compilation of CUDA kernels. -- {class}``GraphBuilder `` for building and executing CUDA graphs. -- {class}``Stream `` and {class}``Event `` for asynchronous execution and timing. +- :class:`Device` class for GPU device operations and context management. +- :class:`Buffer` and :class:`MemoryResource` classes for memory allocation and management. +- :class:`Program` for JIT compilation of CUDA kernels. +- :class:`GraphBuilder` for building and executing CUDA graphs. +- :class:`Stream` and :class:`Event` for asynchronous execution and timing. Example: Compiling and Launching a CUDA kernel ---------------------------------------------- To get a taste for ``cuda.core``, let's walk through a simple example that compiles and launches a vector addition kernel. -You can find the complete example in [``vector_add.py``][vector_add_example]. +You can find the complete example in `vector_add.py `_. First, we define a string containing the CUDA C++ kernel. Note that this is a templated kernel: .. code-block:: python - compute c = a + b - ================= + # compute c = a + b code = """ template __global__ void vector_add(const T* A, @@ -51,9 +52,9 @@ First, we define a string containing the CUDA C++ kernel. Note that this is a te } """ -Next, we create a {class}``Device `` object -and a corresponding {class}``Stream ``. -Don't forget to use {meth}``Device.set_current() ``! +Next, we create a :class:`Device` object +and a corresponding :class:`Stream`. +Don't forget to use :meth:`Device.set_current`! .. code-block:: python @@ -64,9 +65,9 @@ Don't forget to use {meth}``Device.set_current() `` class. +Next, we compile the CUDA C++ kernel from earlier using the :class:`Program` class. The result of the compilation is saved as a CUBIN. -Note the use of the ``name_expressions`` parameter to the {meth}``Program.compile() `` method to specify which kernel template instantiations to compile: +Note the use of the ``name_expressions`` parameter to the :meth:`Program.compile` method to specify which kernel template instantiations to compile: .. code-block:: python @@ -76,28 +77,26 @@ Note the use of the ``name_expressions`` parameter to the {meth}``Program.compil mod = prog.compile("cubin", name_expressions=("vector_add",)) Next, we retrieve the compiled kernel from the CUBIN and prepare the arguments and kernel configuration. -We're using [CuPy][cupy] arrays as inputs for this example, but you can use PyTorch tensors too -(we show how to do this in one of our [examples][examples]). +We're using `CuPy `_ arrays as inputs for this example, but you can use PyTorch tensors too +(we show how to do this in one of our `examples `_). .. code-block:: python ker = mod.get_kernel("vector_add") - Prepare input/output arrays (using CuPy) - ======================================== + # Prepare input/output arrays (using CuPy) size = 50000 rng = cp.random.default_rng() a = rng.random(size, dtype=cp.float32) b = rng.random(size, dtype=cp.float32) c = cp.empty_like(a) - Configure launch parameters - =========================== + # Configure launch parameters block = 256 grid = (size + block - 1) // block config = LaunchConfig(grid=grid, block=block) -Finally, we use the {func}``launch `` function to execute our kernel on the specified stream with the given configuration and arguments. Note the use of ``.data.ptr`` to get the pointer to the array data. +Finally, we use the :func:`launch` function to execute our kernel on the specified stream with the given configuration and arguments. Note the use of ``.data.ptr`` to get the pointer to the array data. .. code-block:: python @@ -113,11 +112,4 @@ Examples and Recipes As we mentioned before, ``cuda.core`` can do much more than just compile and launch kernels. The best way to explore and learn the different features ``cuda.core`` is through -our [``examples``][examples]. Find one that matches your use-case, and modify it to fit your needs! - -[bindings]: https://nvidia.github.io/cuda-python/cuda-bindings/latest/ -[cai]: https://numba.readthedocs.io/en/stable/cuda/cuda_array_interface.html -[cupy]: https://cupy.dev/ -[dlpack]: https://dmlc.github.io/dlpack/latest/ -[examples]: https://github.com/NVIDIA/cuda-python/tree/main/cuda_core/examples -[vector_add_example]: https://github.com/NVIDIA/cuda-python/tree/main/cuda_core/examples/vector_add.py +our `examples `_. Find one that matches your use-case, and modify it to fit your needs! From ec127a4dc0c41f38f78b3f88639b2c24228549f5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 Aug 2025 02:51:11 +0000 Subject: [PATCH 09/13] Fix code block consistency and Python comments in documentation - Changed shell to console code-block in install.rst for consistency - Converted explanatory text to Python comments in overview.rst code blocks - Ensures all code blocks are copy-paste ready for execution Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- cuda_bindings/docs/source/install.rst | 2 +- cuda_bindings/docs/source/overview.rst | 48 +++++++++----------------- 2 files changed, 17 insertions(+), 33 deletions(-) diff --git a/cuda_bindings/docs/source/install.rst b/cuda_bindings/docs/source/install.rst index 06d328f8c8..9ffd5a7307 100644 --- a/cuda_bindings/docs/source/install.rst +++ b/cuda_bindings/docs/source/install.rst @@ -28,7 +28,7 @@ Installing from PyPI $ pip install -U cuda-python Install all optional dependencies with: -.. code-block:: shell +.. code-block:: console pip install -U cuda-python[all] diff --git a/cuda_bindings/docs/source/overview.rst b/cuda_bindings/docs/source/overview.rst index 77f98e7be1..37e4f38b13 100644 --- a/cuda_bindings/docs/source/overview.rst +++ b/cuda_bindings/docs/source/overview.rst @@ -103,31 +103,25 @@ the program is compiled to target our local compute capability architecture with .. code-block:: python - Initialize CUDA Driver API - ========================== + # Initialize CUDA Driver API checkCudaErrors(driver.cuInit(0)) - Retrieve handle for device 0 - ============================ + # Retrieve handle for device 0 cuDevice = checkCudaErrors(driver.cuDeviceGet(0)) - Derive target architecture for device 0 - ======================================= + # Derive target architecture for device 0 major = checkCudaErrors(driver.cuDeviceGetAttribute(driver.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevice)) minor = checkCudaErrors(driver.cuDeviceGetAttribute(driver.CUdevice_attribute.CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevice)) arch_arg = bytes(f'--gpu-architecture=compute_{major}{minor}', 'ascii') - Create program - ============== + # Create program prog = checkCudaErrors(nvrtc.nvrtcCreateProgram(str.encode(saxpy), b"saxpy.cu", 0, [], [])) - Compile program - =============== + # Compile program opts = [b"--fmad=false", arch_arg] checkCudaErrors(nvrtc.nvrtcCompileProgram(prog, 2, opts)) - Get PTX from compilation - ======================== + # Get PTX from compilation ptxSize = checkCudaErrors(nvrtc.nvrtcGetPTXSize(prog)) ptx = b" " * ptxSize checkCudaErrors(nvrtc.nvrtcGetPTX(prog, ptx)) @@ -139,8 +133,7 @@ following code example, a handle for compute device 0 is passed to .. code-block:: python - Create context - ============== + # Create context context = checkCudaErrors(driver.cuCtxCreate(0, cuDevice)) With a CUDA context created on device 0, load the PTX generated earlier into a @@ -150,11 +143,9 @@ After loading into the module, extract a specific kernel with .. code-block:: python - Load PTX as module data and retrieve function - ============================================= + # Load PTX as module data and retrieve function ptx = np.char.array(ptx) - Note: Incompatible --gpu-architecture would be detected here - ============================================================ + # Note: Incompatible --gpu-architecture would be detected here module = checkCudaErrors(driver.cuModuleLoadData(ptx.ctypes.data)) kernel = checkCudaErrors(driver.cuModuleGetFunction(module, b"saxpy")) @@ -258,8 +249,7 @@ in the designated stream are finished. .. code-block:: python - Assert values are same after running kernel - =========================================== + # Assert values are same after running kernel hZ = a * hX + hY if not np.allclose(hOut, hZ): raise ValueError("Error outside tolerance for host-device vectors") @@ -416,18 +406,15 @@ Note how all three pointers are ``np.intp`` since the pointer values are always Putting it all together: .. code-block:: python - Define a custom type - ==================== + # Define a custom type testStruct = np.dtype([("value", np.int32)], align=True) - Allocate device memory - ====================== + # Allocate device memory pInt = checkCudaErrors(cudart.cudaMalloc(np.dtype(np.int32).itemsize)) pFloat = checkCudaErrors(cudart.cudaMalloc(np.dtype(np.float32).itemsize)) pStruct = checkCudaErrors(cudart.cudaMalloc(testStruct.itemsize)) - Collect all input kernel arguments into a single tuple for further processing - ============================================================================= + # Collect all input kernel arguments into a single tuple for further processing kernelValues = ( np.array(1, dtype=np.uint32), np.array([pInt], dtype=np.intp), @@ -478,19 +465,16 @@ For this example the result becomes: .. code-block:: python - Define a custom type - ==================== + # Define a custom type class testStruct(ctypes.Structure): _fields_ = [("value", ctypes.c_int)] - Allocate device memory - ====================== + # Allocate device memory pInt = checkCudaErrors(cudart.cudaMalloc(ctypes.sizeof(ctypes.c_int))) pFloat = checkCudaErrors(cudart.cudaMalloc(ctypes.sizeof(ctypes.c_float))) pStruct = checkCudaErrors(cudart.cudaMalloc(ctypes.sizeof(testStruct))) - Collect all input kernel arguments into a single tuple for further processing - ============================================================================= + # Collect all input kernel arguments into a single tuple for further processing kernelValues = ( 1, pInt, From 7af7edbaf5910eadbdb05a86d228ce1480dfc559 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 24 Aug 2025 04:29:52 +0000 Subject: [PATCH 10/13] Fix reStructuredText syntax errors: correct toctree directive, broken hyperlinks, and code block formatting Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- cuda_bindings/docs/source/contribute.rst | 3 +-- cuda_bindings/docs/source/overview.rst | 2 +- cuda_python/docs/source/release.rst | 27 +++++++++++------------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/cuda_bindings/docs/source/contribute.rst b/cuda_bindings/docs/source/contribute.rst index 0827690562..20c7f51bc9 100644 --- a/cuda_bindings/docs/source/contribute.rst +++ b/cuda_bindings/docs/source/contribute.rst @@ -7,8 +7,7 @@ Contributing Thank you for your interest in contributing to ``cuda-bindings``! Based on the type of contribution, it will fall into two categories: 1. You want to report a bug, feature request, or documentation issue - - File an `issue `_ - describing what you encountered or what you want to see changed. + - File an `issue `_ describing what you encountered or what you want to see changed. - The NVIDIA team will evaluate the issues and triage them, scheduling them for a release. If you believe the issue needs priority attention comment on the issue to notify the team. diff --git a/cuda_bindings/docs/source/overview.rst b/cuda_bindings/docs/source/overview.rst index 37e4f38b13..5218133742 100644 --- a/cuda_bindings/docs/source/overview.rst +++ b/cuda_bindings/docs/source/overview.rst @@ -326,7 +326,7 @@ NumPy `Array objects `_ can Let's use the following kernel definition as an example: .. code-block:: python - kernel_string = """\ + kernel_string = """ typedef struct { int value; } testStruct; diff --git a/cuda_python/docs/source/release.rst b/cuda_python/docs/source/release.rst index 53abf042fa..9280097b6f 100644 --- a/cuda_python/docs/source/release.rst +++ b/cuda_python/docs/source/release.rst @@ -4,20 +4,17 @@ Release Notes ============= -.. code-block:: {toctree} - - --- - maxdepth: 3 - --- +.. toctree:: + :maxdepth: 3 - 13.0.1 - 13.0.0 - 12.9.2 - 12.9.1 - 12.9.0 - 12.8.0 - 12.6.2 - 12.6.1 - 11.8.7 - 11.8.6 + release/13.0.1-notes + release/13.0.0-notes + release/12.9.2-notes + release/12.9.1-notes + release/12.9.0-notes + release/12.8.0-notes + release/12.6.2-notes + release/12.6.1-notes + release/11.8.7-notes + release/11.8.6-notes From 6e993564b0607038442e386a6a45fb5ca5bccec1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 Aug 2025 00:07:32 +0000 Subject: [PATCH 11/13] Fix release.rst toctree titles and improve pre-commit hook message Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- cuda_python/docs/source/release.rst | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7b80398d6c..01595f8ca8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: - id: no-markdown-in-docs-source name: Prevent markdown files in docs/source directories entry: bash -c - args: ['if find . -path "*/docs/source/*.md" -not -path "./docs/README.md" | grep -q .; then echo "ERROR: Markdown files found in docs/source/ directories. Use reStructuredText (.rst) instead."; exit 1; fi'] + args: ['if find . -path "*/docs/source/*.md" -not -path "./docs/README.md" | grep -q .; then echo "ERROR: Markdown files found in docs/source/ directories. Use reST (.rst) instead."; exit 1; fi'] language: system pass_filenames: false always_run: true diff --git a/cuda_python/docs/source/release.rst b/cuda_python/docs/source/release.rst index 9280097b6f..c97e508c45 100644 --- a/cuda_python/docs/source/release.rst +++ b/cuda_python/docs/source/release.rst @@ -6,15 +6,15 @@ Release Notes .. toctree:: :maxdepth: 3 - - release/13.0.1-notes - release/13.0.0-notes - release/12.9.2-notes - release/12.9.1-notes - release/12.9.0-notes - release/12.8.0-notes - release/12.6.2-notes - release/12.6.1-notes - release/11.8.7-notes - release/11.8.6-notes + + 13.0.1 + 13.0.0 + 12.9.2 + 12.9.1 + 12.9.0 + 12.8.0 + 12.6.2 + 12.6.1 + 11.8.7 + 11.8.6 From 051cb1adc361f43037170213b3ad0bf0f832e7f4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 25 Aug 2025 01:20:53 +0000 Subject: [PATCH 12/13] Fix reStructuredText rendering issues: hyperlinks, code block introductions, and syntax errors Co-authored-by: leofang <5534781+leofang@users.noreply.github.com> --- cuda_bindings/docs/source/install.rst | 10 +++-- cuda_bindings/docs/source/overview.rst | 44 ++++++++++--------- .../docs/source/release/11.8.6-notes.rst | 2 +- .../docs/source/release/12.6.1-notes.rst | 2 +- .../docs/source/release/12.6.2-notes.rst | 2 +- .../docs/source/release/12.8.0-notes.rst | 2 +- 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/cuda_bindings/docs/source/install.rst b/cuda_bindings/docs/source/install.rst index 9ffd5a7307..1bba817365 100644 --- a/cuda_bindings/docs/source/install.rst +++ b/cuda_bindings/docs/source/install.rst @@ -27,7 +27,8 @@ Installing from PyPI $ pip install -U cuda-python -Install all optional dependencies with: +Install all optional dependencies with:: + .. code-block:: console pip install -U cuda-python[all] @@ -52,7 +53,8 @@ Installing from Conda When using conda, the ``cuda-version`` metapackage can be used to control the versions of CUDA Toolkit components that are installed to the conda environment. -For example: +For example:: + .. code-block:: console $ conda install -c conda-forge cuda-python cuda-version=13 @@ -70,7 +72,7 @@ Requirements [^2]: The CUDA Runtime static library (``libcudart_static.a`` on Linux, ``cudart_static.lib`` on Windows) is part of the CUDA Toolkit. If using conda packages, it is contained in the ``cuda-cudart-static`` package. -Source builds require that the provided CUDA headers are of the same major.minor version as the ``cuda.bindings`` you're trying to build. Despite this requirement, note that the minor version compatibility is still maintained. Use the ``CUDA_HOME`` (or ``CUDA_PATH``) environment variable to specify the location of your headers. For example, if your headers are located in ``/usr/local/cuda/include``, then you should set ``CUDA_HOME`` with: +Source builds require that the provided CUDA headers are of the same major.minor version as the ``cuda.bindings`` you're trying to build. Despite this requirement, note that the minor version compatibility is still maintained. Use the ``CUDA_HOME`` (or ``CUDA_PATH``) environment variable to specify the location of your headers. For example, if your headers are located in ``/usr/local/cuda/include``, then you should set ``CUDA_HOME`` with:: .. code-block:: console @@ -85,7 +87,7 @@ See `Environment Variables `_ for a description of ot Editable Install ^^^^^^^^^^^^^^^^ -You can use +You can use:: .. code-block:: console diff --git a/cuda_bindings/docs/source/overview.rst b/cuda_bindings/docs/source/overview.rst index 5218133742..e28c7ae18f 100644 --- a/cuda_bindings/docs/source/overview.rst +++ b/cuda_bindings/docs/source/overview.rst @@ -48,7 +48,7 @@ import this dependency as well. import numpy as np Error checking is a fundamental best practice when working with low-level interfaces. -The following code snippet lets us validate each API call and raise exceptions in case of error. +The following code snippet lets us validate each API call and raise exceptions in case of error.:: .. code-block:: python @@ -99,7 +99,7 @@ Go ahead and compile the kernel into PTX. Remember that this is executed at runt In the following code example, the Driver API is initialized so that the NVIDIA driver and GPU are accessible. Next, the GPU is queried for their compute capability. Finally, -the program is compiled to target our local compute capability architecture with FMAD disabled. +the program is compiled to target our local compute capability architecture with FMAD disabled.:: .. code-block:: python @@ -129,7 +129,7 @@ the program is compiled to target our local compute capability architecture with Before you can use the PTX or do any work on the GPU, you must create a CUDA context. CUDA contexts are analogous to host processes for the device. In the following code example, a handle for compute device 0 is passed to -``cuCtxCreate`` to designate that GPU for context creation. +``cuCtxCreate`` to designate that GPU for context creation.:: .. code-block:: python @@ -139,7 +139,7 @@ following code example, a handle for compute device 0 is passed to With a CUDA context created on device 0, load the PTX generated earlier into a module. A module is analogous to dynamically loaded libraries for the device. After loading into the module, extract a specific kernel with -``cuModuleGetFunction``. It is not uncommon for multiple kernels to reside in PTX. +``cuModuleGetFunction``. It is not uncommon for multiple kernels to reside in PTX.:: .. code-block:: python @@ -152,7 +152,7 @@ After loading into the module, extract a specific kernel with Next, get all your data prepared and transferred to the GPU. For increased application performance, you can input data on the device to eliminate data transfers. For completeness, this example shows how you would transfer data to -and from the device. +and from the device.:: .. code-block:: python @@ -175,7 +175,7 @@ execution. Python doesn't have a natural concept of pointers, yet ``cuMemcpyHtoDAsync`` expects ``void*``. This is where we leverage NumPy's data types to retrieve each host data pointer -by calling ``XX.ctypes.data`` for the associated XX. +by calling ``XX.ctypes.data`` for the associated XX.:: .. code-block:: python @@ -196,7 +196,7 @@ With data prep and resources allocation finished, the kernel is ready to be launched. To pass the location of the data on the device to the kernel execution configuration, you must retrieve the device pointer. In the following code example, we call ``int(XXclass)`` to retrieve the device pointer value for the -associated XXclass as a Python ``int`` and wrap it in a ``np.array`` type. +associated XXclass as a Python ``int`` and wrap it in a ``np.array`` type.:: .. code-block:: python @@ -209,14 +209,14 @@ but this time it's of type ``void**``. What this means is that our argument list be a contiguous array of ``void*`` elements, where each element is the pointer to a kernel argument on either host or device. Since we already prepared each of our arguments into a ``np.array`` type, the construction of our final contiguous array is done by retrieving the ``XX.ctypes.data`` -of each kernel argument. +of each kernel argument.:: .. code-block:: python args = [a, dX, dY, dOut, n] args = np.array([arg.ctypes.data for arg in args], dtype=np.uint64) -Now the kernel can be launched: +Now the kernel can be launched:: .. code-block:: python @@ -245,7 +245,7 @@ data transfers. That ensures that the kernel's compute is performed only after the data has finished transfer, as all API calls and kernel launches within a stream are serialized. After the call to transfer data back to the host is executed, ``cuStreamSynchronize`` is used to halt CPU execution until all operations -in the designated stream are finished. +in the designated stream are finished.:: .. code-block:: python @@ -255,7 +255,7 @@ in the designated stream are finished. raise ValueError("Error outside tolerance for host-device vectors") Perform verification of the data to ensure correctness and finish the code with -memory clean up. +memory clean up.:: .. code-block:: python @@ -277,7 +277,7 @@ kernel performance and `CUDA Events `_ was used for application performance. -The following command was used to profile the applications: +The following command was used to profile the applications:: .. code-block:: shell @@ -323,7 +323,8 @@ Using NumPy NumPy `Array objects `_ can be used to fulfill each of these conditions directly. -Let's use the following kernel definition as an example: +Let's use the following kernel definition as an example:: + .. code-block:: python kernel_string = """ @@ -403,7 +404,8 @@ This example uses the following types: Note how all three pointers are ``np.intp`` since the pointer values are always a representation of an address space. -Putting it all together: +Putting it all together:: + .. code-block:: python # Define a custom type @@ -427,13 +429,13 @@ Putting it all together: The final step is to construct a ``kernelParams`` argument that fulfills all of the launch API conditions. This is made easy because each array object comes with a `ctypes `_ data attribute that returns the underlying ``void*`` pointer value. -By having the final array object contain all pointers, we fulfill the contiguous array requirement: +By having the final array object contain all pointers, we fulfill the contiguous array requirement:: .. code-block:: python kernelParams = np.array([arg.ctypes.data for arg in kernelValues], dtype=np.intp) -The launch API supports `Buffer Protocol `_ objects, therefore we can pass the array object directly. +The launch API supports `Buffer Protocol `_ objects, therefore we can pass the array object directly.:: .. code-block:: python @@ -461,7 +463,7 @@ The ctypes approach treats the ``kernelParams`` argument as a pair of two tuples The ctypes `fundamental data types `_ documentation describes the compatibility between different Python types and C types. Furthermore, `custom data types `_ can be used to support kernels with custom types. -For this example the result becomes: +For this example the result becomes:: .. code-block:: python @@ -500,7 +502,7 @@ Values that are set to ``None`` have a special meaning: In all three cases, the API call will fetch the underlying pointer value and construct a contiguous array with other kernel parameters. -With the setup complete, the kernel can be launched: +With the setup complete, the kernel can be launched:: .. code-block:: python @@ -518,7 +520,7 @@ CUDA objects Certain CUDA kernels use native CUDA types as their parameters such as ``cudaTextureObject_t``. These types require special handling since they're neither a primitive ctype nor a custom user type. Since ``cuda.bindings`` exposes each of them as Python classes, they each implement ``getPtr()`` and ``__int__()``. These two callables used to support the NumPy and ctypes approach. The difference between each call is further described under `Tips and Tricks `_. -For this example, lets use the ``transformKernel`` from `examples/0_Introduction/simpleCubemapTexture_test.py `_: +For this example, lets use the ``transformKernel`` from `examples/0_Introduction/simpleCubemapTexture_test.py `_:: .. code-block:: python @@ -537,7 +539,7 @@ For this example, lets use the ``transformKernel`` from `examples/0_Introduction tex = checkCudaErrors(cudart.cudaCreateTextureObject(texRes, texDescr, None)) ... -For NumPy, we can convert these CUDA types by leveraging the ``__int__()`` call to fetch the address of the underlying ``cudaTextureObject_t`` C object and wrapping it in a NumPy object array of type ``np.intp``: +For NumPy, we can convert these CUDA types by leveraging the ``__int__()`` call to fetch the address of the underlying ``cudaTextureObject_t`` C object and wrapping it in a NumPy object array of type ``np.intp``:: .. code-block:: python @@ -548,7 +550,7 @@ For NumPy, we can convert these CUDA types by leveraging the ``__int__()`` call ) kernelArgs = np.array([arg.ctypes.data for arg in kernelValues], dtype=np.intp) -For ctypes, we leverage the special handling of ``None`` type since each Python class already implements ``getPtr()``: +For ctypes, we leverage the special handling of ``None`` type since each Python class already implements ``getPtr()``:: .. code-block:: python diff --git a/cuda_python/docs/source/release/11.8.6-notes.rst b/cuda_python/docs/source/release/11.8.6-notes.rst index bf850f5d5f..9d726c5b07 100644 --- a/cuda_python/docs/source/release/11.8.6-notes.rst +++ b/cuda_python/docs/source/release/11.8.6-notes.rst @@ -9,7 +9,7 @@ Released on January 24, 2025. Included components ------------------- -- ``cuda.bindings` 11.8.6 `_ +* `cuda.bindings 11.8.6 `_ Highlights ---------- diff --git a/cuda_python/docs/source/release/12.6.1-notes.rst b/cuda_python/docs/source/release/12.6.1-notes.rst index 144d8485a2..a882ffea63 100644 --- a/cuda_python/docs/source/release/12.6.1-notes.rst +++ b/cuda_python/docs/source/release/12.6.1-notes.rst @@ -9,7 +9,7 @@ Released on Oct 7, 2024 Included components ------------------- -- ``cuda.bindings` 12.6.1 `_ +* `cuda.bindings 12.6.1 `_ Hightlights ----------- diff --git a/cuda_python/docs/source/release/12.6.2-notes.rst b/cuda_python/docs/source/release/12.6.2-notes.rst index f76d213818..b091fe1de3 100644 --- a/cuda_python/docs/source/release/12.6.2-notes.rst +++ b/cuda_python/docs/source/release/12.6.2-notes.rst @@ -9,7 +9,7 @@ Released on November 5, 2024. Post 1 rebuild released on November 12, 2024. Included components ------------------- -- ``cuda.bindings` 12.6.2 `_ +* `cuda.bindings 12.6.2 `_ Hightlights ----------- diff --git a/cuda_python/docs/source/release/12.8.0-notes.rst b/cuda_python/docs/source/release/12.8.0-notes.rst index 45dca01530..6634c4ea64 100644 --- a/cuda_python/docs/source/release/12.8.0-notes.rst +++ b/cuda_python/docs/source/release/12.8.0-notes.rst @@ -9,7 +9,7 @@ Released on January 24, 2025. Included components ------------------- -- ``cuda.bindings` 12.8.0 `_ +* `cuda.bindings 12.8.0 `_ Highlights ---------- From a6ec69e62a7faf00be6ba57b0be833661e2539d0 Mon Sep 17 00:00:00 2001 From: Leo Fang Date: Mon, 25 Aug 2025 03:44:19 +0000 Subject: [PATCH 13/13] manual edit to fix rendering errors and prune outdated files --- .../docs/source/_static/logo-dark-mode.png | Bin 50546 -> 0 bytes .../docs/source/_static/logo-light-mode.png | Bin 48816 -> 0 bytes .../docs/source/environment_variables.rst | 10 +++-- cuda_bindings/docs/source/install.rst | 10 ++--- cuda_bindings/docs/source/overview.rst | 42 +++++++++--------- .../docs/source/release/11.8.6-notes.rst | 1 + .../docs/source/release/12.8.0-notes.rst | 1 + cuda_bindings/docs/source/tips_and_tricks.rst | 8 ++-- .../docs/source/_static/logo-dark-mode.png | Bin 50546 -> 0 bytes .../docs/source/_static/logo-light-mode.png | Bin 48816 -> 0 bytes cuda_core/docs/source/getting-started.rst | 7 ++- cuda_core/docs/source/install.rst | 23 +++++++--- cuda_core/docs/source/release.rst | 14 +++--- cuda_core/docs/source/release/0.3.1-notes.rst | 2 +- .../docs/source/_static/logo-dark-mode.png | Bin 50546 -> 0 bytes .../docs/source/_static/logo-light-mode.png | Bin 48816 -> 0 bytes 16 files changed, 66 insertions(+), 52 deletions(-) delete mode 100644 cuda_bindings/docs/source/_static/logo-dark-mode.png delete mode 100644 cuda_bindings/docs/source/_static/logo-light-mode.png delete mode 100644 cuda_core/docs/source/_static/logo-dark-mode.png delete mode 100644 cuda_core/docs/source/_static/logo-light-mode.png delete mode 100644 cuda_python/docs/source/_static/logo-dark-mode.png delete mode 100644 cuda_python/docs/source/_static/logo-light-mode.png diff --git a/cuda_bindings/docs/source/_static/logo-dark-mode.png b/cuda_bindings/docs/source/_static/logo-dark-mode.png deleted file mode 100644 index 6b005a283ba6b7299a08cda1d37ceac8f693f535..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50546 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 diff --git a/cuda_bindings/docs/source/_static/logo-light-mode.png b/cuda_bindings/docs/source/_static/logo-light-mode.png deleted file mode 100644 index c07d6848c98d3084b6df4ef4f21fd5d8fd32b2bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48816 zcmeFZc|4Wt_dk4Xvt&$>F|;d`C_@NahKdYPQsz{YIrF?DLzxwtyiQ@SdtL8qt@j%5ZW-zyXWhcL1tG+G@`R=l zLhMfwqIYFtfbW#fHd?^{m|ahtcSi`n75fi&kCSB>ptrurG5?uoZJ9Q+FL&PLuZ zCQQnouRjR z_=CV71pXlK2Z8@@1hP8VH4q}3J9>T5)U@4=h+F0@_^WLG&j+oUgLcv&bS3>(WDa~= zqMaZ9_Mfi@gP9`#`Sbt$e~@sK?Z3Z8;Cf2?-`}~TN&W9{pxvBG|F7?y{3GUn5cz}6 ze@x?#-TViMKR)#zB+&nFiQ+de@5qRYjC0t`%#FD|MsfB^4F2`5Ba!ic!GR(1^Hm!r zgnYuqY!=NlWsj@QuRpR5P~Y4beOGg?j17$&j`01T-zO^FYQ>?lZ+FQnXRL!vIxjr4 zIjUvbX&+k~v{C={O4D&OsFYF#thRR&)%6fwTe&!oap#16gyXAQlS`0h2g*n%$ z8kgth9ljfoeqF{!*Z=!vt_M7yZ**~1NszfMF0lFi*#G_r{_tcsJl7OABg4sQu{!de zHp1cAPK#}p+W-C?B9kB@$%nY-$|x#UZS6GNR_pPv9~0ZL=(E3T-;Nzm+!gFm&MpG*eAV5J?sW-x8wh_3s7c>p&NJv|L=zk{yvq$^^n8813%*DxI+F5 zkWB1u0_r&4YcsVB4ee3)+h)1)ujim?Lre`8#h9Izc@EFwB2`6i@A@z4GMFNpyN*UI zbEW?~upbSFo3OsbU9hFaMxE}z?r~8J?$N{`e~TyQe|@A^8vxGkxWa?K`NFXHz^FyE?!@@y4Q7N{re;nV9P`a zR+};Tn-sE|htnMAm*biqb)u1Bv!R)?|buq8$fmf9Le

wT^+ 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 diff --git a/cuda_bindings/docs/source/environment_variables.rst b/cuda_bindings/docs/source/environment_variables.rst index 03f85c14ff..c582fe57b3 100644 --- a/cuda_bindings/docs/source/environment_variables.rst +++ b/cuda_bindings/docs/source/environment_variables.rst @@ -4,6 +4,12 @@ Environment Variables ===================== +Runtime Environment Variables +----------------------------- + +- ``CUDA_PYTHON_CUDA_PER_THREAD_DEFAULT_STREAM`` : When set to 1, the default stream is the per-thread default stream. When set to 0, the default stream is the legacy default stream. This defaults to 0, for the legacy default stream. See `Stream Synchronization Behavior `_ for an explanation of the legacy and per-thread default streams. + + Build-Time Environment Variables -------------------------------- @@ -13,7 +19,3 @@ Build-Time Environment Variables - ``CUDA_PYTHON_PARALLEL_LEVEL`` (previously ``PARALLEL_LEVEL``) : int, sets the number of threads used in the compilation of extension modules. Not setting it or setting it to 0 would disable parallel builds. -Runtime Environment Variables ------------------------------ - -- ``CUDA_PYTHON_CUDA_PER_THREAD_DEFAULT_STREAM`` : When set to 1, the default stream is the per-thread default stream. When set to 0, the default stream is the legacy default stream. This defaults to 0, for the legacy default stream. See `Stream Synchronization Behavior `_ for an explanation of the legacy and per-thread default streams. diff --git a/cuda_bindings/docs/source/install.rst b/cuda_bindings/docs/source/install.rst index 1bba817365..b9335b4876 100644 --- a/cuda_bindings/docs/source/install.rst +++ b/cuda_bindings/docs/source/install.rst @@ -27,11 +27,11 @@ Installing from PyPI $ pip install -U cuda-python -Install all optional dependencies with:: +Install all optional dependencies with: .. code-block:: console - pip install -U cuda-python[all] + $ pip install -U cuda-python[all] Where the optional dependencies include: @@ -53,7 +53,7 @@ Installing from Conda When using conda, the ``cuda-version`` metapackage can be used to control the versions of CUDA Toolkit components that are installed to the conda environment. -For example:: +For example: .. code-block:: console @@ -72,7 +72,7 @@ Requirements [^2]: The CUDA Runtime static library (``libcudart_static.a`` on Linux, ``cudart_static.lib`` on Windows) is part of the CUDA Toolkit. If using conda packages, it is contained in the ``cuda-cudart-static`` package. -Source builds require that the provided CUDA headers are of the same major.minor version as the ``cuda.bindings`` you're trying to build. Despite this requirement, note that the minor version compatibility is still maintained. Use the ``CUDA_HOME`` (or ``CUDA_PATH``) environment variable to specify the location of your headers. For example, if your headers are located in ``/usr/local/cuda/include``, then you should set ``CUDA_HOME`` with:: +Source builds require that the provided CUDA headers are of the same major.minor version as the ``cuda.bindings`` you're trying to build. Despite this requirement, note that the minor version compatibility is still maintained. Use the ``CUDA_HOME`` (or ``CUDA_PATH``) environment variable to specify the location of your headers. For example, if your headers are located in ``/usr/local/cuda/include``, then you should set ``CUDA_HOME`` with: .. code-block:: console @@ -87,7 +87,7 @@ See `Environment Variables `_ for a description of ot Editable Install ^^^^^^^^^^^^^^^^ -You can use:: +You can use: .. code-block:: console diff --git a/cuda_bindings/docs/source/overview.rst b/cuda_bindings/docs/source/overview.rst index e28c7ae18f..0f32032528 100644 --- a/cuda_bindings/docs/source/overview.rst +++ b/cuda_bindings/docs/source/overview.rst @@ -48,7 +48,7 @@ import this dependency as well. import numpy as np Error checking is a fundamental best practice when working with low-level interfaces. -The following code snippet lets us validate each API call and raise exceptions in case of error.:: +The following code snippet lets us validate each API call and raise exceptions in case of error: .. code-block:: python @@ -99,7 +99,7 @@ Go ahead and compile the kernel into PTX. Remember that this is executed at runt In the following code example, the Driver API is initialized so that the NVIDIA driver and GPU are accessible. Next, the GPU is queried for their compute capability. Finally, -the program is compiled to target our local compute capability architecture with FMAD disabled.:: +the program is compiled to target our local compute capability architecture with FMAD disabled: .. code-block:: python @@ -129,7 +129,7 @@ the program is compiled to target our local compute capability architecture with Before you can use the PTX or do any work on the GPU, you must create a CUDA context. CUDA contexts are analogous to host processes for the device. In the following code example, a handle for compute device 0 is passed to -``cuCtxCreate`` to designate that GPU for context creation.:: +``cuCtxCreate`` to designate that GPU for context creation: .. code-block:: python @@ -139,7 +139,7 @@ following code example, a handle for compute device 0 is passed to With a CUDA context created on device 0, load the PTX generated earlier into a module. A module is analogous to dynamically loaded libraries for the device. After loading into the module, extract a specific kernel with -``cuModuleGetFunction``. It is not uncommon for multiple kernels to reside in PTX.:: +``cuModuleGetFunction``. It is not uncommon for multiple kernels to reside in PTX: .. code-block:: python @@ -152,7 +152,7 @@ After loading into the module, extract a specific kernel with Next, get all your data prepared and transferred to the GPU. For increased application performance, you can input data on the device to eliminate data transfers. For completeness, this example shows how you would transfer data to -and from the device.:: +and from the device: .. code-block:: python @@ -175,7 +175,7 @@ execution. Python doesn't have a natural concept of pointers, yet ``cuMemcpyHtoDAsync`` expects ``void*``. This is where we leverage NumPy's data types to retrieve each host data pointer -by calling ``XX.ctypes.data`` for the associated XX.:: +by calling ``XX.ctypes.data`` for the associated XX: .. code-block:: python @@ -196,7 +196,7 @@ With data prep and resources allocation finished, the kernel is ready to be launched. To pass the location of the data on the device to the kernel execution configuration, you must retrieve the device pointer. In the following code example, we call ``int(XXclass)`` to retrieve the device pointer value for the -associated XXclass as a Python ``int`` and wrap it in a ``np.array`` type.:: +associated XXclass as a Python ``int`` and wrap it in a ``np.array`` type: .. code-block:: python @@ -209,14 +209,14 @@ but this time it's of type ``void**``. What this means is that our argument list be a contiguous array of ``void*`` elements, where each element is the pointer to a kernel argument on either host or device. Since we already prepared each of our arguments into a ``np.array`` type, the construction of our final contiguous array is done by retrieving the ``XX.ctypes.data`` -of each kernel argument.:: +of each kernel argument: .. code-block:: python args = [a, dX, dY, dOut, n] args = np.array([arg.ctypes.data for arg in args], dtype=np.uint64) -Now the kernel can be launched:: +Now the kernel can be launched: .. code-block:: python @@ -245,7 +245,7 @@ data transfers. That ensures that the kernel's compute is performed only after the data has finished transfer, as all API calls and kernel launches within a stream are serialized. After the call to transfer data back to the host is executed, ``cuStreamSynchronize`` is used to halt CPU execution until all operations -in the designated stream are finished.:: +in the designated stream are finished: .. code-block:: python @@ -255,7 +255,7 @@ in the designated stream are finished.:: raise ValueError("Error outside tolerance for host-device vectors") Perform verification of the data to ensure correctness and finish the code with -memory clean up.:: +memory clean up: .. code-block:: python @@ -277,7 +277,7 @@ kernel performance and `CUDA Events `_ was used for application performance. -The following command was used to profile the applications:: +The following command was used to profile the applications: .. code-block:: shell @@ -323,7 +323,7 @@ Using NumPy NumPy `Array objects `_ can be used to fulfill each of these conditions directly. -Let's use the following kernel definition as an example:: +Let's use the following kernel definition as an example: .. code-block:: python @@ -404,7 +404,7 @@ This example uses the following types: Note how all three pointers are ``np.intp`` since the pointer values are always a representation of an address space. -Putting it all together:: +Putting it all together: .. code-block:: python @@ -429,13 +429,13 @@ Putting it all together:: The final step is to construct a ``kernelParams`` argument that fulfills all of the launch API conditions. This is made easy because each array object comes with a `ctypes `_ data attribute that returns the underlying ``void*`` pointer value. -By having the final array object contain all pointers, we fulfill the contiguous array requirement:: +By having the final array object contain all pointers, we fulfill the contiguous array requirement: .. code-block:: python kernelParams = np.array([arg.ctypes.data for arg in kernelValues], dtype=np.intp) -The launch API supports `Buffer Protocol `_ objects, therefore we can pass the array object directly.:: +The launch API supports `Buffer Protocol `_ objects, therefore we can pass the array object directly: .. code-block:: python @@ -463,7 +463,7 @@ The ctypes approach treats the ``kernelParams`` argument as a pair of two tuples The ctypes `fundamental data types `_ documentation describes the compatibility between different Python types and C types. Furthermore, `custom data types `_ can be used to support kernels with custom types. -For this example the result becomes:: +For this example the result becomes: .. code-block:: python @@ -502,7 +502,7 @@ Values that are set to ``None`` have a special meaning: In all three cases, the API call will fetch the underlying pointer value and construct a contiguous array with other kernel parameters. -With the setup complete, the kernel can be launched:: +With the setup complete, the kernel can be launched: .. code-block:: python @@ -520,7 +520,7 @@ CUDA objects Certain CUDA kernels use native CUDA types as their parameters such as ``cudaTextureObject_t``. These types require special handling since they're neither a primitive ctype nor a custom user type. Since ``cuda.bindings`` exposes each of them as Python classes, they each implement ``getPtr()`` and ``__int__()``. These two callables used to support the NumPy and ctypes approach. The difference between each call is further described under `Tips and Tricks `_. -For this example, lets use the ``transformKernel`` from `examples/0_Introduction/simpleCubemapTexture_test.py `_:: +For this example, lets use the ``transformKernel`` from `examples/0_Introduction/simpleCubemapTexture_test.py `_: .. code-block:: python @@ -539,7 +539,7 @@ For this example, lets use the ``transformKernel`` from `examples/0_Introduction tex = checkCudaErrors(cudart.cudaCreateTextureObject(texRes, texDescr, None)) ... -For NumPy, we can convert these CUDA types by leveraging the ``__int__()`` call to fetch the address of the underlying ``cudaTextureObject_t`` C object and wrapping it in a NumPy object array of type ``np.intp``:: +For NumPy, we can convert these CUDA types by leveraging the ``__int__()`` call to fetch the address of the underlying ``cudaTextureObject_t`` C object and wrapping it in a NumPy object array of type ``np.intp``: .. code-block:: python @@ -550,7 +550,7 @@ For NumPy, we can convert these CUDA types by leveraging the ``__int__()`` call ) kernelArgs = np.array([arg.ctypes.data for arg in kernelValues], dtype=np.intp) -For ctypes, we leverage the special handling of ``None`` type since each Python class already implements ``getPtr()``:: +For ctypes, we leverage the special handling of ``None`` type since each Python class already implements ``getPtr()``: .. code-block:: python diff --git a/cuda_bindings/docs/source/release/11.8.6-notes.rst b/cuda_bindings/docs/source/release/11.8.6-notes.rst index 2c24c54944..9ab6db2d50 100644 --- a/cuda_bindings/docs/source/release/11.8.6-notes.rst +++ b/cuda_bindings/docs/source/release/11.8.6-notes.rst @@ -22,6 +22,7 @@ Optional dependencies are added for packages: - nvidia-cuda-nvrtc-cu12 Installing these dependencies with ``cuda-python`` can be done using: + .. code-block:: shell pip install cuda-python[all] diff --git a/cuda_bindings/docs/source/release/12.8.0-notes.rst b/cuda_bindings/docs/source/release/12.8.0-notes.rst index 7023c681d5..6c9c951779 100644 --- a/cuda_bindings/docs/source/release/12.8.0-notes.rst +++ b/cuda_bindings/docs/source/release/12.8.0-notes.rst @@ -24,6 +24,7 @@ Optional dependencies are added for packages: - nvidia-nvjitlink-cu12 Installing these dependencies with ``cuda-python`` can be done using: + .. code-block:: shell pip install cuda-python[all] diff --git a/cuda_bindings/docs/source/tips_and_tricks.rst b/cuda_bindings/docs/source/tips_and_tricks.rst index 97f585f9b4..cc666ca275 100644 --- a/cuda_bindings/docs/source/tips_and_tricks.rst +++ b/cuda_bindings/docs/source/tips_and_tricks.rst @@ -7,16 +7,16 @@ Tips and Tricks Getting the address of underlying C objects from the low-level bindings ======================================================================= -All CUDA C types are exposed to Python as Python classes. For example, the :class:`~cuda.bindings.driver.CUstream` type is exposed as a class with methods :meth:`~cuda.bindings.driver.CUstream.getPtr()` and :meth:`~cuda.bindings.driver.CUstream.__int__()` implemented. - -There is an important distinction between the ``getPtr()`` method and the behaviour of ``__int__()``. Since a ``CUstream`` is itself just a pointer, calling ``instance_of_CUstream.getPtr()`` returns the pointer *to* the pointer, instead of the value of the ``CUstream`` C object that is the pointer to the underlying stream handle. ``int(instance_of_CUstream)`` returns the value of the ``CUstream`` converted to a Python int and is the actual address of the underlying handle. - .. warning:: Using ``int(cuda_obj)`` to retrieve the underlying address of a CUDA object is deprecated and subject to future removal. Please switch to use :func:`~cuda.bindings.utils.get_cuda_native_handle` instead. +All CUDA C types are exposed to Python as Python classes. For example, the :class:`~cuda.bindings.driver.CUstream` type is exposed as a class with methods :meth:`~cuda.bindings.driver.CUstream.getPtr()` and :meth:`~cuda.bindings.driver.CUstream.__int__()` implemented. + +There is an important distinction between the ``getPtr()`` method and the behaviour of ``__int__()``. Since a ``CUstream`` is itself just a pointer, calling ``instance_of_CUstream.getPtr()`` returns the pointer *to* the pointer, instead of the value of the ``CUstream`` C object that is the pointer to the underlying stream handle. ``int(instance_of_CUstream)`` returns the value of the ``CUstream`` converted to a Python int and is the actual address of the underlying handle. + Lifetime management of the CUDA objects ======================================= diff --git a/cuda_core/docs/source/_static/logo-dark-mode.png b/cuda_core/docs/source/_static/logo-dark-mode.png deleted file mode 100644 index 6b005a283ba6b7299a08cda1d37ceac8f693f535..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50546 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 diff --git a/cuda_core/docs/source/_static/logo-light-mode.png b/cuda_core/docs/source/_static/logo-light-mode.png deleted file mode 100644 index c07d6848c98d3084b6df4ef4f21fd5d8fd32b2bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48816 zcmeFZc|4Wt_dk4Xvt&$>F|;d`C_@NahKdYPQsz{YIrF?DLzxwtyiQ@SdtL8qt@j%5ZW-zyXWhcL1tG+G@`R=l zLhMfwqIYFtfbW#fHd?^{m|ahtcSi`n75fi&kCSB>ptrurG5?uoZJ9Q+FL&PLuZ zCQQnouRjR z_=CV71pXlK2Z8@@1hP8VH4q}3J9>T5)U@4=h+F0@_^WLG&j+oUgLcv&bS3>(WDa~= zqMaZ9_Mfi@gP9`#`Sbt$e~@sK?Z3Z8;Cf2?-`}~TN&W9{pxvBG|F7?y{3GUn5cz}6 ze@x?#-TViMKR)#zB+&nFiQ+de@5qRYjC0t`%#FD|MsfB^4F2`5Ba!ic!GR(1^Hm!r zgnYuqY!=NlWsj@QuRpR5P~Y4beOGg?j17$&j`01T-zO^FYQ>?lZ+FQnXRL!vIxjr4 zIjUvbX&+k~v{C={O4D&OsFYF#thRR&)%6fwTe&!oap#16gyXAQlS`0h2g*n%$ z8kgth9ljfoeqF{!*Z=!vt_M7yZ**~1NszfMF0lFi*#G_r{_tcsJl7OABg4sQu{!de zHp1cAPK#}p+W-C?B9kB@$%nY-$|x#UZS6GNR_pPv9~0ZL=(E3T-;Nzm+!gFm&MpG*eAV5J?sW-x8wh_3s7c>p&NJv|L=zk{yvq$^^n8813%*DxI+F5 zkWB1u0_r&4YcsVB4ee3)+h)1)ujim?Lre`8#h9Izc@EFwB2`6i@A@z4GMFNpyN*UI zbEW?~upbSFo3OsbU9hFaMxE}z?r~8J?$N{`e~TyQe|@A^8vxGkxWa?K`NFXHz^FyE?!@@y4Q7N{re;nV9P`a zR+};Tn-sE|htnMAm*biqb)u1Bv!R)?|buq8$fmf9Le

wT^+ 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 diff --git a/cuda_core/docs/source/getting-started.rst b/cuda_core/docs/source/getting-started.rst index cd6c209c16..502ea66375 100644 --- a/cuda_core/docs/source/getting-started.rst +++ b/cuda_core/docs/source/getting-started.rst @@ -6,8 +6,8 @@ Overview ======== -What is ``cuda core``? --------------------- +What is ``cuda.core``? +---------------------- ``cuda.core`` provides a Pythonic interface to the CUDA runtime and other functionality, including: @@ -71,8 +71,7 @@ Note the use of the ``name_expressions`` parameter to the :meth:`Program.compile .. code-block:: python - arch = "".join(f"{i}" for i in dev.compute_capability) - program_options = ProgramOptions(std="c++17", arch=f"sm_{arch}") + program_options = ProgramOptions(std="c++17", arch=f"sm_{dev.arch}") prog = Program(code, code_type="c++", options=program_options) mod = prog.compile("cubin", name_expressions=("vector_add",)) diff --git a/cuda_core/docs/source/install.rst b/cuda_core/docs/source/install.rst index 493f62118c..8bc1faa0e1 100644 --- a/cuda_core/docs/source/install.rst +++ b/cuda_core/docs/source/install.rst @@ -10,19 +10,29 @@ Runtime Requirements ``cuda.core`` is supported on all platforms that CUDA is supported. Specific dependencies are as follows: -| | CUDA 11 | CUDA 12 | -|------------------ | ------------ | ----------- | -| CUDA Toolkit [^1] | 11.2 - 11.8 | 12.x | -| Driver | 450.80.02+ (Linux), 452.39+ (Windows) | 525.60.13+ (Linux), 527.41+ (Windows) | +.. list-table:: + :header-rows: 1 + + * - + - CUDA 11 + - CUDA 12 + * - CUDA Toolkit\ [#f1]_ + - 11.2 - 11.8 + - 12.x + * - Driver + - 450.80.02+ (Linux), 452.39+ (Windows) + - 525.60.13+ (Linux), 527.41+ (Windows) + +.. [#f1] Including ``cuda-python``. -[^1]: Including ``cuda-python``. ``cuda.core`` supports Python 3.9 - 3.13, on Linux (x86-64, arm64) and Windows (x86-64). Installing from PyPI -------------------- -``cuda.core`` works with ``cuda.bindings`` (part of ``cuda-python``) 11 or 12. Test dependencies now use the ```cuda-toolkit``` metapackage for improved dependency resolution. For example with CUDA 12: +``cuda.core`` works with ``cuda.bindings`` (part of ``cuda-python``) 11 or 12. Test dependencies now use the ``cuda-toolkit`` metapackage for improved dependency resolution. For example with CUDA 12: + .. code-block:: console $ pip install cuda-core[cu12] @@ -36,6 +46,7 @@ Installing from Conda (conda-forge) ----------------------------------- Same as above, ``cuda.core`` can be installed in a CUDA 11 or 12 environment. For example with CUDA 12: + .. code-block:: console $ conda install -c conda-forge cuda-core cuda-version=12 diff --git a/cuda_core/docs/source/release.rst b/cuda_core/docs/source/release.rst index 954d296e29..dc28b31220 100644 --- a/cuda_core/docs/source/release.rst +++ b/cuda_core/docs/source/release.rst @@ -7,10 +7,10 @@ Release Notes .. toctree:: :maxdepth: 3 - release/0.X.Y-notes - release/0.3.2-notes - release/0.3.1-notes - release/0.3.0-notes - release/0.2.0-notes - release/0.1.1-notes - release/0.1.0-notes + 0.X.Y + 0.3.2 + 0.3.1 + 0.3.0 + 0.2.0 + 0.1.1 + 0.1.0 diff --git a/cuda_core/docs/source/release/0.3.1-notes.rst b/cuda_core/docs/source/release/0.3.1-notes.rst index 33ea3b48e4..82138763db 100644 --- a/cuda_core/docs/source/release/0.3.1-notes.rst +++ b/cuda_core/docs/source/release/0.3.1-notes.rst @@ -12,7 +12,7 @@ Released on July 2, 2025 Highlights ---------- -- Add a :doc:`Getting Started ` page. +- Add a :doc:`Getting Started <../getting-started>` page. - :class:`Stream` and :class:`Event` creation and some operations are made faster. diff --git a/cuda_python/docs/source/_static/logo-dark-mode.png b/cuda_python/docs/source/_static/logo-dark-mode.png deleted file mode 100644 index 6b005a283ba6b7299a08cda1d37ceac8f693f535..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50546 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 diff --git a/cuda_python/docs/source/_static/logo-light-mode.png b/cuda_python/docs/source/_static/logo-light-mode.png deleted file mode 100644 index c07d6848c98d3084b6df4ef4f21fd5d8fd32b2bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48816 zcmeFZc|4Wt_dk4Xvt&$>F|;d`C_@NahKdYPQsz{YIrF?DLzxwtyiQ@SdtL8qt@j%5ZW-zyXWhcL1tG+G@`R=l zLhMfwqIYFtfbW#fHd?^{m|ahtcSi`n75fi&kCSB>ptrurG5?uoZJ9Q+FL&PLuZ zCQQnouRjR z_=CV71pXlK2Z8@@1hP8VH4q}3J9>T5)U@4=h+F0@_^WLG&j+oUgLcv&bS3>(WDa~= zqMaZ9_Mfi@gP9`#`Sbt$e~@sK?Z3Z8;Cf2?-`}~TN&W9{pxvBG|F7?y{3GUn5cz}6 ze@x?#-TViMKR)#zB+&nFiQ+de@5qRYjC0t`%#FD|MsfB^4F2`5Ba!ic!GR(1^Hm!r zgnYuqY!=NlWsj@QuRpR5P~Y4beOGg?j17$&j`01T-zO^FYQ>?lZ+FQnXRL!vIxjr4 zIjUvbX&+k~v{C={O4D&OsFYF#thRR&)%6fwTe&!oap#16gyXAQlS`0h2g*n%$ z8kgth9ljfoeqF{!*Z=!vt_M7yZ**~1NszfMF0lFi*#G_r{_tcsJl7OABg4sQu{!de zHp1cAPK#}p+W-C?B9kB@$%nY-$|x#UZS6GNR_pPv9~0ZL=(E3T-;Nzm+!gFm&MpG*eAV5J?sW-x8wh_3s7c>p&NJv|L=zk{yvq$^^n8813%*DxI+F5 zkWB1u0_r&4YcsVB4ee3)+h)1)ujim?Lre`8#h9Izc@EFwB2`6i@A@z4GMFNpyN*UI zbEW?~upbSFo3OsbU9hFaMxE}z?r~8J?$N{`e~TyQe|@A^8vxGkxWa?K`NFXHz^FyE?!@@y4Q7N{re;nV9P`a zR+};Tn-sE|htnMAm*biqb)u1Bv!R)?|buq8$fmf9Le

wT^+ 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