Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 72 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -195,19 +195,87 @@ option(DEV_MODE "Build testing dev_test.cpp with cytnx" OFF)
# #####################################################################
project(CYTNX VERSION ${CYTNX_VERSION} LANGUAGES CXX C)

# #####################################################################
# ## HOST COMPILER REQUIREMENT
# #####################################################################
# Cytnx targets C++20 (CMAKE_CXX_STANDARD 20). The GCC floor is 13: earlier
# releases lack the C++20 library/feature support the codebase relies on. Only
# enforce this for GNU; Clang/AppleClang are validated separately on macOS.
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13)
message(FATAL_ERROR
"GCC >= 13 is required (C++20 support), but found GCC "
"${CMAKE_CXX_COMPILER_VERSION} at ${CMAKE_CXX_COMPILER}.\n"
"Install GCC 13+ and point CMake at it, e.g. "
"-DCMAKE_CXX_COMPILER=g++-13 (and -DCMAKE_C_COMPILER=gcc-13), "
"or set the CXX/CC environment variables.")
endif()
Comment on lines +204 to +211

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need this guard? We have had CMAKE_CXX_STANDARD_REQUIRED ON for guarding the compiler version.


set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if(USE_CUDA)
# Resolve the CUDA toolkit up front (before enable_language) so we can both
# enforce the version requirement early and choose an architecture list the
# toolkit can actually compile. find_package(CUDAToolkit) does not require the
# CUDA language to be enabled; the later find_package in CytnxBKNDCMakeLists
# re-uses this cached result.
find_package(CUDAToolkit QUIET)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Honor pinned CUDA compilers when finding the toolkit

This probes FindCUDAToolkit before the CUDA language is enabled, but cmake --help-module FindCUDAToolkit says the compiler directory is searched only after CUDA has been enabled; at this point it searches CUDAToolkit_ROOT/CUDA_PATH/PATH/defaults instead of CMAKE_CUDA_COMPILER or CUDACXX. A user with nvcc only under a non-default path who follows the new error text (-DCMAKE_CUDA_COMPILER=/opt/cuda-13/bin/nvcc or CUDACXX=...) will still hit this fatal path, and a user with another CUDA on PATH can have the version check/link libraries come from the wrong toolkit before enable_language(CUDA) honors the pinned compiler.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed — find_package(CUDAToolkit) runs before enable_language(CUDA), so it consults CUDAToolkit_ROOT/CUDA_PATH/PATH rather than CMAKE_CUDA_COMPILER/CUDACXX, and a pinned-but-not-on-PATH nvcc (or a stray CUDA on PATH) can drive the early check from the wrong toolkit. The intended fix is to derive CUDAToolkit_ROOT from a pinned CMAKE_CUDA_COMPILER/CUDACXX before the probe. Deferring this to a follow-up PR since it needs a Linux box with multiple CUDA toolkits to verify; tracked there.

Posted by Claude Code on behalf of @pcchen

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we defer this issue to following PR, this PR fixed the problem in the wrong way. In this change, setting CMAKE_CUDA_COMPILER to a path other than the path found in find_package(CUDAToolkit) may cause the version of nvcc and cuda toolkit different, and the build may fail. Suggest pattern is not to find cuda toolkit before enabling cuda

# Record user intent before CUDA is enabled.
set(_USER_SET_CUDA_ARCHITECTURES FALSE)
if(DEFINED CMAKE_CUDA_ARCHITECTURES)
  set(_USER_SET_CUDA_ARCHITECTURES TRUE)
endif()

enable_language(CUDA)
find_package(CUDAToolkit REQUIRED)

if(NOT _USER_SET_CUDA_ARCHITECTURES)
  if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 13.0)
    set(CMAKE_CUDA_ARCHITECTURES
        75-real 80-real 86-real 89-real 90-real 90-virtual)
  else()
    set(CMAKE_CUDA_ARCHITECTURES
        70-real 75-real 80-real 86-real 89-real 90-real 90-virtual)
  endif()
endif()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CMAKE_CUDA_ARCHITECTURES all-major cannot be used here because that will include 50 and 60 for cuda 12.x. 50 and 60 are not supported by our required cuTENSOR and cuQuantum.


