diff --git a/@osqp/build.m b/@osqp/build.m index c601a9c..0884518 100644 --- a/@osqp/build.m +++ b/@osqp/build.m @@ -7,6 +7,7 @@ function build(varargin) % empty: Build osqp_mex interface % 'all': Build all interfaces % 'osqp_mex': Builds the OSQP mex interface +% 'osqp_cuda': Builds the OSQP mex interface with the CUDA backend % 'clean': Delete all compiled files % 'purge': Delete all compiled files and copied code generation files % @@ -15,26 +16,24 @@ function build(varargin) p = inputParser; - addOptional( p, 'target', 'osqp_mex' ); - addOptional( p, 'verbose', false ); + targets = {'all', 'osqp_mex', 'osqp_cuda', 'clean', 'purge'}; + + addOptional( p, 'target', 'osqp_mex', @(s) length( validatestring(s, targets) > 0 ) ); + addParameter( p, 'verbose', false ); + addParameter( p, 'toolchain', 'default', @isstring ) parse( p, varargin{:} ); target = p.Results.target; verbose = p.Results.verbose; - - if(isempty(strfind(target, 'all')) && ... - isempty(strfind(target, 'osqp_mex')) && ... - isempty(strfind(target, 'clean')) && ... - isempty(strfind(target, 'purge'))) - fprintf('No rule to make target "%s", exiting.\n', target); - end + cuda_toolchain = p.Results.toolchain; %% Determine where the various files are all located % Various parts of the build system [osqp_classpath,~,~] = fileparts( mfilename( 'fullpath' ) ); osqp_mex_src_dir = fullfile( osqp_classpath, '..', 'c_sources' ); osqp_mex_build_dir = fullfile( osqp_mex_src_dir, 'build' ); + osqp_mex_cuda_build_dir = fullfile( osqp_mex_src_dir, 'build_cuda' ); osqp_cg_src_dir = fullfile( osqp_mex_build_dir, 'codegen_src' ); osqp_cg_dest_dir = fullfile( osqp_classpath, '..', 'codegen', 'sources' ); @@ -125,9 +124,108 @@ function build(varargin) fprintf( '\t\t\t\t\t\t[done]\n' ); end + %% Configure, build and install the OSQP CUDA mex interface + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if( any(strcmpi(target, 'osqp_cuda')) || any(strcmpi(target, 'all')) ) + fprintf('Compiling OSQP CUDA solver mex interface...\n'); + + % Create build for the mex file and go inside + if exist( osqp_mex_cuda_build_dir, 'dir' ) + rmdir( osqp_mex_cuda_build_dir, 's' ); + end + mkdir( osqp_mex_cuda_build_dir ); + + % Extend path for CMake mac (via Homebrew) + PATH = getenv('PATH'); + if( (ismac) && (isempty(strfind(PATH, '/usr/local/bin'))) ) + setenv('PATH', [PATH ':/usr/local/bin']); + end + + %% Figure out the CUDA toolkit and compiler location + if( any(strcmpi(cuda_toolchain, 'default')) || any(strcmpi(target, 'builtin')) ) + + if( exist('mexcuda') ) + cmd = "mexcuda -n -v NVCCFLAGS='$NVCCFLAGS -allow-unsupported-compiler' " + osqp_mex_src_dir + filesep + "configure" + filesep + "config.cu"; + x = evalc( cmd ); + + [tok, match] = regexp(x, 'CUDA_ROOT : ([^\n])*', 'tokens', 'match'); + cuda_root = tok{1}{1}; + + [tok, match] = regexp(x, 'CUDA_LIBS : ([^\n])*', 'tokens', 'match'); + cuda_libs = tok{1}{1}; + + if( verbose ) + fprintf( ' Found CUDA_ROOT=%s\n', cuda_root ); + fprintf( ' Found CUDA_LIBS=%s\n', cuda_libs ); + end + elseif( strcmpi(target, 'builtin') ) + error( 'Unable to locate the builtin MATLAB CUDA toolchain' ); + elseif( verbose ) + fprintf( ' Using CMake CUDA toolchain detection\n' ); + end + else + cuda_root = cuda_toolchain; + + if( verbose ) + fprintf( '\n' ); + fprintf( ' Using CUDA_ROOT=%s\n', cuda_root ); + end + end + + %% Configure CMake for the mex interface + fprintf(' Configuring...' ) + [status, output] = system( sprintf( 'cmake -B %s -S %s -DCMAKE_BUILD_TYPE=RelWithDebInfo -DMATLAB_CUDA=ON -DMATLAB_CUDA_ROOT=\"%s\" -DMATLAB_CUDA_LIBS=\"%s\" -DMatlab_ROOT_DIR=\"%s\"', ... + osqp_mex_cuda_build_dir, ... + osqp_mex_src_dir, ... + cuda_root, ... + cuda_libs, ... + Matlab_ROOT ), 'LD_LIBRARY_PATH', '' ); + if( status ) + fprintf( '\n' ); + disp( output ); + error( 'Error configuring CMake environment' ); + elseif( verbose ) + fprintf( '\n' ); + disp( output ); + else + fprintf( '\t\t\t\t\t[done]\n' ); + end + + %% Build the mex interface + fprintf( ' Building...') + [status, output] = system( sprintf( 'cmake --build %s --config Release', osqp_mex_cuda_build_dir ), 'LD_LIBRARY_PATH', '' ); + if( status ) + fprintf( '\n' ); + disp( output ); + error( 'Error compiling OSQP mex interface' ); + elseif( verbose ) + fprintf( '\n' ); + disp( output ); + else + fprintf( '\t\t\t\t\t\t[done]\n' ); + end + + %% Install various files + fprintf( ' Installing...' ) + + % Copy mex file to root directory for use + if( ispc ) + [err, errmsg, ~] = copyfile( [osqp_mex_cuda_build_dir, filesep, 'Release', filesep, 'osqp_mex.mex*'], [osqp_classpath, filesep, 'private'] ); + else + [err, errmsg, ~] = copyfile( [osqp_mex_cuda_build_dir, filesep, 'osqp_mex.mex*'], [osqp_classpath, filesep, 'private'] ); + end + if( ~err ) + fprintf( '\n' ) + disp( errmsg ) + error( ' Error copying mex file' ) + end + + fprintf( '\t\t\t\t\t\t[done]\n' ); + end + %% Clean and purge %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - if( any(strcmpi(target, 'clean')) || any(strcmpi(wtargethat, 'purge')) ) + if( any(strcmpi(target, 'clean')) || any(strcmpi(target, 'purge')) ) fprintf('Cleaning OSQP mex files and build directory...'); % Delete mex file @@ -140,7 +238,9 @@ function build(varargin) if exist(osqp_mex_build_dir, 'dir') rmdir(osqp_mex_build_dir, 's'); end - + if exist(osqp_mex_cuda_build_dir, 'dir') + rmdir(osqp_mex_cuda_build_dir, 's'); + end fprintf('\t\t[done]\n'); diff --git a/c_sources/CMakeLists.txt b/c_sources/CMakeLists.txt index 5c42782..514a93b 100644 --- a/c_sources/CMakeLists.txt +++ b/c_sources/CMakeLists.txt @@ -14,6 +14,9 @@ include_directories( ${Matlab_INCLUDE_DIRS} ) set( CMAKE_CXX_STANDARD 11 ) set( CMAKE_CXX_STANDARD_REQUIRED ON ) +# Some options +option( MATLAB_CUDA "Build CUDA backend" OFF ) + if( CMAKE_COMPILER_IS_GNUCXX ) # Add debug symbols and optimizations to the build set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O2" ) @@ -21,7 +24,7 @@ if( CMAKE_COMPILER_IS_GNUCXX ) endif() # Insist on the pre-2018 complex data API so that mxGetPr will work correctly - add_compile_definitions( MATLAB_MEXSRC_RELEASE=R2017b ) +add_compile_definitions( MATLAB_MEXSRC_RELEASE=R2017b ) message( STATUS "Using Matlab pre-2018a API for mxGetPr compatibility" ) # Some parts of the main libraries need to know we are building for MATLAB @@ -29,19 +32,33 @@ message( STATUS "Using Matlab pre-2018a API for mxGetPr compatibility" ) add_compile_definitions( MATLAB ) # Configure the settings of the OSQP library -set( OSQP_ALGEBRA_BACKEND "builtin" ) -set( OSQP_USE_FLOAT OFF ) -set( OSQP_CODEGEN ON ) -set( OSQP_ENABLE_DERIVATIVES ON ) -set( OSQP_ENABLE_PRINTING ON ) -set( OSQP_ENABLE_PROFILING ON ) -set( OSQP_ENABLE_INTERRUPT ON ) +if( MATLAB_CUDA ) + message( STATUS "Building CUDA-based mex file" ) + set( OSQP_ALGEBRA_BACKEND "cuda" CACHE STRING "Build CUDA mex file" ) + set( OSQP_CODEGEN OFF CACHE BOOL "Disable codegen" ) + set( OSQP_ENABLE_DERIVATIVES OFF CACHE BOOL "Disable derivatives" ) + + # Give CMake hints for where to find libraries + set( CUDAToolkit_ROOT "${MATLAB_CUDA_ROOT}" CACHE STRING "CUDA Toolkit root directory" ) + set( CUDA_TOOLKIT_ROOT_DIR "${MATLAB_CUDA_ROOT}" CACHE STRING "CUDA Toolkit root directory" ) + set( CUDA_HOME "${MATLAB_CUDA_ROOT}" CACHE STRING "CUDA Toolkit root directory" ) +else() + message( STATUS "Building builtin algebramex file") + set( OSQP_ALGEBRA_BACKEND "builtin" CACHE STRING "Build builtin algebra") + set( OSQP_CODEGEN ON CACHE BOOL "Enable codegen" ) + set( OSQP_ENABLE_DERIVATIVES ON CACHE BOOL "Enable derivatives" ) +endif() + +set( OSQP_USE_FLOAT OFF CACHE BOOL "Use doubles" ) +set( OSQP_ENABLE_PRINTING ON CACHE BOOL "Enable printing" ) +set( OSQP_ENABLE_PROFILING ON CACHE BOOL "Enable profiling" ) +set( OSQP_ENABLE_INTERRUPT ON CACHE BOOL "Enable interrupt" ) # Configure the build outputs for the OSQP library -set( OSQP_BUILD_STATIC_LIB ON ) -set( OSQP_BUILD_SHARED_LIB OFF ) -set( OSQP_BUILD_UNITTESTS OFF ) -set( OSQP_BUILD_DEMO_EXE OFF ) +set( OSQP_BUILD_STATIC_LIB ON CACHE BOOL "Build static library" ) +set( OSQP_BUILD_SHARED_LIB OFF CACHE BOOL "Disable shared library" ) +set( OSQP_BUILD_UNITTESTS OFF CACHE BOOL "Disable unit tests" ) +set( OSQP_BUILD_DEMO_EXE OFF CACHE BOOL "Disable demo executable" ) # Add the custom MATLAB memory handling set( OSQP_CUSTOM_MEMORY "memory_matlab.h" ) @@ -63,6 +80,12 @@ else() set( UT_LIBRARY "-L${Matlab_LIB_DIR} -lut" ) endif() +if( MATLAB_CUDA ) + set( ADDITIONAL_LIBS "-L${MATLAB_CUDA_LIBS}" ) +else() + set( ADDITIONAL_LIBS "") +endif() + matlab_add_mex( NAME osqp_mex SRC ${CMAKE_CURRENT_SOURCE_DIR}/osqp_mex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/interrupt_matlab.c @@ -71,5 +94,6 @@ matlab_add_mex( NAME osqp_mex ${CMAKE_CURRENT_SOURCE_DIR}/osqp_struct_settings.cpp LINK_TO osqpstatic ${UT_LIBRARY} + ${ADDITIONAL_LIBS} # Force compilation in the traditional C API (equivalent to the -R2017b flag) R2017b ) \ No newline at end of file diff --git a/c_sources/configure/config.cu b/c_sources/configure/config.cu new file mode 100644 index 0000000..163b9d8 --- /dev/null +++ b/c_sources/configure/config.cu @@ -0,0 +1 @@ +#include \ No newline at end of file