cmake_minimum_required(VERSION 3.3)
project(knapsack-local-cuts)

### PARAMETERS

set(DOWNLOAD_ISPC ON CACHE BOOL "Download a local copy of Intel's ISPC")
set(USE_GPU_ORACLE OFF CACHE BOOL "Use a GPU oracle.")
set(USE_HYBRID_ENUMERATION_ORACLE OFF CACHE BOOL "Use brute-force CPU enumeration oracle.")
set(ENUMERATION_THRESHOLD 8 CACHE STRING "Threshold for switching between dynamic programming and enumeration oracle")
set(CUDA_TARGET "86")
set(USE_FW_SEPA ON CACHE BOOL "Use a Frank-Wolfe idea for separation.")
set(USE_AVX512 OFF CACHE BOOL "Use AVX512 instructions for enumeration oracle.")

### ISPC TARGETS

if(USE_AVX512)
    set(ISPC_TARGET "avx512skx-i32x16" CACHE STRING "Target architecture for ISPC")
    set(GANGSIZE 16)
else()
    set(ISPC_TARGET "avx2-i32x8" CACHE STRING "Target architecture for ISPC")
    set(GANGSIZE 8)
endif()

### END PARAMETERS

if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release)
endif()

string(APPEND CMAKE_CXX_FLAGS " -march=native -Wfatal-errors -fopenmp")
include(CTest)
find_package(SCIP REQUIRED)
include_directories(${SCIP_INCLUDE_DIRS})
include_directories(../external)
include_directories(../external/cuda-samples)

### ISPC
# in case ispc is already pre-installed
set(ISPC_PATH "ispc")
if(DOWNLOAD_ISPC)
    message("Downloading ISPC...")
    file(DOWNLOAD "https://github.com/ispc/ispc/releases/download/v1.16.0/ispc-v1.16.0-linux.tar.gz" ${CMAKE_BINARY_DIR}/ispc.tar.gz)
    make_directory(${CMAKE_BINARY_DIR}/ispc)
    execute_process(
        COMMAND tar xzf ${CMAKE_BINARY_DIR}/ispc.tar.gz --strip 1
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/ispc)
    set(ISPC_PATH ${CMAKE_BINARY_DIR}/ispc/bin/ispc)
else()
    set(ISPC_PATH ispc)
endif()

if(USE_FW_SEPA)
    add_definitions(-DUSE_FW_SEPA)
endif(USE_FW_SEPA)

if(USE_GPU_ORACLE)
 
    add_definitions(-DGPU_ORACLE)  
    enable_language(CUDA)
    if(NOT DEFINED CMAKE_CUDA_STANDARD)
        set(CMAKE_CUDA_STANDARD 14)
        set(CMAKE_CUDA_STANDARD_REQUIRED ON)
    endif()

    string(APPEND CMAKE_CUDA_FLAGS " -gencode=arch=compute_${CUDA_TARGET},code=sm_${CUDA_TARGET} -Xcompiler -fPIC")
    
    add_library(oracle SHARED 
        src/oracle/lmo.cpp
        src/oracle/lmo_api.cpp
        src/oracle/gpu/enumeration.cu)
        
    target_link_libraries(oracle PUBLIC ${SCIP_LIBRARIES} ${CUDA_LIBRARIES})
    set_target_properties(oracle PROPERTIES CUDA_ARCHITECTURES "${CUDA_TARGET}")
else()
    add_custom_command(
        OUTPUT ispc.h ispc.o
        COMMAND bash -c "${ISPC_PATH} -O3 --pic --target=${ISPC_TARGET} ${CMAKE_SOURCE_DIR}/src/oracle/enum_oracle.ispc -o ispc.o -h ispc.h"
        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
    )
    
    add_library(oracle SHARED 
        src/oracle/lmo.cpp
        src/oracle/lmo_api.cpp
        ispc.o)
    
    add_definitions(-DGANGSIZE=${GANGSIZE})
    target_link_libraries(oracle PUBLIC ${SCIP_LIBRARIES})
    target_include_directories(oracle PUBLIC ${CMAKE_BINARY_DIR})

    if(USE_HYBRID_ENUMERATION_ORACLE)
        add_definitions(-DHYBRID_ORACLE)
        add_definitions(-DORACLE_THRESHOLD=${ENUMERATION_THRESHOLD})
    endif(USE_HYBRID_ENUMERATION_ORACLE)


endif(USE_GPU_ORACLE)

add_executable(knapsack
   src/cmain.c
   src/handle_localcuts.c
   src/sepa_localcuts.c
   src/timer.cpp
   src/lc_clock.c
   src/scalar-fw/fwknapcut.cpp)

target_compile_definitions(knapsack PRIVATE SCIP_CHECK=1)

add_custom_target(doc
    COMMAND /bin/bash -c \"cd ${PROJECT_SOURCE_DIR}/doc && doxygen knapsack.dxy && cp ${PROJECT_SOURCE_DIR}/doc/*.png ${PROJECT_BINARY_DIR}/doc && cp ${PROJECT_SOURCE_DIR}/doc/*.png ${PROJECT_BINARY_DIR}/doc\"
    DEPENDS ${PROJECT_SOURCE_DIR}/doc/knapsack.dxy)   

target_link_libraries(knapsack PUBLIC
    oracle
    ${SCIP_LIBRARIES}
    m)
    
enable_testing()

add_executable(test_oracle src/oracle/test_oracles.cpp)
target_link_libraries(test_oracle PUBLIC oracle)