# enable_language(CUDA) finds the compiler ONLY via CMAKE_CUDA_COMPILER, the
# CUDACXX environment variable, or nvcc on PATH -- it does not reuse the
# toolkit located above. Catch the "no usable nvcc" case here with an
# actionable message instead of CMake's terse "No CMAKE_CUDA_COMPILER could be
# found". Skip the check when the user pinned a compiler explicitly.
if(NOT CUDAToolkit_FOUND OR (NOT CUDAToolkit_NVCC_EXECUTABLE
AND NOT CMAKE_CUDA_COMPILER AND NOT DEFINED ENV{CUDACXX}))
message(FATAL_ERROR
"USE_CUDA is ON but no usable CUDA compiler (nvcc) could be found.\n"
"enable_language(CUDA) searches only CMAKE_CUDA_COMPILER, the CUDACXX "
"environment variable, and nvcc on PATH; it does not reuse the libraries "
"found by find_package(CUDAToolkit), so installed CUDA runtime/dev "
"packages alone are not enough.\n"
"Do one of the following, then delete the build directory (a stale "
"CMakeCache.txt caches the failed detection) and re-configure:\n"
" * pass -DCMAKE_CUDA_COMPILER=/path/to/nvcc "
"(e.g. /usr/local/cuda-12/bin/nvcc), or\n"
" * export CUDACXX=/path/to/nvcc, or\n"
" * put the CUDA bin directory first on PATH "
"(export PATH=/usr/local/cuda-12/bin:$PATH).")
endif()

# CUDA requirement: >= 12.0, with 12.x and 13.x explicitly supported. The
# 12.0 floor comes from device-side C++20 (nvcc gained -std=c++20 in CUDA
# 12.0; 11.x tops out at C++17), matching CMAKE_CUDA_STANDARD 20. Fail early
# with an actionable message instead of a cryptic later C++20/arch error.
if(CUDAToolkit_VERSION VERSION_LESS 12.0)
message(FATAL_ERROR
"CUDA Toolkit >= 12.0 is required (device-side C++20 support), but found "
"${CUDAToolkit_VERSION} at ${CUDAToolkit_BIN_DIR}.\n"
"Install CUDA 12 or 13 and select it with "
"-DCMAKE_CUDA_COMPILER=/path/to/cuda-12+/bin/nvcc (or set CUDACXX / put "
"the desired nvcc first on PATH).")
elseif(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 14.0)
message(WARNING
"CUDA Toolkit ${CUDAToolkit_VERSION} is newer than the versions Cytnx is "
"tested against (12.x and 13.x). The build will proceed; please report "
"any issues.")
endif()
Comment on lines +255 to +260

@IvanaGyro IvanaGyro Jun 29, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For packages supporting cuda, there are many configurations. It's hard to test on all configurations as the discussion in #583 and #584. Some packages don't explicitly set the range of supported system dependencies. They just list the tested configuration in the document. This reduces the responsibility of package, makes maintenance easier and let the users who want to build from source take the responsibility to solve the configuration problem.

We may support and build with CUDA 14 in the future, so guarding with the maximum version is not needed.

message(STATUS " CUDA Toolkit: ${CUDAToolkit_VERSION} (supported: 12.x, 13.x)")

# Default to a portable fat binary unless the caller picked architectures via
# -D, the cache, a preset's cacheVariables, or the CUDAARCHS environment
# variable. This must run before enable_language(CUDA): afterwards
# CMAKE_CUDA_ARCHITECTURES is never empty (CMake fills in its own default), so
# the "not specified" case can no longer be detected. enable_language(CUDA)
# reads CUDAARCHS on its own, so we only avoid shadowing it here, not copy it.
# The default embeds SASS for each supported real architecture (Volta sm_70 is
# the floor required by cuTENSOR/cuQuantum, up through Hopper sm_90) plus PTX
# of the newest (90-virtual) so the driver can JIT for newer/unknown GPUs.
# The default embeds SASS for each supported real architecture plus PTX of the
# newest (90-virtual) so the driver can JIT for newer/unknown GPUs. CUDA 13.0
# removed offline support for compute capability < 7.5, so Volta sm_70 is only
# included for CUDA 12.x.
if(NOT CMAKE_CUDA_ARCHITECTURES AND NOT DEFINED ENV{CUDAARCHS})
set(CMAKE_CUDA_ARCHITECTURES 70-real 75-real 80-real 86-real 89-real 90-real 90-virtual)
if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL 13.0)
set(CMAKE_CUDA_ARCHITECTURES 75-real 80-real 86-real 89-real 90-real 90-virtual)
else()
set(CMAKE_CUDA_ARCHITECTURES 70-real 75-real 80-real 86-real 89-real 90-real 90-virtual)
endif()
endif()
enable_language(CUDA)
# Disable generation of "--option-file" flag in compile_commands.json.
Expand Down
11 changes: 9 additions & 2 deletions CytnxBKNDCMakeLists.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ if(USE_CUDA)
set(CYTNX_VARIANT_INFO "${CYTNX_VARIANT_INFO} UNI_CUDA")

