From 0c19d7e4c6ab0add56b34a86731f46d8895a2dae Mon Sep 17 00:00:00 2001 From: Pochung Chen Date: Mon, 6 Jul 2026 11:10:15 +0800 Subject: [PATCH 1/2] fix(cmake): make cuTENSOR/cuQuantum finders honest and version-aware Split out of #950 (finder-correctness portion). - CUTENSOR_FOUND / CUQUANTUM_FOUND are now derived from the actual find_library results via find_package_handle_standard_args, instead of being set TRUE unconditionally (which let a NOTFOUND silently pass a caller's REQUIRED check and link an empty / -NOTFOUND entry). - FindCUTENSOR: search both the cuTENSOR 2.x flat lib/ layout and the legacy per-CUDA-major lib/ layout (PATH_SUFFIXES), dropping the dead lib/10.2 and lib/11 branches; report the real found dir. - FindCUTENSOR: read CUTENSOR_MAJOR/MINOR from the headers and FATAL_ERROR on the unsupported 1.x API (Cytnx requires cuTENSOR >= 2.0). - FindCUQUANTUM: build CUQUANTUM_LIBRARIES conditionally (parity). Addresses the finder portions of #945 / #946. No CMakeLists.txt or compiler/version-floor policy changes (those remain separate splits). Co-Authored-By: Claude Opus 4.8 (1M context) --- cmake/Modules/FindCUQUANTUM.cmake | 18 +++++-- cmake/Modules/FindCUTENSOR.cmake | 80 +++++++++++++++++++++++++------ 2 files changed, 80 insertions(+), 18 deletions(-) diff --git a/cmake/Modules/FindCUQUANTUM.cmake b/cmake/Modules/FindCUQUANTUM.cmake index dc134ad93..057870fe1 100644 --- a/cmake/Modules/FindCUQUANTUM.cmake +++ b/cmake/Modules/FindCUQUANTUM.cmake @@ -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) diff --git a/cmake/Modules/FindCUTENSOR.cmake b/cmake/Modules/FindCUTENSOR.cmake index f9d15e707..c789afeee 100644 --- a/cmake/Modules/FindCUTENSOR.cmake +++ b/cmake/Modules/FindCUTENSOR.cmake @@ -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/, 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 @@ -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/) 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) From 6650adcbfdb3724f1b5b085df7cecd7616df9f4c Mon Sep 17 00:00:00 2001 From: Pochung Chen Date: Mon, 6 Jul 2026 12:23:38 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(cmake):=20address=20review=20=E2=80=94?= =?UTF-8?q?=20defer=20CUDA-floor=20policy,=20guard=20version=20parse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FindCUTENSOR: drop the CUDA>=12 FATAL_ERROR (a version-floor policy that belongs in the separate #962/CMakeLists.txt split and would reject valid CUDA 11 + cuTENSOR 2.x installs). The generic lib/ suffix already covers 11/12/13. This finder now stays policy-free. (codex review) - FindCUTENSOR: default CUTENSOR_MINOR to 0 when the header line is absent, so CUTENSOR_VERSION isn't malformed ("2.") for the VERSION_VAR check. (gemini review) - FindCUQUANTUM: derive CUQUANTUM_LIBRARY_DIRS from the found library via get_filename_component, mirroring FindCUTENSOR. (gemini review) Co-Authored-By: Claude Opus 4.8 (1M context) --- cmake/Modules/FindCUQUANTUM.cmake | 5 +++++ cmake/Modules/FindCUTENSOR.cmake | 32 ++++++++++++++++--------------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/cmake/Modules/FindCUQUANTUM.cmake b/cmake/Modules/FindCUQUANTUM.cmake index 057870fe1..abee325c9 100644 --- a/cmake/Modules/FindCUQUANTUM.cmake +++ b/cmake/Modules/FindCUQUANTUM.cmake @@ -49,6 +49,11 @@ find_library( ) message(STATUS "CUQUANTUM_TENSORNET_LIB: ${CUQUANTUM_TENSORNET_LIB}") message(STATUS "CUQUANTUM_CUSTATEVEC_LIB: ${CUQUANTUM_CUSTATEVEC_LIB}") +# Report the directory a library was actually found in (mirrors FindCUTENSOR), +# rather than the earlier guessed ${CUQUANTUM_ROOT}/${CUTNLIB_DIR}. +if(CUQUANTUM_TENSORNET_LIB) + get_filename_component(CUQUANTUM_LIBRARY_DIRS "${CUQUANTUM_TENSORNET_LIB}" DIRECTORY) +endif() set(CUQUANTUM_LIBRARIES "") if(CUQUANTUM_TENSORNET_LIB) list(APPEND CUQUANTUM_LIBRARIES "${CUQUANTUM_TENSORNET_LIB}") diff --git a/cmake/Modules/FindCUTENSOR.cmake b/cmake/Modules/FindCUTENSOR.cmake index c789afeee..738222484 100644 --- a/cmake/Modules/FindCUTENSOR.cmake +++ b/cmake/Modules/FindCUTENSOR.cmake @@ -27,20 +27,15 @@ else() endif() message(STATUS " cudaver: ${CUDAToolkit_VERSION_MAJOR}" ) -# 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/, 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() +# Search both cuTENSOR library layouts: 2.x tarballs place the libraries +# directly under lib/, while older tarballs and apt use a per-CUDA-major +# subdir (lib/, e.g. lib/11, lib/12, lib/13). Listing both as +# find_library PATH_SUFFIXES resolves either layout for any CUDA major. The +# older minor-specific lib/10.2 and lib/11.0 special-cases were removed; the +# generic lib/ covers them (apt multiarch paths remain, issue #946). +# The CUDA-version floor is a separate policy decision (issue #962), enforced +# in CMakeLists.txt rather than gated here, so this finder stays policy-free. +set(CUTNLIB_DIR lib lib/${CUDAToolkit_VERSION_MAJOR}) set(CUTENSOR_INCLUDE_DIRS ${CUTENSOR_ROOT}/include) @@ -61,7 +56,14 @@ 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}") + # CUTENSOR_MINOR may be absent/unmatched; default it to 0 rather than leaving + # CUTENSOR_VERSION malformed (e.g. "2."), which would break the VERSION_VAR + # comparison in find_package_handle_standard_args. + if(_cutensor_minor_line) + string(REGEX REPLACE ".*CUTENSOR_MINOR[ \t]+([0-9]+).*" "\\1" CUTENSOR_VERSION_MINOR "${_cutensor_minor_line}") + else() + set(CUTENSOR_VERSION_MINOR "0") + endif() 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)