diff --git a/.github/workflows/ci_build_wheels.yaml b/.github/workflows/ci_build_wheels.yaml index 205d4be18..738137662 100644 --- a/.github/workflows/ci_build_wheels.yaml +++ b/.github/workflows/ci_build_wheels.yaml @@ -20,6 +20,7 @@ on: branches: - master - main + - mh-consolidate-cmake-configs pull_request: types: [opened, synchronize] @@ -56,4 +57,5 @@ jobs: uses: ./.github/workflows/reusable_build_wheels.yaml secrets: inherit with: - debug: ${{inputs.debug == true}} + # debug: ${{inputs.debug == true}} + debug: true diff --git a/.github/workflows/reusable_build_wheels.yaml b/.github/workflows/reusable_build_wheels.yaml index 4f3138121..8729e30ef 100644 --- a/.github/workflows/reusable_build_wheels.yaml +++ b/.github/workflows/reusable_build_wheels.yaml @@ -60,6 +60,9 @@ jobs: {os: macos-15, arch: arm64}, {os: windows-2025, arch: AMD64}, ] + env: + # SHELLOPTS is used by Bash. Add xtrace when debugging is turned on. + SHELLOPTS: ${{inputs.debug && 'xtrace' || '' }} steps: - name: Check out a copy of the git repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 @@ -89,19 +92,6 @@ jobs: os=${{matrix.conf.os}} echo MACOSX_DEPLOYMENT_TARGET=${os: -2} >> "$GITHUB_ENV" - - if: startsWith(matrix.conf.os, 'ubuntu') - name: Determine the number of threads to use (Linux) - run: echo "num_threads=$(( $(nproc) - 1 ))" >> "$GITHUB_ENV" - - - if: startsWith(matrix.conf.os, 'macos') - name: Determine the number of threads to use (MacOS) - run: echo "num_threads=$(( $(sysctl -n hw.ncpu) - 1 ))" >> "$GITHUB_ENV" - - - if: startsWith(matrix.conf.os, 'win') - name: Determine the number of threads to use (Windows) - shell: bash - run: echo "num_threads=$(( NUMBER_OF_PROCESSORS - 1 ))" >> "$GITHUB_ENV" - - name: Build and test wheels env: # Note: additional cibuildwheel settings are in pyproject.toml. @@ -110,7 +100,6 @@ jobs: CIBW_BUILD_VERBOSITY: ${{inputs.debug && 1 || ''}} # Color codes make the raw logs hard to read. (CMake uses CLICOLOR.) CLICOLOR: ${{inputs.debug && 0 || ''}} - CMAKE_BUILD_PARALLEL_LEVEL: ${{env.num_threads}} run: | cibuildwheel --output-dir wheelhouse diff --git a/CMakeLists.txt b/CMakeLists.txt index e9bf3fef4..8ce6f3bd6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,44 +15,84 @@ cmake_minimum_required(VERSION 3.31) project(qsim LANGUAGES CXX) -include(CheckLanguage) -check_language(CUDA) +# ~~~~~ Set project-wide policies ~~~~~ + +# The following settings mirror what is in our hand-written Makefiles. +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) -# This text is prepended to messages printed by this config file so it's -# easier to figure out what came from where in the logs. -set(MSG_PREFIX "[qsim cmake configuration]") +# Check we have Python libraries & header files necessary to build modules. +find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module) + +# ~~~~~ Analyze the host's hardware & software features ~~~~~ # CMake normally sets CMAKE_APPLE_SILICON_PROCESSOR on Apple Silicon; however, # it doesn't happen when running builds using cibuildwheel, even on Apple -# Silicon. We have had better luck checking and seting it ourselves. +# Silicon. It's more reliable to check and set it ourselves. if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") set(CMAKE_APPLE_SILICON_PROCESSOR TRUE) - message(STATUS "${MSG_PREFIX} detected Apple Silicon") + message(VERBOSE "Detected Apple Silicon") else() set(CMAKE_APPLE_SILICON_PROCESSOR FALSE) - message(STATUS "${MSG_PREFIX} did not detect Apple Silicon") endif() +find_package(OpenMP COMPONENTS CXX NO_POLICY_SCOPE) +if(NOT OpenMP_CXX_FOUND) + message(STATUS "(Without OpenMP, qsim cannot support thread parallelization)") +endif() + +include(CheckLanguage) +check_language(CUDA) if(CMAKE_CUDA_COMPILER) enable_language(CUDA) - message(STATUS "${MSG_PREFIX} found CUDA compiler " - "${CMAKE_CUDA_COMPILER} ${CMAKE_CUDA_COMPILER_VERSION}") else() - message(STATUS "${MSG_PREFIX} did not find CUDA compiler") - # Did not find the CUDA framewwork, so check for the HIP as an alternative. - execute_process(COMMAND which hipcc - OUTPUT_VARIABLE has_hipcc - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(has_hipcc) - message(STATUS "${MSG_PREFIX} found hipcc") - project(qsim LANGUAGES CXX HIP) + # If CUDA is not available, check for HIP as an alternative. + message(STATUS "CUDA not available; looking for a HIP compiler") + find_program(_HIP_COMPILER hipcc) + if(_HIP_COMPILER) + enable_language(HIP) + message(STATUS "CUDA not available; looking for a HIP compiler - found") else() - message(STATUS "${MSG_PREFIX} did not find hipcc") + message(STATUS "CUDA not available; looking for a HIP compiler - not found") + message(STATUS "(Without CUDA or HIP, qsim cannot use GPUs for acceleration)") endif() endif() -find_package(OpenMP REQUIRED) +include(dev_tools/cmake/CheckCPU.cmake) +# Note: CMake uses "WIN32" for Windows targets, including Win64. +if(WIN32) + check_cpu_support("avx2" CPU_SUPPORTS_AVX2) + check_cpu_support("avx512f" CPU_SUPPORTS_AVX512) + check_cpu_support("sse4.1" CPU_SUPPORTS_SSE4) +elseif(LINUX) + check_cpu_support("avx2" CPU_SUPPORTS_AVX2) + check_cpu_support("avx512f" CPU_SUPPORTS_AVX512) + check_cpu_support("sse4" CPU_SUPPORTS_SSE4) +elseif(APPLE AND NOT CMAKE_APPLE_SILICON_PROCESSOR) + check_cpu_support("avx2_0" CPU_SUPPORTS_AVX2) + check_cpu_support("avx512f" CPU_SUPPORTS_AVX512) + check_cpu_support("sse4_1" CPU_SUPPORTS_SSE4) +endif() + +# Configure LTO for compilers that support it. +include(CheckIPOSupported) +check_ipo_supported(RESULT HAVE_LTO) + +# ~~~~~ Configure the build ~~~~~ + +if(WIN32) + add_compile_options(/O2) +else() + add_compile_options(-O3 -D_GLIBCXX_USE_CXX11_ABI=1) +endif() + +if(HAVE_LTO) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) +endif() + +include(dev_tools/cmake/GetPybind11.cmake) # Always build the basic part. add_subdirectory(pybind_interface/basic) @@ -65,20 +105,34 @@ if(NOT CMAKE_APPLE_SILICON_PROCESSOR) if(DEFINED ENV{CUQUANTUM_ROOT}) add_subdirectory(pybind_interface/custatevec) endif() - elseif(has_hipcc) + elseif(HAVE_HIPCC) add_subdirectory(pybind_interface/hip) endif() - add_subdirectory(pybind_interface/sse) - add_subdirectory(pybind_interface/avx512) - add_subdirectory(pybind_interface/avx2) -endif() + if(CPU_SUPPORTS_AVX2) + add_subdirectory(pybind_interface/avx2) + endif() -# Additional miscellanous settings. -# The following settings mirror what is in our hand-written Makefiles. -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_POSITION_INDEPENDENT_CODE ON) + if(CPU_SUPPORTS_AVX512) + add_subdirectory(pybind_interface/avx512) + endif() -# Print additional useful info. -message(STATUS "${MSG_PREFIX} OpenMP found = ${OPENMP_FOUND}") -message(STATUS "${MSG_PREFIX} shell $PATH = $ENV{PATH}") + if(CPU_SUPPORTS_SSE4) + add_subdirectory(pybind_interface/sse) + endif() +endif() + +if(APPLE) + include_directories( + "/usr/local/include" + "/usr/local/opt/llvm/include" + "/opt/homebrew/include" + "/opt/homebrew/opt/llvm@19/include" + ) + link_directories( + "/usr/local/lib" + "/usr/local/opt/llvm/lib" + "/opt/homebrew/lib" + "/opt/homebrew/opt/llvm@19/lib" + ) +endif() diff --git a/dev_tools/cmake/CheckCPU.cmake b/dev_tools/cmake/CheckCPU.cmake new file mode 100644 index 000000000..3cd0fdf2e --- /dev/null +++ b/dev_tools/cmake/CheckCPU.cmake @@ -0,0 +1,104 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +include(CheckCXXCompilerFlag) +include(CheckCXXSourceRuns) + +macro(check_cpu_support _FEATURE_STRING _FEATURE_FLAG) + set(${_FEATURE_FLAG} FALSE) + + message(STATUS "Testing platform support for ${_FEATURE_STRING}") + if(WIN32) + # On Windows, there's no built-in method to learn the CPU flags. Third- + # party tools exist, but downloading & running them is a security risk. + # We resort instead to compiling and running our own small program. + set(_CHECKER_FILE_PATH "${CMAKE_BINARY_DIR}/checker.cpp") + file(WRITE ${_CHECKER_FILE_PATH} "${_WIN32_CHECKER_SRC}") + try_run( + _CHECKER_RETURN_VALUE + _CHECKER_COMPILED + "${CMAKE_BINARY_DIR}" + "${_CHECKER_FILE_PATH}" + RUN_OUTPUT_VARIABLE _CPU_FEATURES + ) + if(_CHECKER_COMPILED AND _CHECKER_RETURN_VALUE EQUAL 0) + string(FIND "${_CPU_FEATURES}" ${_FEATURE_STRING} _FOUND) + if(NOT _FOUND EQUAL -1) + set(${_FEATURE_FLAG} TRUE) + endif() + else() + message(STATUS "Unable to autodetect vector instruction sets") + if(NOT _CHECKER_COMPILED) + message(STATUS " (failed to compile CPU checker utility)") + else() + message(STATUS " (got an error trying to run our CPU checker)") + endif() + endif() + + elseif(LINUX) + execute_process( + COMMAND bash --noprofile -c "grep -q ${_FEATURE_STRING} /proc/cpuinfo" + RESULT_VARIABLE _EXIT_CODE + ) + if(_EXIT_CODE EQUAL 0) + set(${_FEATURE_FLAG} TRUE) + endif() + + elseif(APPLE AND NOT CMAKE_APPLE_SILICON_PROCESSOR) + execute_process( + COMMAND bash --noprofile -c "sysctl -n hw.optional.${_FEATURE_STRING}" + RESULT_VARIABLE _EXIT_CODE + OUTPUT_VARIABLE _FLAG_VALUE + ) + if(_EXIT_CODE EQUAL 0 AND _FLAG_VALUE EQUAL "1") + set(${_FEATURE_FLAG} TRUE) + endif() + endif() + + if(${_FEATURE_FLAG}) + message(STATUS "Testing platform support for ${_FEATURE_STRING} - found") + else() + message(STATUS "Testing platform support for ${_FEATURE_STRING} - not found") + endif() +endmacro() + +# Small Windows C++ program to test bits in certain Intel CPU registers. +# Info about the registers in Intel CPUs: https://en.wikipedia.org/wiki/CPUID +# +# EAX ECX Bit Name +# 1 0 19 sse4.1 +# 1 0 20 sse4.2 +# 1 0 28 avx +# 7 0 5 avx2 +# 7 0 16 avx512f +# +# Note: CMake caches the output of try_run() by default; therefore, this program +# will not be executed each time try_run() is called. + +set(_WIN32_CHECKER_SRC " +#include +#include +#include + +int main() { + int cpuInfo[4]; + __cpuidex(cpuInfo, 1, 0); + std::cout << ((cpuInfo[2] & (1 << 19)) ? \"sse4.1\\n\" : \"\"); + std::cout << ((cpuInfo[2] & (1 << 20)) ? \"sse4.2\\n\" : \"\"); + __cpuidex(cpuInfo, 7, 0); + std::cout << ((cpuInfo[1] & (1 << 5)) ? \"avx2\\n\" : \"\") + << ((cpuInfo[1] & (1 << 16)) ? \"avx512f\\n\" : \"\"); + return 0; +} +") diff --git a/dev_tools/cmake/GetPybind11.cmake b/dev_tools/cmake/GetPybind11.cmake new file mode 100644 index 000000000..ec9386c15 --- /dev/null +++ b/dev_tools/cmake/GetPybind11.cmake @@ -0,0 +1,40 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(NOT pybind11_FOUND) + set(MIN_PYBIND_VERSION "2.13.6") + + find_package( + pybind11 + CONFIG + HINTS "${Python3_SITELIB}" + NO_POLICY_SCOPE) + + # qsim's requirements.txt and setup.py both include a requirement for + # "pybind11[global]", so the Pybind11 CMake plugin should be found no matter + # whether the user is doing a "pip install qsim" or a local build. Still, we + # want to be sure, and also want to make sure to get the min version we need. + if(NOT pybind11_FOUND OR ${pybind11_VERSION} VERSION_LESS ${MIN_PYBIND_VERSION}) + include(FetchContent) + FetchContent_Declare( + pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11 + GIT_TAG "v${MIN_PYBIND_VERSION}" + OVERRIDE_FIND_PACKAGE + ) + FetchContent_MakeAvailable(pybind11) + endif() + + include_directories(${PYTHON_INCLUDE_DIRS} ${pybind11_INCLUDE_DIR}) +endif() diff --git a/pybind_interface/GetPybind11.cmake b/pybind_interface/GetPybind11.cmake deleted file mode 100644 index b2182b45d..000000000 --- a/pybind_interface/GetPybind11.cmake +++ /dev/null @@ -1,18 +0,0 @@ -include(FetchContent) - -set(MIN_PYBIND_VERSION "2.13.6") - -# Suppress warning "Compatibility with CMake < 3.10 will be removed ..." coming -# from Pybind11. Not ideal, but avoids wasting time trying to find the cause. -# TODO(mhucka): remove the settings when pybind11 updates its CMake files -set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "Disable CMake deprecation warnings" FORCE) - -FetchContent_Declare( - pybind11 - GIT_REPOSITORY https://github.com/pybind/pybind11 - GIT_TAG "v${MIN_PYBIND_VERSION}" - OVERRIDE_FIND_PACKAGE -) -FetchContent_MakeAvailable(pybind11) - -set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "Reenable CMake deprecation warnings" FORCE) diff --git a/pybind_interface/avx2/CMakeLists.txt b/pybind_interface/avx2/CMakeLists.txt index 16eed37d2..ae59edf83 100644 --- a/pybind_interface/avx2/CMakeLists.txt +++ b/pybind_interface/avx2/CMakeLists.txt @@ -12,31 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.31) -project(qsim) - IF (WIN32) - set(CMAKE_CXX_FLAGS "/arch:AVX2 /O2 /openmp") -ELSE() - set(CMAKE_CXX_FLAGS "-mavx2 -mfma -O3 -flto=auto") + add_compile_options(/arch:AVX2) +ELSEIF(NOT CMAKE_APPLE_SILICON_PROCESSOR) + add_compile_options(-mavx2 -mfma) ENDIF() -if(APPLE) - include_directories( - "/usr/local/include" - "/usr/local/opt/llvm/include" - "/opt/homebrew/include" - "/opt/homebrew/opt/llvm@19/include" - ) - link_directories( - "/usr/local/lib" - "/usr/local/opt/llvm/lib" - "/opt/homebrew/lib" - "/opt/homebrew/opt/llvm@19/lib" - ) -endif() - -INCLUDE(../GetPybind11.cmake) pybind11_add_module(qsim_avx2 pybind_main_avx2.cpp) -target_link_libraries(qsim_avx2 PUBLIC OpenMP::OpenMP_CXX) +if(OpenMP_CXX_FOUND) + target_link_libraries(qsim_avx2 PUBLIC pybind11::headers OpenMP::OpenMP_CXX) +endif() + diff --git a/pybind_interface/avx512/CMakeLists.txt b/pybind_interface/avx512/CMakeLists.txt index 5ca2da26e..1929f32b9 100644 --- a/pybind_interface/avx512/CMakeLists.txt +++ b/pybind_interface/avx512/CMakeLists.txt @@ -12,31 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.31) -project(qsim) - IF (WIN32) - set(CMAKE_CXX_FLAGS "/arch:AVX512 /O2 /openmp") -ELSE() - set(CMAKE_CXX_FLAGS "-mavx512f -mbmi2 -O3 -flto=auto") + add_compile_options(/arch:AVX512) +ELSEIF(NOT CMAKE_APPLE_SILICON_PROCESSOR) + add_compile_options(-mavx512f -mbmi2) ENDIF() -if(APPLE) - include_directories( - "/usr/local/include" - "/usr/local/opt/llvm/include" - "/opt/homebrew/include" - "/opt/homebrew/opt/llvm@19/include" - ) - link_directories( - "/usr/local/lib" - "/usr/local/opt/llvm/lib" - "/opt/homebrew/lib" - "/opt/homebrew/opt/llvm@19/lib" - ) -endif() - -INCLUDE(../GetPybind11.cmake) pybind11_add_module(qsim_avx512 pybind_main_avx512.cpp) -target_link_libraries(qsim_avx512 PUBLIC OpenMP::OpenMP_CXX) +if(OpenMP_CXX_FOUND) + target_link_libraries(qsim_avx512 PUBLIC pybind11::headers OpenMP::OpenMP_CXX) +endif() + diff --git a/pybind_interface/GetCUDAARCHS.cmake b/pybind_interface/avx512/CMakeLists.txt.avx10 similarity index 50% rename from pybind_interface/GetCUDAARCHS.cmake rename to pybind_interface/avx512/CMakeLists.txt.avx10 index aafa0ad33..1ce78a335 100644 --- a/pybind_interface/GetCUDAARCHS.cmake +++ b/pybind_interface/avx512/CMakeLists.txt.avx10 @@ -1,4 +1,4 @@ -# Copyright 2025 Google LLC. All Rights Reserved. +# Copyright 2019 Google LLC. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,15 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. +cmake_minimum_required(VERSION 3.31) +project(qsim) -# Check whether the user has provided info about the GPU(s) installed -# on their system. If not, try to determine what it is automaticaly. -if(CMAKE_CUDA_ARCHITECTURES) - # CMake 3.18+ sets this variable from $CUDAARCHS automatically. - message(STATUS "qsim: using CUDA architectures " - "${CMAKE_CUDA_ARCHITECTURES}") -else() - # Compile for all supported major and minor real architectures, and the - # highest major virtual architecture. - set(CMAKE_CUDA_ARCHITECTURES native) +IF (WIN32) + add_compile_options(/arch:AVX512) +ELSEIF(HAVE_AVX512_10) + add_compile_options(-mavx10.1-512 -mbmi2) +ELSE() + add_compile_options(-mavx512f -mbmi2) +ENDIF() + +INCLUDE(../../dev_tools/cmake/GetPybind11.cmake) +pybind11_add_module(qsim_avx512 pybind_main_avx512.cpp) + +if(OpenMP_CXX_FOUND) + target_link_libraries(qsim_avx512 PUBLIC OpenMP::OpenMP_CXX) endif() diff --git a/pybind_interface/basic/CMakeLists.txt b/pybind_interface/basic/CMakeLists.txt index ca9e6c263..1a222052a 100644 --- a/pybind_interface/basic/CMakeLists.txt +++ b/pybind_interface/basic/CMakeLists.txt @@ -12,31 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.31) -project(qsim) - -if(WIN32) - set(CMAKE_CXX_FLAGS "/O2 /openmp") -else() - set(CMAKE_CXX_FLAGS "-O3 -flto=auto") -endif() - -if(APPLE) - include_directories( - "/usr/local/include" - "/usr/local/opt/llvm/include" - "/opt/homebrew/include" - "/opt/homebrew/opt/llvm@19/include" - ) - link_directories( - "/usr/local/lib" - "/usr/local/opt/llvm/lib" - "/opt/homebrew/lib" - "/opt/homebrew/opt/llvm@19/lib" - ) +if(NOT WIN32) + add_compile_options(-march=native) endif() -INCLUDE(../GetPybind11.cmake) pybind11_add_module(qsim_basic pybind_main_basic.cpp) -target_link_libraries(qsim_basic PUBLIC OpenMP::OpenMP_CXX) +if(OpenMP_CXX_FOUND) + target_link_libraries(qsim_basic PUBLIC OpenMP::OpenMP_CXX) +endif() + diff --git a/pybind_interface/cuda/CMakeLists.txt b/pybind_interface/cuda/CMakeLists.txt index 8c57e3b81..f83b485c9 100644 --- a/pybind_interface/cuda/CMakeLists.txt +++ b/pybind_interface/cuda/CMakeLists.txt @@ -12,40 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.31) -project(qsim LANGUAGES CXX CUDA) - -if(WIN32) - set(CMAKE_CXX_FLAGS "/O2 /openmp") +if(CMAKE_CUDA_ARCHITECTURES) + # CMake 3.18+ sets this variable from $CUDAARCHS automatically. + message(STATUS "using CUDA architectures ${CMAKE_CUDA_ARCHITECTURES}") else() - set(CMAKE_CXX_FLAGS "-O3 -flto=auto") -endif() - -if(APPLE) - include_directories( - "/usr/local/include" - "/usr/local/opt/llvm/include" - "/opt/homebrew/include" - "/opt/homebrew/opt/llvm@19/include" - ) - link_directories( - "/usr/local/lib" - "/usr/local/opt/llvm/lib" - "/opt/homebrew/lib" - "/opt/homebrew/opt/llvm@19/lib" - ) -endif() - -include(../GetPybind11.cmake) -include(../GetCUDAARCHS.cmake) - -find_package(Python 3.10 REQUIRED) - -include_directories(${PYTHON_INCLUDE_DIRS}) -if(pybind11_FOUND) - include_directories(${pybind11_INCLUDE_DIRS}) -else() # means pybind11 has been fetched in GetPybind11.cmake - include_directories(${pybind11_SOURCE_DIR}/include) + message(STATUS "using 'native' for the CUDA architecture value") + set(CMAKE_CUDA_ARCHITECTURES native) endif() add_library(qsim_cuda MODULE pybind_main_cuda.cpp) @@ -56,4 +28,7 @@ set_target_properties(qsim_cuda PROPERTIES ) set_source_files_properties(pybind_main_cuda.cpp PROPERTIES LANGUAGE CUDA) -target_link_libraries(qsim_cuda OpenMP::OpenMP_CXX) +if(OpenMP_CXX_FOUND) + target_link_libraries(qsim_cuda PUBLIC OpenMP::OpenMP_CXX) +endif() + diff --git a/pybind_interface/custatevec/CMakeLists.txt b/pybind_interface/custatevec/CMakeLists.txt index d5b280e72..ba2e2fd49 100644 --- a/pybind_interface/custatevec/CMakeLists.txt +++ b/pybind_interface/custatevec/CMakeLists.txt @@ -12,35 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.31) -project(qsim LANGUAGES CXX CUDA) - -if(WIN32) - set(CMAKE_CXX_FLAGS "/O2 /openmp") -else() - set(CMAKE_CXX_FLAGS "-O3 -flto=auto") -endif() - -if(APPLE) - include_directories( - "/usr/local/include" - "/usr/local/opt/llvm/include" - "/opt/homebrew/include" - "/opt/homebrew/opt/llvm@19/include" - ) - link_directories( - "/usr/local/lib" - "/usr/local/opt/llvm/lib" - "/opt/homebrew/lib" - "/opt/homebrew/opt/llvm@19/lib" - ) -endif() - -INCLUDE(../GetPybind11.cmake) -find_package(Python3 3.10 REQUIRED) - -include_directories(${pybind11_INCLUDE_DIRS}) - include_directories($ENV{CUQUANTUM_ROOT}/include) link_directories($ENV{CUQUANTUM_ROOT}/lib $ENV{CUQUANTUM_ROOT}/lib64) @@ -53,4 +24,7 @@ set_target_properties(qsim_custatevec PROPERTIES ) set_source_files_properties(pybind_main_custatevec.cpp PROPERTIES LANGUAGE CUDA) -target_link_libraries(qsim_custatevec OpenMP::OpenMP_CXX) +if(OpenMP_CXX_FOUND) + target_link_libraries(qsim_custatevec PUBLIC pybind11::headers OpenMP::OpenMP_CXX) +endif() + diff --git a/pybind_interface/decide/CMakeLists.txt b/pybind_interface/decide/CMakeLists.txt index 8b6413821..60b1df8c8 100644 --- a/pybind_interface/decide/CMakeLists.txt +++ b/pybind_interface/decide/CMakeLists.txt @@ -12,67 +12,37 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.31) -project(qsim LANGUAGES CXX) - -include(CheckLanguage) -check_language(CUDA) - -if(WIN32) - set(CMAKE_CXX_FLAGS "/O2 /openmp") -else() - set(CMAKE_CXX_FLAGS "-O3 -flto=auto") -endif() - -if(APPLE) - include_directories( - "/usr/local/include" - "/usr/local/opt/llvm/include" - "/opt/homebrew/include" - "/opt/homebrew/opt/llvm@19/include" - ) - link_directories( - "/usr/local/lib" - "/usr/local/opt/llvm/lib" - "/opt/homebrew/lib" - "/opt/homebrew/opt/llvm@19/lib" - ) -endif() - -include(../GetPybind11.cmake) - # Configure based on the detected platform if(CMAKE_CUDA_COMPILER) - include(../GetCUDAARCHS.cmake) + if(NOT CMAKE_CUDA_ARCHITECTURES) + set(CMAKE_CUDA_ARCHITECTURES native) + endif() add_library(qsim_decide MODULE decide.cpp) if(DEFINED ENV{CUQUANTUM_ROOT}) target_compile_options(qsim_decide PRIVATE $<$:-D__CUSTATEVEC__> ) endif() - find_package(Python3 3.10 REQUIRED COMPONENTS Interpreter Development) - include_directories(${PYTHON_INCLUDE_DIRS} ${pybind11_SOURCE_DIR}/include) set_target_properties(qsim_decide PROPERTIES CUDA_ARCHITECTURES "${CMAKE_CUDA_ARCHITECTURES}" PREFIX "${PYTHON_MODULE_PREFIX}" SUFFIX "${PYTHON_MODULE_EXTENSION}" ) set_source_files_properties(decide.cpp PROPERTIES LANGUAGE CUDA) - target_link_libraries(qsim_decide OpenMP::OpenMP_CXX) elseif(has_hipcc) list(APPEND CMAKE_MODULE_PATH "/opt/rocm/lib/cmake/hip") find_package(HIP REQUIRED) hip_add_library(qsim_decide MODULE decide.cpp) set_source_files_properties(decide.cpp PROPERTIES LANGUAGE HIP) - find_package(Python3 3.10 REQUIRED COMPONENTS Interpreter Development) - include_directories(${PYTHON_INCLUDE_DIRS} ${pybind11_SOURCE_DIR}/include) set_target_properties(qsim_decide PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" SUFFIX "${PYTHON_MODULE_EXTENSION}" ) - target_link_libraries(qsim_decide PUBLIC OpenMP::OpenMP_CXX) else() pybind11_add_module(qsim_decide decide.cpp) +endif() + +if(OpenMP_CXX_FOUND) target_link_libraries(qsim_decide PUBLIC OpenMP::OpenMP_CXX) endif() diff --git a/pybind_interface/hip/CMakeLists.txt b/pybind_interface/hip/CMakeLists.txt index 6956b01b9..a7f82a9b0 100644 --- a/pybind_interface/hip/CMakeLists.txt +++ b/pybind_interface/hip/CMakeLists.txt @@ -12,23 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.31) -project(qsim LANGUAGES CXX HIP) - -if(WIN32) - set(CMAKE_CXX_FLAGS "/O2 /openmp") -else() - set(CMAKE_CXX_FLAGS "-O3 -flto=auto") -endif() - -INCLUDE(../GetPybind11.cmake) -find_package(PythonLibs 3.10 REQUIRED) - list(APPEND CMAKE_MODULE_PATH "/opt/rocm/lib/cmake/hip") find_package(HIP REQUIRED) -include_directories(${PYTHON_INCLUDE_DIRS} ${pybind11_SOURCE_DIR}/include) - hip_add_library(qsim_hip MODULE pybind_main_hip.cpp) set_target_properties(qsim_hip PROPERTIES @@ -37,4 +23,7 @@ set_target_properties(qsim_hip PROPERTIES ) set_source_files_properties(pybind_main_hip.cpp PROPERTIES LANGUAGE HIP) -target_link_libraries(qsim_hip PUBLIC OpenMP::OpenMP_CXX) +if(OpenMP_CXX_FOUND) + target_link_libraries(qsim_hip PUBLIC pybind11::headers OpenMP::OpenMP_CXX) +endif() + diff --git a/pybind_interface/sse/CMakeLists.txt b/pybind_interface/sse/CMakeLists.txt index e9ddea57a..9ea5e0890 100644 --- a/pybind_interface/sse/CMakeLists.txt +++ b/pybind_interface/sse/CMakeLists.txt @@ -12,31 +12,48 @@ # See the License for the specific language governing permissions and # limitations under the License. -cmake_minimum_required(VERSION 3.31) -project(qsim) - -IF (WIN32) - set(CMAKE_CXX_FLAGS "/O2 /openmp") -ELSE() - set(CMAKE_CXX_FLAGS "-msse4.1 -O3 -flto=auto") -ENDIF() - -if(APPLE) - include_directories( - "/usr/local/include" - "/usr/local/opt/llvm/include" - "/opt/homebrew/include" - "/opt/homebrew/opt/llvm@19/include" - ) - link_directories( - "/usr/local/lib" - "/usr/local/opt/llvm/lib" - "/opt/homebrew/lib" - "/opt/homebrew/opt/llvm@19/lib" - ) +# If the present module is included in the qsim build, it means that the +# top-level CMakeLists.txt found evidence that the hardware supports SSE4.1. +# However, the correct compiler flag still needs to be determined. +if(WIN32) + # Some comments in forums suggest that for some programs, using arch=sse4.1 + # may provide better performance than using arch=sse4.2. It's not available + # in newer VS but still try this flag first in case this compiler is older. + check_cxx_compiler_flag("/arch:SSE4.1" WIN32_SSE4_AVAILABLE) + if(WIN32_SSE4_AVAILABLE) + add_compile_options(/arch:SSE4.1) + else() + # Visual Studio 17.11.5 added /arch:SSE4.2 in Oct. 2024. Try to use that + # if it's available. + check_cxx_compiler_flag("/arch:SSE4.2" WIN32_SSE4_AVAILABLE) + if(WIN32_SSE4_AVAILABLE) + add_compile_options(/arch:SSE4.2) + else() + # VS 2022 docs say /arch:SSE2 will make the auto-vectorizer emit 4.2 + # instructions when available. Use this if we get this far. + check_cxx_compiler_flag("/arch:SSE2" WIN32_SSE2_AVAILABLE) + if(WIN32_SSE2_AVAILABLE) + add_compile_options(/arch:SSE2) + else() + # Although it's not guaranteed that AVX2 support guarantees + # SSE4.1 too, it seems to be true in practice. + if(CPU_SUPPORTS_AVX2) + # If we get here, it means elsewhere we'll have added avx2. + # Nothing more to do. + else() + # Other options failed. Final fallback: in practice, if a + # CPU supports AVX, it is likely to also support SSE4.1. + add_compile_options(/arch:AVX) + endif() + endif() + endif() + endif() +else() + add_compile_options(-msse4.1) endif() -INCLUDE(../GetPybind11.cmake) pybind11_add_module(qsim_sse pybind_main_sse.cpp) -target_link_libraries(qsim_sse PUBLIC OpenMP::OpenMP_CXX) +if(OpenMP_CXX_FOUND) + target_link_libraries(qsim_sse PUBLIC pybind11::headers OpenMP::OpenMP_CXX) +endif()