enable_language(CUDA)
# CUDA toolkit version is validated at the top level (CMakeLists.txt, before
# enable_language) so the architecture defaults can adapt to it; this call
# re-uses that cached result.
find_package(CUDAToolkit REQUIRED)
if(NOT DEFINED CMAKE_CUDA_STANDARD)
set(CMAKE_CUDA_STANDARD 20)
Expand Down Expand Up @@ -231,8 +234,12 @@ if(USE_CUDA)


message( STATUS " Build CUDA Support: YES")
message( STATUS " - CUDA Version: ${CUDA_VERSION_STRING}")
message( STATUS " - CUDA Toolkit Root: ${CUDA_TOOLKIT_ROOT_DIR}")
# Use the modern CUDAToolkit_* variables: the legacy FindCUDA variables
# (CUDA_VERSION_STRING / CUDA_TOOLKIT_ROOT_DIR) are not set by
# find_package(CUDAToolkit) and would print blank.
message( STATUS " - CUDA Version: ${CUDAToolkit_VERSION}")
message( STATUS " - CUDA Toolkit bin dir: ${CUDAToolkit_BIN_DIR}")
message( STATUS " - CUDA compiler (nvcc): ${CMAKE_CUDA_COMPILER}")
message( STATUS " - Internal macro switch: GPU/CUDA")
FILE(APPEND "${CMAKE_BINARY_DIR}/cxxflags.tmp" "-DUNI_GPU\n" "")
message( STATUS " - Cudatoolkit include dir: ${CUDAToolkit_INCLUDE_DIRS}")
Expand Down
18 changes: 15 additions & 3 deletions cmake/Modules/FindCUQUANTUM.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ find_library(
)
message(STATUS "CUQUANTUM_TENSORNET_LIB: ${CUQUANTUM_TENSORNET_LIB}")
message(STATUS "CUQUANTUM_CUSTATEVEC_LIB: ${CUQUANTUM_CUSTATEVEC_LIB}")
set(CUQUANTUM_LIBRARIES "${CUQUANTUM_TENSORNET_LIB};${CUQUANTUM_CUSTATEVEC_LIB}")
message(STATUS "ok")
set(CUQUANTUM_FOUND TRUE)
set(CUQUANTUM_LIBRARIES "")
if(CUQUANTUM_TENSORNET_LIB)
list(APPEND CUQUANTUM_LIBRARIES "${CUQUANTUM_TENSORNET_LIB}")
endif()
if(CUQUANTUM_CUSTATEVEC_LIB)
list(APPEND CUQUANTUM_LIBRARIES "${CUQUANTUM_CUSTATEVEC_LIB}")
endif()

# CUQUANTUM_FOUND must reflect whether the libraries were actually located;
# both cuTensorNet and cuStateVec are linked, so both are required. Setting it
# unconditionally would let a NOTFOUND (e.g. an "...-NOTFOUND" entry on the link
# line) silently pass the caller's REQUIRED check.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CUQUANTUM
REQUIRED_VARS CUQUANTUM_TENSORNET_LIB CUQUANTUM_CUSTATEVEC_LIB CUQUANTUM_INCLUDE_DIRS)
80 changes: 65 additions & 15 deletions cmake/Modules/FindCUTENSOR.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,54 @@ else()
endif()

