diff --git a/build_quest.py b/build_quest.py index 50213fb..be50420 100644 --- a/build_quest.py +++ b/build_quest.py @@ -16,11 +16,15 @@ import ctypes from cffi import FFI import os +import platform def build_quest_so(): lib_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'pyquest_cffi/questlib/') quest_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "QuEST/QuEST") - questlib = os.path.join(lib_path, "libQuEST.so") + if platform.system() == 'Darwin': + questlib = os.path.join(lib_path, "libQuEST.dylib") + else: + questlib = os.path.join(lib_path, "libQuEST.so") include = [os.path.join(quest_path, "include")] _questlib = ctypes.CDLL(questlib) diff --git a/pyquest_cffi/questlib/build_quest.py b/pyquest_cffi/questlib/build_quest.py index 321691a..f8dbc57 100644 --- a/pyquest_cffi/questlib/build_quest.py +++ b/pyquest_cffi/questlib/build_quest.py @@ -16,6 +16,8 @@ import ctypes from cffi import FFI import os +import platform +import subprocess def build_quest_so() -> None: @@ -26,7 +28,11 @@ def build_quest_so() -> None: """ lib_path = os.path.dirname(os.path.realpath(__file__)) quest_path = os.path.join(lib_path, "../../QuEST/QuEST") - questlib = os.path.join(lib_path, "libQuEST.so") + + if platform.system() == 'Darwin': + questlib = os.path.join(lib_path, "libQuEST.dylib") + else: + questlib = os.path.join(lib_path, "libQuEST.so") include = [os.path.join(quest_path, "include")] _questlib = ctypes.CDLL(questlib) @@ -86,7 +92,21 @@ def build_quest_so() -> None: extra_link_args=['-Wl,-rpath,$ORIGIN'], # extra_link_args=['-Wl,-rpath={}'.format(lib_path)], ) - ffibuilder.compile(verbose=True) + # For working import also under macos target must produce .so library + ffibuilder.compile(target = '_quest.so', verbose=True) + + #Setting relative paths in libraries + if platform.system() == 'Darwin': + librun = subprocess.run(['otool', '-L', os.path.join(lib_path, '_quest.so')], + stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True) + libraries_text = librun.stdout.split('\n') + for line in libraries_text: + if 'libQuEST.dylib' in line: + pathname = line.strip().split('/libQuEST.dylib')[0] + break + subprocess.run(['install_name_tool', '-change', + os.path.join(pathname, 'libQuEST.dylib'), '@loader_path/libQuEST.dylib', + os.path.join(lib_path, '_quest.so')], check=True) if __name__ == '__main__': diff --git a/pyquest_cffi/questlib/makefile b/pyquest_cffi/questlib/makefile deleted file mode 100644 index 5cc8d39..0000000 --- a/pyquest_cffi/questlib/makefile +++ /dev/null @@ -1,382 +0,0 @@ -## This makefile is a modified version of the original QuEST makefile governed by the MIT license: - -# MIT License - -# Copyright (c) 2017 aniabrown - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# This makefile builds the QuEST library and links/compiles user code -# While attempting to accomodate as many platforms and compilers, -# unforeseen problems are inevitable: please email anna.brown@oerc.ox.ac.uk -# or tyson.jones@materials.ox.ac.uk about any errors or complications, or -# raise an issue on Github - -# TODO: adjust compiler settigs for MPI and GPU! - -#======================================================================# -# # -# User settings # -# # -#======================================================================# - -# name of the executable to create -EXE = demo - -# space-separated names (no file type) of all user source files (.c or .cpp) in the root directory -# SOURCES = - -# path to QuEST library from root directory -#QUEST_DIR = ../QuEST/QuEST -QUEST_DIR = ../../QuEST/QuEST/src -QUEST_INCLUDE_DIR = ${QUEST_DIR}/../include -# compiler to use, which should support both C and C++, to be wrapped by GPU/MPI compilers -COMPILER = gcc - -# type of above compiler, one of {GNU, INTEL, CLANG}, used for setting compiler flags -COMPILER_TYPE = GNU - -# hardwares to target: 1 means use, 0 means don't use -MULTITHREADED = 1 -DISTRIBUTED = 0 -GPUACCELERATED = 0 - -# GPU hardware dependent, lookup at https://developer.nvidia.com/cuda-gpus, write without fullstop -GPU_COMPUTE_CAPABILITY = 30 - -# whether to suppress the below warnings about compiler compatibility -SUPPRESS_WARNING = 0 - -# whether to use single, double or quad floating point precision in the state-vector {1,2,4} -PRECISION = 2 - - - -#======================================================================# -# # -# Checking user settings # -# # -#======================================================================# - -# suppresses all non-gcc output, useful for calling scripts -SILENT = 0 - -# always allow cleaning without errors or warnings -ifneq ($(MAKECMDGOALS), clean) -ifneq ($(MAKECMDGOALS), veryclean) -ifneq ($(SILENT), 1) - - # check $COMPILER_TYPE is correct - ifneq ($(COMPILER_TYPE), CLANG) - ifneq ($(COMPILER_TYPE), GNU) - ifneq ($(COMPILER_TYPE), INTEL) - $(error COMPILER_TYPE must be one of CLANG, GNU or INTEL) - endif - endif - endif - - # distributed GPU not supported - ifeq ($(DISTRIBUTED), 1) - ifeq ($(GPUACCELERATED), 1) - $(error Distributed GPU acceleration not supported) - endif - endif - - # GPU doesn't use threading - ifeq ($(MULTITHREADED), 1) - ifeq ($(GPUACCELERATED), 1) - $(warning GPU acceleration makes no use of multithreading. Disabling the latter...) - override MULTITHREADED = 0 - endif - endif - - # CLANG compilers don't support threading - ifeq ($(MULTITHREADED), 1) - ifeq ($(COMPILER_TYPE), CLANG) - $(warning Clang does not support multithreading. Disabling...) - override MULTITHREADED = 0 - endif - endif - - # check PRECISION is valid - ifneq ($(PRECISION), 1) - ifneq ($(PRECISION), 2) - ifneq ($(PRECISION), 4) - $(error PRECISION must be set to 1, 2 or 4) - endif - endif - endif - - # GPU does not support quad precision - ifeq ($(PRECISION), 4) - ifeq ($(GPUACCELERATED), 1) - $(warning GPUs do not support quad precision. Setting PRECISION=2...) - override PRECISION = 2 - endif - endif - - # NVCC doesn't support new CLANG compilers - ifeq ($(GPUACCELERATED), 1) - ifeq ($(COMPILER_TYPE), CLANG) - ifeq ($(SUPPRESS_WARNING), 0) - $(info Some versions of Clang are not NVIDIA-GPU compatible. If compilation fails, try Clang 3.7) - endif - endif - endif - - # NVCC doesn't support GNU compilers on OSX - ifeq ($(GPUACCELERATED), 1) - ifeq ($(COMPILER_TYPE), GNU) - ifeq ($(SUPPRESS_WARNING), 0) - $(info On some platforms (e.g. OSX), NVIDIA-GPUs are not compatible with GNU compilers. If compilation fails, try an alternative compiler, like Clang 3.7) - endif - endif - endif - -# end of allowed cleaning -endif -endif -endif - - -#======================================================================# -# # -# Compilation # -# # -#======================================================================# - -# -# --- libraries -# - -LIBS = -lm - -QUEST_COMMON_DIR = $(QUEST_DIR) -ifeq ($(GPUACCELERATED), 1) - QUEST_INNER_DIR = $(QUEST_DIR)/GPU -else - QUEST_INNER_DIR = $(QUEST_DIR)/CPU -endif -QUEST_INCLUDE = -I${QUEST_INCLUDE_DIR} -I$(QUEST_INNER_DIR) -I$(QUEST_COMMON_DIR) - - - -# -# --- wrapper compilers -# - -CUDA_COMPILER = nvcc -MPI_COMPILER = mpicc - - - -# -# --- compiler flags -# - -# note: -# several flag names depend not just on the compiler type, but also compiler version -# the user should update these below. For example: -# -# - GNU C++ compilers of version < 4.7 use -std=c++0x instead of -std=c++11 -# - INTEL compilers of version < ? use -openmp instead of -qopenmp -# - INTEL compilers of version < ? won't recognise -diad-disable and -cpu-dispatch -# - CLANG compilers don't support openmp (threading) at all - - -# threading flag -ifeq ($(MULTITHREADED), 1) - ifeq ($(COMPILER_TYPE), GNU) - THREAD_FLAGS = -fopenmp - else ifeq ($(COMPILER_TYPE), INTEL) - THREAD_FLAGS = -qopenmp - endif -else - THREAD_FLAGS = -endif - -# c -C_CLANG_FLAGS = -O2 -std=c99 -mavx -Wall -DQuEST_PREC=$(PRECISION) -C_GNU_FLAGS = -O2 -std=c99 -mavx -Wall -DQuEST_PREC=$(PRECISION) $(THREAD_FLAGS) -fpic -C_INTEL_FLAGS = -O2 -std=c99 -fprotect-parens -Wall -xAVX -axCORE-AVX2 -diag-disable -cpu-dispatch -DQuEST_PREC=$(PRECISION) $(THREAD_FLAGS) - -# c++ -CPP_CLANG_FLAGS = -O2 -std=c++11 -mavx -Wall -DQuEST_PREC=$(PRECISION) -CPP_GNU_FLAGS = -O2 -std=c++11 -mavx -Wall -DQuEST_PREC=$(PRECISION) $(THREAD_FLAGS) -CPP_INTEL_FLAGS = -O2 -std=c++11 -fprotect-parens -Wall -xAVX -axCORE-AVX2 -diag-disable -cpu-dispatch -DQuEST_PREC=$(PRECISION) $(THREAD_FLAGS) - -# wrappers -CPP_CUDA_FLAGS = -O2 -arch=compute_$(GPU_COMPUTE_CAPABILITY) -code=sm_$(GPU_COMPUTE_CAPABILITY) -DQuEST_PREC=$(PRECISION) -ccbin $(COMPILER) - -# choose c/c++ flags based on compiler type -ifeq ($(COMPILER_TYPE), CLANG) - C_FLAGS = $(C_CLANG_FLAGS) - CPP_FLAGS = $(CPP_CLANG_FLAGS) -else ifeq ($(COMPILER_TYPE), GNU) - C_FLAGS = $(C_GNU_FLAGS) - CPP_FLAGS = $(CPP_GNU_FLAGS) -else ifeq ($(COMPILER_TYPE), INTEL) - C_FLAGS = $(C_INTEL_FLAGS) - CPP_FLAGS = $(CPP_INTEL_FLAGS) -endif - - - -# -# --- compiler environment vars -# - -MPI_WRAPPED_COMP = I_MPI_CC=$(COMPILER) OMPI_CC=$(COMPILER) MPICH_CC=$(COMPILER) - - - -# -# --- targets -# - -OBJ = QuEST.o QuEST_validation.o QuEST_common.o QuEST_qasm.o mt19937ar.o -ifeq ($(GPUACCELERATED), 1) - OBJ += QuEST_gpu.o -else ifeq ($(DISTRIBUTED), 1) - OBJ += QuEST_cpu.o QuEST_cpu_distributed.o -else - OBJ += QuEST_cpu.o QuEST_cpu_local.o -endif -OBJ += $(addsuffix .o, $(SOURCES)) - - - -# -# --- rules -# - -# notes: -# - if $SOURCES appear as both c and c++ files, the c files will be compiled -# - CUDA won't compile .c files ($COMPILER will), only .cpp and .cu -# - MPICC will compile .c and .cpp files (wrapping $COMPILER) - - -# GPU -ifeq ($(GPUACCELERATED), 1) - - %.o: %.c - $(COMPILER) -x c $(C_FLAGS) $(QUEST_INCLUDE) -c $< - %.o: $(QUEST_INNER_DIR)/%.c - $(COMPILER) -x c $(C_FLAGS) -c $< - %.o: $(QUEST_COMMON_DIR)/%.c - $(COMPILER) -x c $(C_FLAGS) -c $< - - %.o: %.cu - $(CUDA_COMPILER) -dc $(CPP_CUDA_FLAGS) $(QUEST_INCLUDE) $< - %.o: $(QUEST_INNER_DIR)/%.cu - $(CUDA_COMPILER) -dc $(CPP_CUDA_FLAGS) $< - - %.o: %.cpp - $(CUDA_COMPILER) -dc $(CPP_CUDA_FLAGS) $(QUEST_INCLUDE) $< - %.o: $(QUEST_INNER_DIR)/%.cpp - $(CUDA_COMPILER) -dc $(CPP_CUDA_FLAGS) $< - -# distributed -else ifeq ($(DISTRIBUTED), 1) - - %.o: %.c - $(MPI_WRAPPED_COMP) $(MPI_COMPILER) -x c $(C_FLAGS) $(QUEST_INCLUDE) -c $< - %.o: $(QUEST_INNER_DIR)/%.c - $(MPI_WRAPPED_COMP) $(MPI_COMPILER) -x c $(C_FLAGS) $(QUEST_INCLUDE) -c $< - %.o: $(QUEST_COMMON_DIR)/%.c - $(MPI_WRAPPED_COMP) $(MPI_COMPILER) -x c $(C_FLAGS) $(QUEST_INCLUDE) -c $< - - %.o: %.cpp - $(MPI_WRAPPED_COMP) $(MPI_COMPILER) $(CPP_FLAGS) $(QUEST_INCLUDE) -c $< - %.o: $(QUEST_INNER_DIR)/%.cpp - $(MPI_WRAPPED_COMP) $(MPI_COMPILER) $(CPP_FLAGS) $(QUEST_INCLUDE) -c $< - -# CPU -else - - %.o: %.c - $(COMPILER) -x c $(C_FLAGS) $(QUEST_INCLUDE) -c $< - %.o: $(QUEST_INNER_DIR)/%.c - $(COMPILER) -x c $(C_FLAGS) $(QUEST_INCLUDE) -c $< - %.o: $(QUEST_COMMON_DIR)/%.c - $(COMPILER) -x c $(C_FLAGS) $(QUEST_INCLUDE) -c $< - - %.o: %.cpp - $(COMPILER) $(CPP_FLAGS) $(QUEST_INCLUDE) -c $< - %.o: $(QUEST_INNER_DIR)/%.cpp - $(COMPILER) $(CPP_FLAGS) -c $< - -endif - - - -# -# --- build -# - -# CUDA -ifeq ($(GPUACCELERATED), 1) - - all: $(OBJ) - $(CUDA_COMPILER) $(CPP_CUDA_FLAGS) $(QUEST_INCLUDE) -o $(EXE) $(OBJ) $(LIBS) - -# MPI -else ifeq ($(DISTRIBUTED), 1) - - default: $(EXE) - $(EXE): $(OBJ) - $(MPI_WRAPPED_COMP) $(MPI_COMPILER) $(C_FLAGS) -shared $(QUEST_INCLUDE) -o libQuEST.so $(OBJ) $(LIBS) - -# C -else - - default: $(EXE) - $(EXE): $(OBJ) - $(COMPILER) $(C_FLAGS) -shared $(QUEST_INCLUDE) -o libQuEST.so $(OBJ) $(LIBS) - # default: $(EXE) - # $(EXE): $(OBJ) - # $(COMPILER) $(C_FLAGS) $(QUEST_INCLUDE) -o libQuEST.so $(OBJ) $(LIBS) - -endif - - - -# -# --- clean -# - -.PHONY: clean veryclean -clean: - /bin/rm -f *.o $(EXE) -veryclean: clean - /bin/rm -f *.h~ *.c~ makefile~ - - - -# -# --- debug -# - -print-%: - @echo $*=$($*) - -getvalue-%: - @echo $($*) - - diff --git a/setup.py b/setup.py index f0d3181..a5d1077 100644 --- a/setup.py +++ b/setup.py @@ -17,9 +17,11 @@ from setuptools.command.build_ext import build_ext from setuptools.command import build_py import subprocess +import platform import os path = os.path.dirname(os.path.abspath(__file__)) + class CustomExtension(Extension): """Custom Extension class""" @@ -34,24 +36,30 @@ class CustomBuild(build_ext): def run(self) -> None: """Run custom build function""" + # try: + # subprocess.run(['make', '--version'], check=True) + # except OSError: + # raise RuntimeError( + # "Make must be installed to build pyquest" + # ) try: - subprocess.run(['make', '--version'], check=True) + subprocess.run(['wget', '--version'], check=True) except OSError: raise RuntimeError( - "Make must be installed to build the following extensions: " - + ", ".join(e.name for e in self.extensions)) + "Wget must be installed to build pyquest " + ) try: - subprocess.run(['wget', '--version'], check=True) + subprocess.run(['tar', '--version'], check=True) except OSError: raise RuntimeError( - "Wget must be installed to build the following extensions: " - + ", ".join(e.name for e in self.extensions)) + "Tar must be installed to build pyquest: " + ) try: - subprocess.run(['tar', '--version'], check=True) + subprocess.run(['cmake', '--version'], check=True) except OSError: raise RuntimeError( - "Tar must be installed to build the following extensions: " - + ", ".join(e.name for e in self.extensions)) + "Cmake must be installed to build pyquest" + ) for ext in self.extensions: self.build_extension(ext) @@ -83,11 +91,20 @@ def build_extension(self, ext: Extension) -> None: os.path.join(src_path, 'QuEST/'), '--strip-components=1'], check=True) + # Switch off Multithreading on macos because of openmp problems + if platform.system() == 'Darwin': + args_for_cmake = ['-DMULTITHREADED=0'] + else: + args_for_cmake = [] + os.chdir(os.path.join(src_path, 'pyquest_cffi/questlib/')) - subprocess.run(['make'], check=True) - #subprocess.run(['python', 'build_quest.py'], check=True) + run_command_make = ['cmake', os.path.join(src_path, 'QuEST/QuEST/')] + args_for_cmake + run_command_build = ['cmake', '--build', '.'] + subprocess.run(run_command_make, check=True) + subprocess.run(run_command_build, check=True) from build_quest import build_quest_so build_quest_so() + print(os.listdir('.')) os.chdir(old_path) @@ -102,7 +119,7 @@ def run(self): def setup_packages(): """Setup method""" - with open(os.path.join(path,'README.md')) as file: + with open(os.path.join(path, 'README.md')) as file: readme = file.read() with open(os.path.join('LICENSE')) as file: @@ -121,20 +138,20 @@ def setup_packages(): + ' to QuEST quantum simulation toolkit;' + ' Compile functionality, create, build and import' + ' valid QuEST source code from python'), - 'version': '3.2.0', + 'version': '3.2.1', 'long_description': readme, 'packages': packages, # 'package_dir': {'': 'pyquest_cffi'}, 'author': 'HQS Quantum Simulations: Sebastian Zanker, Nicolas Vogt', 'author_email': 'info@quantumsimulations.de', 'url': '', - 'download_url': '', + 'download_url': 'https://github.com/HQSquantumsimulations/PyQuEST-cffi/archive/3.2.0.tar.gz', 'license': License, 'install_requires': install_requires, 'setup_requires': ['cffi'], - #'include_package_data': True, - 'package_data': {'pyquest_cffi': ['questlib/*', 'questlib/*.so']}, - 'data_files': [("", ["LICENSE", "pyquest_cffi/questlib/makefile"])], + # 'include_package_data': True, + 'package_data': {'pyquest_cffi': ['questlib/*', 'questlib/*.so', 'questlib/*.dylib']}, + 'data_files': [("", ["LICENSE"])], # add custom build_ext command 'cmdclass': {'build_ext': CustomBuild, 'build_py': BuildPyCommand},