diff --git a/.github/workflows/release_wheels.yml b/.github/workflows/release_wheels.yml index 292f4dc5..d6161a47 100644 --- a/.github/workflows/release_wheels.yml +++ b/.github/workflows/release_wheels.yml @@ -12,10 +12,14 @@ jobs: fail-fast: false matrix: include: - - os: macos-12 + - os: macos-12 # x86_64 name: mac cibw: build: "cp37* cp38* cp39* cp310* cp311*" + - os: macos-13-large # Apple Silicon + name: mac_arm64 + cibw: + build: "cp37* cp38* cp39* cp310* cp311*" - os: ubuntu-20.04 name: manylinux2014 cibw: @@ -32,7 +36,7 @@ jobs: CIBW_SKIP: "*musllinux*" CIBW_ARCHS: "${{ matrix.cibw.arch || 'auto' }}" CIBW_MANYLINUX_X86_64_IMAGE: "${{ matrix.cibw.manylinux_image }}" - CIBW_BEFORE_BUILD_MACOS: "brew install libomp llvm" + CIBW_BEFORE_BUILD_MACOS: "brew install libomp llvm && brew link --overwrite python@3.11 && brew link --force libomp" CIBW_REPAIR_WHEEL_COMMAND_MACOS: "delocate-listdeps {wheel} && delocate-wheel --verbose --require-archs {delocate_archs} -w {dest_dir} {wheel}" # to install latest delocate package CIBW_DEPENDENCY_VERSIONS: "latest" diff --git a/.github/workflows/testing_wheels.yml b/.github/workflows/testing_wheels.yml index 4d3c1ca3..f30921af 100644 --- a/.github/workflows/testing_wheels.yml +++ b/.github/workflows/testing_wheels.yml @@ -17,10 +17,14 @@ jobs: fail-fast: false matrix: include: - - os: macos-12 + - os: macos-12 # x86_64 name: mac cibw: build: "cp37* cp38* cp39* cp310* cp311*" + - os: macos-13-large # Apple Silicon + name: mac_arm64 + cibw: + build: "cp37* cp38* cp39* cp310* cp311*" - os: ubuntu-20.04 name: manylinux2014 cibw: @@ -37,7 +41,7 @@ jobs: CIBW_SKIP: "*musllinux*" CIBW_ARCHS: "${{ matrix.cibw.arch || 'auto' }}" CIBW_MANYLINUX_X86_64_IMAGE: "${{ matrix.cibw.manylinux_image }}" - CIBW_BEFORE_BUILD_MACOS: "brew install libomp llvm" + CIBW_BEFORE_BUILD_MACOS: "brew install libomp llvm && brew link --overwrite python@3.11 && brew link --force libomp" CIBW_REPAIR_WHEEL_COMMAND_MACOS: "delocate-listdeps {wheel} && delocate-wheel --verbose --require-archs {delocate_archs} -w {dest_dir} {wheel}" # to install latest delocate package CIBW_DEPENDENCY_VERSIONS: "latest" diff --git a/CMakeLists.txt b/CMakeLists.txt index f902a421..b98e1bbe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,24 +1,51 @@ +set(CMAKE_CXX_STANDARD 11) cmake_minimum_required(VERSION 3.11) -execute_process(COMMAND which nvcc OUTPUT_VARIABLE has_nvcc) -if(has_nvcc STREQUAL "") - execute_process(COMMAND which hipcc OUTPUT_VARIABLE has_hipcc) - if(has_hipcc STREQUAL "") - project(qsim) - else() - project(qsim LANGUAGES CXX HIP) - ADD_SUBDIRECTORY(pybind_interface/hip) +# Set APPLE_ARM to TRUE if running on Apple Silicon +if(APPLE) + execute_process(COMMAND uname -m OUTPUT_VARIABLE OSX_ARCH OUTPUT_STRIP_TRAILING_WHITESPACE) + if (OSX_ARCH STREQUAL "arm64") + set(APPLE_ARM TRUE) + else() # x86_64 + set(APPLE_ARM FALSE) endif() else() - project(qsim LANGUAGES CXX CUDA) - ADD_SUBDIRECTORY(pybind_interface/cuda) - if(DEFINED ENV{CUQUANTUM_ROOT}) - ADD_SUBDIRECTORY(pybind_interface/custatevec) + set(APPLE_ARM FALSE) +endif(APPLE) + +# Set the project name and language +if(APPLE) + project(qsim LANGUAGES CXX) +else() + execute_process(COMMAND which nvcc OUTPUT_VARIABLE has_nvcc OUTPUT_STRIP_TRAILING_WHITESPACE) + if(has_nvcc) + project(qsim LANGUAGES CXX CUDA) + else() + execute_process(COMMAND which hipcc OUTPUT_VARIABLE has_hipcc OUTPUT_STRIP_TRAILING_WHITESPACE) + if(has_hipcc) + project(qsim LANGUAGES CXX HIP) + else() + project(qsim LANGUAGES CXX) + endif() endif() endif() -ADD_SUBDIRECTORY(pybind_interface/sse) -ADD_SUBDIRECTORY(pybind_interface/avx512) -ADD_SUBDIRECTORY(pybind_interface/avx2) +find_package(OpenMP REQUIRED) + +# Add subdirectories based on the architecture or available compilers ADD_SUBDIRECTORY(pybind_interface/basic) ADD_SUBDIRECTORY(pybind_interface/decide) +if(NOT APPLE_ARM) + if(has_nvcc) + ADD_SUBDIRECTORY(pybind_interface/cuda) + if(DEFINED ENV{CUQUANTUM_ROOT}) + ADD_SUBDIRECTORY(pybind_interface/custatevec) + endif() + elseif(has_hipcc) + ADD_SUBDIRECTORY(pybind_interface/hip) + endif() + + ADD_SUBDIRECTORY(pybind_interface/sse) + ADD_SUBDIRECTORY(pybind_interface/avx512) + ADD_SUBDIRECTORY(pybind_interface/avx2) +endif() diff --git a/pybind_interface/GetPybind11.cmake b/pybind_interface/GetPybind11.cmake index e8516a0b..9a55c904 100644 --- a/pybind_interface/GetPybind11.cmake +++ b/pybind_interface/GetPybind11.cmake @@ -8,6 +8,21 @@ FetchContent_Declare( ) FetchContent_GetProperties(pybind11) find_package(pybind11 "${MIN_PYBIND_VERSION}" CONFIG) + +if (pybind11_FOUND) + message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") + # The pybind11_add_module doesn't correctly set the CXX_INCLUDES properly if a system pybind11 is found. + # Using `include_directories(${pybind11_INCLUDE_DIRS})` doesn't result in anything in + # CXX_INCLUDES. e.g., `pybind_interface/basic/CMakeFiles/qsim_basic.dir/flags.make` would only + # have `CXX_INCLUDES = -isystem $PREFIX/include/python3.11` and would miss `$PREFIX/include`. + # This problem would result in `fatal error: pybind11/complex.h: No such file or directory` + # This is a hack to get around that by passing `-I/path/to/include` to CXX_FLAGS + # Iterate over each include directory and add it as a compile option + foreach(INCLUDE_DIR ${pybind11_INCLUDE_DIRS}) + add_compile_options("-I${INCLUDE_DIR}") + endforeach() +endif() + if((NOT pybind11_FOUND) AND (NOT pybind11_POPULATED)) # check first on system path, then attempt git fetch FetchContent_Populate(pybind11) add_subdirectory(${pybind11_SOURCE_DIR} ${pybind11_BINARY_DIR}) diff --git a/pybind_interface/avx2/CMakeLists.txt b/pybind_interface/avx2/CMakeLists.txt index eebba584..ab7d6d83 100644 --- a/pybind_interface/avx2/CMakeLists.txt +++ b/pybind_interface/avx2/CMakeLists.txt @@ -4,7 +4,7 @@ project(qsim) IF (WIN32) set(CMAKE_CXX_FLAGS "/arch:AVX2 /O2 /openmp") ELSE() - set(CMAKE_CXX_FLAGS "-mavx2 -mfma -O3 -fopenmp") + set(CMAKE_CXX_FLAGS "-mavx2 -mfma -O3") ENDIF() if(APPLE) @@ -14,3 +14,5 @@ if(APPLE) endif() INCLUDE(../GetPybind11.cmake) pybind11_add_module(qsim_avx2 pybind_main_avx2.cpp) + +target_link_libraries(qsim_avx2 PUBLIC OpenMP::OpenMP_CXX) diff --git a/pybind_interface/avx512/CMakeLists.txt b/pybind_interface/avx512/CMakeLists.txt index 86cfdfa8..9d635dd0 100644 --- a/pybind_interface/avx512/CMakeLists.txt +++ b/pybind_interface/avx512/CMakeLists.txt @@ -5,7 +5,7 @@ project(qsim) IF (WIN32) set(CMAKE_CXX_FLAGS "/arch:AVX512 /O2 /openmp") ELSE() - set(CMAKE_CXX_FLAGS "-mavx512f -mbmi2 -O3 -fopenmp") + set(CMAKE_CXX_FLAGS "-mavx512f -mbmi2 -O3") ENDIF() if(APPLE) @@ -16,3 +16,5 @@ endif() INCLUDE(../GetPybind11.cmake) pybind11_add_module(qsim_avx512 pybind_main_avx512.cpp) + +target_link_libraries(qsim_avx512 PUBLIC OpenMP::OpenMP_CXX) diff --git a/pybind_interface/basic/CMakeLists.txt b/pybind_interface/basic/CMakeLists.txt index 35347211..ab9cd94b 100644 --- a/pybind_interface/basic/CMakeLists.txt +++ b/pybind_interface/basic/CMakeLists.txt @@ -1,11 +1,11 @@ cmake_minimum_required(VERSION 3.11) project(qsim) -IF (WIN32) +if(WIN32) set(CMAKE_CXX_FLAGS "/O2 /openmp") -ELSE() - set(CMAKE_CXX_FLAGS "-O3 -fopenmp") -ENDIF() +else() + set(CMAKE_CXX_FLAGS "-O3") +endif() if(APPLE) @@ -16,3 +16,5 @@ endif() INCLUDE(../GetPybind11.cmake) pybind11_add_module(qsim_basic pybind_main_basic.cpp) + +target_link_libraries(qsim_basic PUBLIC OpenMP::OpenMP_CXX) diff --git a/pybind_interface/cuda/CMakeLists.txt b/pybind_interface/cuda/CMakeLists.txt index 4e46eb32..89df1948 100644 --- a/pybind_interface/cuda/CMakeLists.txt +++ b/pybind_interface/cuda/CMakeLists.txt @@ -1,11 +1,11 @@ cmake_minimum_required(VERSION 3.11) project(qsim LANGUAGES CXX CUDA) -IF (WIN32) +if(WIN32) set(CMAKE_CXX_FLAGS "/O2 /openmp") -ELSE() - set(CMAKE_CXX_FLAGS "-O3 -fopenmp") -ENDIF() +else() + set(CMAKE_CXX_FLAGS "-O3") +endif() if(APPLE) @@ -26,3 +26,5 @@ set_target_properties(qsim_cuda PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}" ) set_source_files_properties(pybind_main_cuda.cpp PROPERTIES LANGUAGE CUDA) + +target_link_libraries(qsim_cuda PUBLIC OpenMP::OpenMP_CXX) diff --git a/pybind_interface/custatevec/CMakeLists.txt b/pybind_interface/custatevec/CMakeLists.txt index 1ef63166..77fe6631 100644 --- a/pybind_interface/custatevec/CMakeLists.txt +++ b/pybind_interface/custatevec/CMakeLists.txt @@ -15,11 +15,11 @@ cmake_minimum_required(VERSION 3.11) project(qsim LANGUAGES CXX CUDA) -IF (WIN32) +if(WIN32) set(CMAKE_CXX_FLAGS "/O2 /openmp") -ELSE() - set(CMAKE_CXX_FLAGS "-O3 -fopenmp") -ENDIF() +else() + set(CMAKE_CXX_FLAGS "-O3") +endif() if(APPLE) set(CMAKE_CXX_STANDARD 14) @@ -44,3 +44,5 @@ set_target_properties(qsim_custatevec PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}" ) set_source_files_properties(pybind_main_custatevec.cpp PROPERTIES LANGUAGE CUDA) + +target_link_libraries(qsim_custatevec PUBLIC OpenMP::OpenMP_CXX) diff --git a/pybind_interface/decide/CMakeLists.txt b/pybind_interface/decide/CMakeLists.txt index b94b0ade..15d51707 100644 --- a/pybind_interface/decide/CMakeLists.txt +++ b/pybind_interface/decide/CMakeLists.txt @@ -1,66 +1,48 @@ cmake_minimum_required(VERSION 3.11) -execute_process(COMMAND which nvcc OUTPUT_VARIABLE has_nvcc) -if(has_nvcc STREQUAL "") - execute_process(COMMAND which hipcc OUTPUT_VARIABLE has_hipcc) - if(has_hipcc STREQUAL "") - project(qsim) - else() - project(qsim LANGUAGES CXX HIP) - endif() +if(WIN32) + set(CMAKE_CXX_FLAGS "/O2 /openmp") else() - project(qsim LANGUAGES CXX CUDA) + set(CMAKE_CXX_FLAGS "-O3") endif() -IF (WIN32) - set(CMAKE_CXX_FLAGS "/O2 /openmp") -ELSE() - set(CMAKE_CXX_FLAGS "-O3 -fopenmp") -ENDIF() - -if(APPLE) +if(APPLE AND NOT APPLE_ARM) set(CMAKE_CXX_STANDARD 14) include_directories("/usr/local/include" "/usr/local/opt/llvm/include") link_directories("/usr/local/lib" "/usr/local/opt/llvm/lib") endif() -INCLUDE(../GetPybind11.cmake) - -if(has_nvcc STREQUAL "") - if(has_hipcc STREQUAL "") - pybind11_add_module(qsim_decide decide.cpp) - else() +include(../GetPybind11.cmake) + +# Configure based on the detected platform +if(has_nvcc) + find_package(CUDA REQUIRED) + cuda_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.7 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}" + ) + set_source_files_properties(decide.cpp PROPERTIES LANGUAGE CUDA) +elseif(has_hipcc) list(APPEND CMAKE_MODULE_PATH "/opt/rocm/lib/cmake/hip") find_package(HIP REQUIRED) - find_package(PythonLibs 3.7 REQUIRED) - - include_directories(${PYTHON_INCLUDE_DIRS} ${pybind11_SOURCE_DIR}/include) - hip_add_library(qsim_decide MODULE decide.cpp) - + set_source_files_properties(decide.cpp PROPERTIES LANGUAGE HIP) + find_package(Python3 3.7 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}" + PREFIX "${PYTHON_MODULE_PREFIX}" + SUFFIX "${PYTHON_MODULE_EXTENSION}" ) - set_source_files_properties(decide.cpp PROPERTIES LANGUAGE HIP) - endif() else() - find_package(PythonLibs 3.7 REQUIRED) - find_package(CUDA REQUIRED) - - include_directories(${PYTHON_INCLUDE_DIRS} ${pybind11_SOURCE_DIR}/include) - - cuda_add_library(qsim_decide MODULE decide.cpp) - - if(DEFINED ENV{CUQUANTUM_ROOT}) - target_compile_options(qsim_decide PRIVATE - $<$:-D__CUSTATEVEC__> - ) - endif() - - set_target_properties(qsim_decide PROPERTIES - PREFIX "${PYTHON_MODULE_PREFIX}" - SUFFIX "${PYTHON_MODULE_EXTENSION}" - ) - set_source_files_properties(decide.cpp PROPERTIES LANGUAGE CUDA) + pybind11_add_module(qsim_decide decide.cpp) endif() + +target_link_libraries(qsim_decide PUBLIC OpenMP::OpenMP_CXX) diff --git a/pybind_interface/decide/decide.cpp b/pybind_interface/decide/decide.cpp index 5e9a63f1..b40f6975 100644 --- a/pybind_interface/decide/decide.cpp +++ b/pybind_interface/decide/decide.cpp @@ -21,8 +21,8 @@ namespace py = pybind11; #include #define cpuid(info, x) __cpuidex(info, x, 0) -#else -// GCC Intrinsics +#elif defined(__x86_64__) || defined(__i386__) +// GCC Intrinsics for x86/x86_64 #include void cpuid(int info[4], int infoType){ __cpuid_count(infoType, 0, info[0], info[1], info[2], info[3]); @@ -34,10 +34,11 @@ enum Instructions { AVX512F = 0, AVX2 = 1, SSE4_1 = 2, BASIC = 3}; int detect_instructions() { Instructions instr = BASIC; - int info[4]; + #if !defined(__aarch64__) || !defined(__APPLE__) + // Existing x86/x86_64 specific instruction set detection logic + int info[4]; cpuid(info, 0); - int nIds = info[0]; if (nIds >= 1) { cpuid(info, 1); @@ -47,14 +48,14 @@ int detect_instructions() { } if (nIds >= 7) { cpuid(info, 7); - if ((info[1] & (1 << 5))!= 0) { + if ((info[1] & (1 << 5)) != 0) { instr = AVX2; } if ((info[1] & (1 << 16)) != 0) { instr = AVX512F; } - } + #endif return static_cast(instr); } diff --git a/pybind_interface/hip/CMakeLists.txt b/pybind_interface/hip/CMakeLists.txt index fe4b1c54..8d540c90 100644 --- a/pybind_interface/hip/CMakeLists.txt +++ b/pybind_interface/hip/CMakeLists.txt @@ -1,11 +1,11 @@ cmake_minimum_required(VERSION 3.18) project(qsim LANGUAGES CXX HIP) -IF (WIN32) +if(WIN32) set(CMAKE_CXX_FLAGS "/O2 /openmp") -ELSE() - set(CMAKE_CXX_FLAGS "-O3 -fopenmp") -ENDIF() +else() + set(CMAKE_CXX_FLAGS "-O3") +endif() INCLUDE(../GetPybind11.cmake) find_package(PythonLibs 3.7 REQUIRED) @@ -22,3 +22,5 @@ set_target_properties(qsim_hip PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}" ) set_source_files_properties(pybind_main_hip.cpp PROPERTIES LANGUAGE HIP) + +target_link_libraries(qsim_hip PUBLIC OpenMP::OpenMP_CXX) diff --git a/pybind_interface/sse/CMakeLists.txt b/pybind_interface/sse/CMakeLists.txt index fe9b218e..a9123a5a 100644 --- a/pybind_interface/sse/CMakeLists.txt +++ b/pybind_interface/sse/CMakeLists.txt @@ -4,7 +4,7 @@ project(qsim) IF (WIN32) set(CMAKE_CXX_FLAGS "/O2 /openmp") ELSE() - set(CMAKE_CXX_FLAGS "-msse4.1 -O3 -fopenmp") + set(CMAKE_CXX_FLAGS "-msse4.1 -O3") ENDIF() @@ -16,3 +16,5 @@ endif() INCLUDE(../GetPybind11.cmake) pybind11_add_module(qsim_sse pybind_main_sse.cpp) + +target_link_libraries(qsim_sse PUBLIC OpenMP::OpenMP_CXX) diff --git a/setup.py b/setup.py index 593620a6..ed4720c3 100644 --- a/setup.py +++ b/setup.py @@ -59,10 +59,23 @@ def build_extension(self, ext): build_args += ["--", "-j2"] if platform.system() == "Darwin": - cmake_args += [ - "-DCMAKE_C_COMPILER=/usr/local/opt/llvm/bin/clang", - "-DCMAKE_CXX_COMPILER=/usr/local/opt/llvm/bin/clang++", - ] + homebrew_x86 = "/usr/local/opt/llvm/bin" + homebrew_arm = "/opt/homebrew/opt/llvm/bin" + # Add clang + if shutil.which("clang") is not None: # Always prefer from PATH + cmake_args.append("-DCMAKE_C_COMPILER=clang") + elif os.path.exists(f"{homebrew_x86}/clang"): + cmake_args.append(f"-DCMAKE_C_COMPILER={homebrew_x86}/clang") + elif os.path.exists(f"{homebrew_arm}/clang"): + cmake_args.append(f"-DCMAKE_C_COMPILER={homebrew_arm}/clang") + + # Add clang++ + if shutil.which("clang++") is not None: # Always prefer from PATH + cmake_args.append("-DCMAKE_CXX_COMPILER=clang++") + elif os.path.exists(f"{homebrew_x86}/clang++"): + cmake_args.append(f"-DCMAKE_CXX_COMPILER={homebrew_x86}/clang++") + elif os.path.exists(f"{homebrew_arm}/clang++"): + cmake_args.append(f"-DCMAKE_CXX_COMPILER={homebrew_arm}/clang++") if shutil.which("hipcc") is not None: cmake_args += [