message(STATUS " cudaver: ${CUDAToolkit_VERSION_MAJOR}" )
if(EXISTS "${CUTENSOR_ROOT}/lib")
set(CUTNLIB_DIR "lib/")
endif()
if((${CUDAToolkit_VERSION_MAJOR} LESS_EQUAL 10))
set(CUTNLIB_DIR "${CUTNLIB_DIR}10.2")
elseif((${CUDAToolkit_VERSION_MAJOR} GREATER_EQUAL 11) AND (${CUDAToolkit_VERSION_MAJOR} LESS 12) AND (${CUDAToolkit_VERSION_MINOR} LESS_EQUAL 0))
set(CUTNLIB_DIR "${CUTNLIB_DIR}11.0")
elseif((${CUDAToolkit_VERSION_MAJOR} GREATER_EQUAL 11) AND (${CUDAToolkit_VERSION_MAJOR} LESS 12) AND (${CUDAToolkit_VERSION_MINOR} GREATER_EQUAL 1))
set(CUTNLIB_DIR "${CUTNLIB_DIR}11")
elseif((${CUDAToolkit_VERSION_MAJOR} GREATER_EQUAL 12))
set(CUTNLIB_DIR "${CUTNLIB_DIR}12")
# Cytnx requires CUDA >= 12 (enforced in CMakeLists.txt) and cuTENSOR >= 2.0.
# Search both library layouts: cuTENSOR 2.x tarballs place the libraries
# directly under lib/, while 1.x tarballs and apt use a per-CUDA subdir
# (lib/<cuda-major>, e.g. lib/12, lib/13). Listing both as find_library
# PATH_SUFFIXES lets the supported 2.x flat layout and the legacy versioned
# layout resolve for both CUDA 12 and 13. The older lib/10.2 and lib/11
# branches were removed as dead code; apt multiarch paths remain (issue #946).
if(CUDAToolkit_VERSION_MAJOR GREATER_EQUAL 12)
set(CUTNLIB_DIR lib lib/${CUDAToolkit_VERSION_MAJOR})
else()
message(FATAL_ERROR
"cuTENSOR support requires CUDA >= 12, but CUDAToolkit_VERSION_MAJOR is "
"'${CUDAToolkit_VERSION_MAJOR}'.")
endif()

set(CUTENSOR_LIBRARY_DIRS ${CUTENSOR_ROOT}/${CUTNLIB_DIR})
set(CUTENSOR_INCLUDE_DIRS ${CUTENSOR_ROOT}/include)

# Require cuTENSOR >= 2.0. The version macros (CUTENSOR_MAJOR/MINOR/PATCH) live
# in cutensor.h (older releases) or cutensor/types.h (newer ones); read whichever
# defines them and fail early on the 1.x API, which Cytnx no longer supports.
set(_cutensor_version_header "")
foreach(_hdr "${CUTENSOR_INCLUDE_DIRS}/cutensor.h" "${CUTENSOR_INCLUDE_DIRS}/cutensor/types.h")
if(EXISTS "${_hdr}")
file(STRINGS "${_hdr}" _cutensor_major_line REGEX "^#define[ \t]+CUTENSOR_MAJOR[ \t]+[0-9]+")
if(_cutensor_major_line)
set(_cutensor_version_header "${_hdr}")
break()
endif()
endif()
endforeach()

if(_cutensor_version_header)
file(STRINGS "${_cutensor_version_header}" _cutensor_minor_line REGEX "^#define[ \t]+CUTENSOR_MINOR[ \t]+[0-9]+")
string(REGEX REPLACE ".*CUTENSOR_MAJOR[ \t]+([0-9]+).*" "\\1" CUTENSOR_VERSION_MAJOR "${_cutensor_major_line}")
string(REGEX REPLACE ".*CUTENSOR_MINOR[ \t]+([0-9]+).*" "\\1" CUTENSOR_VERSION_MINOR "${_cutensor_minor_line}")
set(CUTENSOR_VERSION "${CUTENSOR_VERSION_MAJOR}.${CUTENSOR_VERSION_MINOR}")
message(STATUS "cuTENSOR version: ${CUTENSOR_VERSION} (from ${_cutensor_version_header})")
if(CUTENSOR_VERSION_MAJOR LESS 2)
message(FATAL_ERROR
"cuTENSOR >= 2.0 is required, but found ${CUTENSOR_VERSION} in "
"${CUTENSOR_ROOT}. Install cuTENSOR 2.x and point CUTENSOR_ROOT at it.")
endif()
else()
message(WARNING
"Could not determine the cuTENSOR version from headers under "
"${CUTENSOR_INCLUDE_DIRS}; Cytnx requires cuTENSOR >= 2.0.")
endif()

# set libs:
find_library(
CUTENSOR_LIB
Expand All @@ -60,6 +92,24 @@ find_library(
)
message(STATUS "CUTENSOR_LIB: ${CUTENSOR_LIB}")
message(STATUS "CUTENSORMg_LIB: ${CUTENSORMg_LIB}")
set(CUTENSOR_LIBRARIES "${CUTENSOR_LIB};${CUTENSORMg_LIB}")
message(STATUS "ok")
set(CUTENSOR_FOUND TRUE)
# Report the directory the library was actually found in (flat lib/ or the
# versioned lib/<major>) rather than guessing a subdir, so callers and runtime
# guidance reference the real location.
if(CUTENSOR_LIB)
get_filename_component(CUTENSOR_LIBRARY_DIRS "${CUTENSOR_LIB}" DIRECTORY)
endif()
set(CUTENSOR_LIBRARIES "")
if(CUTENSOR_LIB)
list(APPEND CUTENSOR_LIBRARIES "${CUTENSOR_LIB}")
endif()
if(CUTENSORMg_LIB)
list(APPEND CUTENSOR_LIBRARIES "${CUTENSORMg_LIB}")
endif()

# CUTENSOR_FOUND must reflect whether the core library was actually located:
# the main cutensor lib is mandatory, cutensorMg is optional. Setting it
# unconditionally would let a NOTFOUND silently pass the caller's REQUIRED check.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CUTENSOR
REQUIRED_VARS CUTENSOR_LIB CUTENSOR_INCLUDE_DIRS
VERSION_VAR CUTENSOR_VERSION)
96 changes: 94 additions & 2 deletions docs/source/adv_install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ In addition, you might want to install the following optional dependencies if yo

[CUDA]

* Nvidia cuda library v10+
* Nvidia CUDA toolkit >= 12.0 (12.x and 13.x supported)
* Nvidia cuDNN library
* Nvidia cuTensor library
* Nvidia cuTENSOR library >= 2.0
* Nvidia cuQuantum library


Expand Down Expand Up @@ -155,6 +155,43 @@ Similarly, cuqauantum (compile option -DUSE_CUTENSOR=ON), requires:

$conda install -c nvidia cuquantum

.. important::

**If you install cuTENSOR and/or cuQuantum from NVIDIA tarballs**
(instead of conda), you must configure two separate paths -- one for the
build and one for runtime:

1. **Build time** -- point CMake at the extracted directories so the
libraries are found during configuration:

.. code-block:: shell

$export CUTENSOR_ROOT=/path/to/libcutensor-<version>
$export CUQUANTUM_ROOT=/path/to/cuquantum-<version>

2. **Runtime** -- the dynamic loader must also find the shared libraries
when you ``import cytnx`` (or run a C++ binary). Tarball libraries live
outside the system loader's default search path, and unlike conda they
are **not** registered with ``ldconfig``, so add their library
directories to ``LD_LIBRARY_PATH``:

.. code-block:: shell

$export LD_LIBRARY_PATH=$CUTENSOR_ROOT/lib:$CUQUANTUM_ROOT/lib:$LD_LIBRARY_PATH

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Match cuTENSOR runtime path to the finder

The documented runtime workaround does not match the library directory that this commit's finder accepts: FindCUTENSOR.cmake now constructs ${CUTENSOR_ROOT}/lib/${CUDAToolkit_VERSION_MAJOR} for CUDA 12/13. For a tarball install that is found by the build, exporting only $CUTENSOR_ROOT/lib leaves the actual libcutensor.so directory out of LD_LIBRARY_PATH, so import cytnx can still fail with the loader error this note is meant to prevent; document the versioned subdirectory or reuse the detected library dir.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 10b1536f, together with the finder change. Instead of documenting a versioned-subdir caveat, FindCUTENSOR now searches the flat lib/ directly and derives CUTENSOR_LIBRARY_DIRS from the located library, so for a 2.x tarball the documented $CUTENSOR_ROOT/lib matches the directory the build actually used.

Posted by Claude Code on behalf of @pcchen


For cuTENSOR 2.x tarballs the libraries sit directly under ``lib/``, so
the path above is sufficient at both build and runtime. Older cuTENSOR
1.x tarballs instead use a per-CUDA subdirectory (``lib/<cuda-major>``,
e.g. ``lib/12``); on that legacy layout point ``LD_LIBRARY_PATH`` at the
subdirectory. Cytnx's ``FindCUTENSOR`` searches both layouts at build
time, so the runtime path matches whichever one was found. If
``LD_LIBRARY_PATH`` is not set
up, importing/running Cytnx fails with ``error while loading shared
libraries: libcutensor.so... cannot open shared object file``, or
silently binds to a mismatched system copy of the library if one is
present. Add these ``export`` lines to your shell profile (e.g.
``~/.bashrc``) to make them persistent.
Comment on lines +182 to +193

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for all 2.x. cuTENSOR start to separate the package for different CUDA version since 2.3. And we don't support cuTENSOR < 2.0 and cuQuantum < 24.0. See #447.

I will not recommend users export LD_LIBRARY_PATH to ~/.bashrc. This will pollute the environment system-wide just for running cytnx built for local. Actually, having multiple version of the same libraries without isolated environment is always easy to get trouble. We can tell users how LD_LIBRARY_PATH will be used like cupy and jax, but we may also have to consider to point the user or developer who requires LD_LIBRARY_PATH at this moment to conda environment.


**Option B. Install dependencies via system package manager**

You can also choose to install dependencies directly from the system package manager, but one needs to carefully resolve the dependency path for cmake to capture them successfully.
Expand Down Expand Up @@ -345,6 +382,61 @@ In the case that Cytnx is installed locally from binary build, not from anaconda
Build troubleshooting
*************************************

Choosing between CUDA 12 and CUDA 13 when both are installed
-------------------------------------------------------------------------------------

Cytnx supports both CUDA 12.x and 13.x. If more than one CUDA toolkit is present
(for example CUDA 12 installed via ``apt`` and CUDA 13 unpacked under
``/usr/local/cuda-13``), you must select the one to build against; otherwise
CMake picks up whichever ``nvcc`` it finds first on ``PATH``, which may not be
the one you intended. There are two independent things to pin -- the **build**
toolkit and the **runtime** libraries.

**1. Build time -- select the compiler.** ``enable_language(CUDA)`` finds the
compiler only from ``CMAKE_CUDA_COMPILER``, the ``CUDACXX`` environment
variable, or ``nvcc`` on ``PATH`` (it does *not* reuse the libraries found by
``find_package(CUDAToolkit)``). Point it at the toolkit you want -- the rest of
the toolkit (libraries, headers, version) is then derived from that ``nvcc``:

.. code-block:: shell

# Use CUDA 13:
$cmake -S . -B build -DUSE_CUDA=ON -DCMAKE_CUDA_COMPILER=/usr/local/cuda-13/bin/nvcc

# ...or use CUDA 12:
$cmake -S . -B build -DUSE_CUDA=ON -DCMAKE_CUDA_COMPILER=/usr/local/cuda-12/bin/nvcc

Equivalent alternatives are ``export CUDACXX=/usr/local/cuda-13/bin/nvcc`` or
putting the desired ``bin`` directory first on ``PATH``
(``export PATH=/usr/local/cuda-13/bin:$PATH``). For the ``pip`` build, pass it
through scikit-build-core:

.. code-block:: shell

$pip install . --config-settings=cmake.args="-DUSE_CUDA=ON;-DCMAKE_CUDA_COMPILER=/usr/local/cuda-13/bin/nvcc"
Comment on lines +401 to +416

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use preset in the build command. CUDA presets require both cuTENSOR and cuQuantum. Per discussion in #583 and #584, we will not test for the configuration enabling CUDA but not cuTENSOR and cuQuantum.


The configure output reports the resolved toolkit (``CUDA Version``,
``CUDA Toolkit bin dir``, ``CUDA compiler (nvcc)``) -- check these match the
version you intended. CMake caches the detected compiler, so if you previously
configured with the wrong one, delete the build directory before reconfiguring;
a stale ``CMakeCache.txt`` keeps the old choice.

**2. Runtime -- match the shared libraries.** Selecting the compiler does not
control which CUDA runtime libraries are loaded at run time. The dynamic loader
resolves ``libcudart.so.<major>`` (and cuTENSOR/cuQuantum, etc.) via
``LD_LIBRARY_PATH``, then the ``ldconfig`` cache. If a different major version is
registered with ``ldconfig`` (commonly the ``apt`` copy under
``/usr/lib/x86_64-linux-gnu``), it can be loaded instead of the toolkit you
built against, causing version-skew crashes. To force the matching runtime, put
its library directory first:
Comment on lines +424 to +431

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This content seems duplicated with the section of cuTENSOR and cuQuantum.


.. code-block:: shell

$export LD_LIBRARY_PATH=/usr/local/cuda-13/lib64:$LD_LIBRARY_PATH

(Adjust the path to the toolkit you built against; append the cuTENSOR/cuQuantum
``lib`` directories too if you enabled them -- see the tarball note above.)

CUDA device link fails with ``elfLink linker library load error``
-------------------------------------------------------------------------------------

Expand Down
Loading