From b0f34a2e961055fc8d5caf9657d9f92e6227660f Mon Sep 17 00:00:00 2001 From: Pablo Hernandez-Cerdan Date: Sun, 24 Sep 2017 18:30:02 +1300 Subject: [PATCH 01/13] Add cmake support for the c code. Add dummy test checking if linking works properly. Add FindLAPACKE.cmake and export proxTVconfig.cmake. FindLAPACKE.cmake is used to handle lapacke installations with no export targets. Also complete the generation of proxTVconfig.cmake Add a dummy project to test usage of proxTV from external project. Add C Interface to Readme Remove Foo_FOUND in the case FOO is REQUIRED Update cmake required version to 3.5 This simplifies policy and version set up. Require a modern lapacke and remove FIND_LAPACKE This will look for a `lapacke-config.cmake`, that is generated since lapacke version 3.6. And allow us to remove the buggy FIND_LAPACKE. Add test for cmake in travis. Restore FindLAPACKE.cmake Because travis still uses ubuntu 14.04, with an old lapacke which does not provide config.cmake. Fix CMake Added FindLAPACK FindLAPACKE CONFIG is not populating LAPACK. From @jcfr branch: https://github.com/jcfr/proxTV/tree/cmake_support_squashed Co-authored-by: Jean-Christophe Fillion-Robin WIP: Avoid deploy in .travis Also install cmake with pip (3.12) Also use find_package(LAPACKE NOCONFIG) for old librart versions Error: ``` +twine upload --repository-url https://test.pypi.org/legacy/ --username albarji dist/prox_tv-3.3.0-cp27-cp27mu-manylinux1_x86_64.whl Enter your password: No output has been received in the last 10m0s, this potentially indicates a stalled build or something wrong with the build itself. Check the details on how to adjust your build configuration on: https://docs.travis-ci.com/user/common-build-problems/#Build-times-out-because-no-output-was-received ``` --- .travis.yml | 7 +- .travis/installconda.sh | 1 + CMakeLists.txt | 119 ++++++++++++++ README.md | 27 ++++ cmake/FindLAPACKE.cmake | 190 +++++++++++++++++++++++ cmake/README.md | 7 + cmake/proxTVConfig.cmake.in | 13 ++ src/CMakeLists.txt | 91 +++++++++++ test/CMakeLists.txt | 21 +++ test/project_using_proxTV/CMakeLists.txt | 6 + test/project_using_proxTV/README.md | 12 ++ test/project_using_proxTV/dummy.cpp | 26 ++++ test/test_tv1_2d.cpp | 26 ++++ 13 files changed, 545 insertions(+), 1 deletion(-) create mode 100644 CMakeLists.txt create mode 100644 cmake/FindLAPACKE.cmake create mode 100644 cmake/README.md create mode 100644 cmake/proxTVConfig.cmake.in create mode 100644 src/CMakeLists.txt create mode 100644 test/CMakeLists.txt create mode 100644 test/project_using_proxTV/CMakeLists.txt create mode 100644 test/project_using_proxTV/README.md create mode 100644 test/project_using_proxTV/dummy.cpp create mode 100644 test/test_tv1_2d.cpp diff --git a/.travis.yml b/.travis.yml index c0c1fdc..6192bd7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,7 +53,12 @@ install: script: - source activate testenv - nosetests --with-coverage --cover-package=prox_tv -- bash -x ./.travis/deploy.sh +# - bash -x ./.travis/deploy.sh +# Build C lib with CMake +- mkdir -p build && cd build +- cmake -DENABLE_TESTING:BOOL=ON ../ +- cmake --build . +- ctest -VV after_success: - source activate testenv - coveralls diff --git a/.travis/installconda.sh b/.travis/installconda.sh index dcb9ede..20a9314 100644 --- a/.travis/installconda.sh +++ b/.travis/installconda.sh @@ -22,6 +22,7 @@ export PATH="$HOME/miniconda3/bin:$PATH" rm miniconda.sh conda config --set always_yes yes --set changeps1 no conda update -q conda +pip install cmake # Create environment with specific python version conda create -n testenv python=${TRAVIS_PYTHON_VERSION} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..086d985 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,119 @@ +cmake_minimum_required(VERSION 3.12) + +project(proxTV + VERSION 3.2.1 + DESCRIPTION "Toolbox for fast Total Variation proximity operators" + ) +message(STATUS "Configuring ${PROJECT_NAME}") +message(STATUS " version: ${proxTV_VERSION}") +message(STATUS " description: ${proxTV_DESCRIPTION}") + +# Update CMake module path to lookup proxTV custom CMake modules +list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +#------------------------------------------------------------------------------ +option(proxTV_INSTALL_DEVELOPMENT "Install development files" ON) +option(ENABLE_TESTING "Compile tests" OFF) + +#------------------------------------------------------------------------------ +# Set a default build type if none was specified +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to 'Release' as none was specified.") + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE) + mark_as_advanced(CMAKE_BUILD_TYPE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") +endif() + +#------------------------------------------------------------------------------ +# Install directories +if(NOT DEFINED proxTV_INSTALL_BIN_DIR) + set(proxTV_INSTALL_BIN_DIR bin) +endif() +if(NOT DEFINED proxTV_INSTALL_LIB_DIR) + set(proxTV_INSTALL_LIB_DIR lib) +endif() +if(NOT DEFINED proxTV_INSTALL_INCLUDE_DIR) + set(proxTV_INSTALL_INCLUDE_DIR include) +endif() + +#------------------------------------------------------------------------------ +# External dependencies + +find_package(LAPACKE REQUIRED) +message(STATUS "Lapacke libraries: ${LAPACKE_LIBRARIES}") + +find_package(LAPACK REQUIRED) +message(STATUS "Lapack libraries: ${LAPACK_LIBRARIES}") + +set(THREADS_PREFER_PTHREAD_FLAG 1) +find_package(Threads) + +find_package(OpenMP) +message(STATUS "OpenMP found: ${OpenMP_FOUND}") + +#------------------------------------------------------------------------------ +# Add libraries + +add_subdirectory(src) + +#------------------------------------------------------------------------------ +# Testing +if(ENABLE_TESTING) + enable_testing() + add_subdirectory(test) +endif() + +#------------------------------------------------------------------------------ +# Configure proxTVConfigVersion.cmake common to build and install tree +include(CMakePackageConfigHelpers) +set(config_version_file "${proxTV_BINARY_DIR}/proxTVConfigVersion.cmake") +write_basic_package_version_file(${config_version_file} + VERSION ${proxTV_VERSION} + COMPATIBILITY SameMajorVersion + ) + +#------------------------------------------------------------------------------ +# Export 'proxTVTargets.cmake' for a build tree +export(TARGETS proxTV + FILE ${PROJECT_BINARY_DIR}/proxTVTargets.cmake + NAMESPACE proxTV:: + ) + +# Configure 'proxTVConfig.cmake' for a build tree +include(CMakePackageConfigHelpers) +set(build_config ${PROJECT_BINARY_DIR}/proxTVConfig.cmake) +configure_package_config_file( + cmake/proxTVConfig.cmake.in + ${build_config} + INSTALL_DESTINATION ${PROJECT_BINARY_DIR} + INSTALL_PREFIX ${PROJECT_BINARY_DIR} + NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) + +#------------------------------------------------------------------------------ +# Configure 'proxTVConfig.cmake' for an install tree +if(proxTV_INSTALL_DEVELOPMENT) + set(install_config ${PROJECT_BINARY_DIR}/CMakeFiles/proxTVConfig.cmake) + configure_package_config_file( + cmake/proxTVConfig.cmake.in + ${install_config} + INSTALL_DESTINATION ${proxTV_INSTALL_LIB_DIR}/cmake/proxTV + NO_CHECK_REQUIRED_COMPONENTS_MACRO + ) + + # Install 'proxTVTargets.cmake' + install(EXPORT proxTVTargets + FILE proxTVTargets.cmake + NAMESPACE proxTV:: + DESTINATION ${proxTV_INSTALL_LIB_DIR}/cmake/proxTV + COMPONENT Development + ) + + # Install config files + install( + FILES ${config_version_file} ${install_config} + DESTINATION ${proxTV_INSTALL_LIB_DIR}/cmake/proxTV + COMPONENT Development + ) +endif() diff --git a/README.md b/README.md index 6ba6280..dbeca13 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,33 @@ More technically, the library provides efficient solvers for the following Total | Anisotropic Total Variation on a 3-dimensional signal (video denoising) | ![alt tag](docs/img/TV3D.png) | | Generalized N-dimensional Anisotropic Total Variation (tensor denoising) | ![alt tag](docs/img/TVND.png), with X(di) every possible 1-dimensional slice of X following dimension di.| +## C interface + +You can generate a **c** static or dynamic library using **cmake**. If `libproxTV` is not provided by your package-manager, install it from source: + + mkdir proxTV-dev ; cd proxTV-dev + git clone https://github.com/albarji/proxTV proxTV + mkdir build ; cd build + cmake ../proxTV + make -j4 + make install + +The required dependencies are `lapack` and `lapacke`, the c-interface for `lapack`, and optionally, but recommended: `OpenMP` with `pthreads` for multi-threading support. + +You can provide extra options to `cmake` via the command line or a gui (i.e `ccmake`). + + cmake ../proxTV -DBUILD_SHARED_LIBS:BOOL=ON -DCMAKE_INSTALL_PREFIX=/opt/ -DENABLE_TESTING:BOOL=ON + +To use proxTV in your `cmake` project just write in your `CMakeLists.txt`: + + find_package(proxTV) + add_executable(foo main.cpp) + target_link_libraries(foo PUBLIC proxTV::proxTV) + +That will propagate all the dependencies of proxTV to your target. If you haven't installed proxTV in a system folder, you have to point to the installation directory when configuring your project with `cmake`. + + cmake /path/my_project_source_folder -DproxTV_DIR:PATH="/proxTV_install_folder/lib/cmake/proxTV" + ## Python interface ### Install diff --git a/cmake/FindLAPACKE.cmake b/cmake/FindLAPACKE.cmake new file mode 100644 index 0000000..b0e9639 --- /dev/null +++ b/cmake/FindLAPACKE.cmake @@ -0,0 +1,190 @@ +#.rst: +# FindLAPACKE +# ------------- +# +# Find the LAPACKE library +# +# Using LAPACKE: +# +# :: +# +# find_package(LAPACKE REQUIRED) +# include_directories(${LAPACKE_INCLUDE_DIRS}) +# add_executable(foo foo.cc) +# target_link_libraries(foo ${LAPACKE_LIBRARIES}) +# +# This module sets the following variables: +# +# :: +# +# LAPACKE_FOUND - set to true if the library is found +# LAPACKE_INCLUDE_DIRS - list of required include directories +# LAPACKE_LIBRARIES - list of libraries to be linked +# LAPACKE_VERSION_MAJOR - major version number +# LAPACKE_VERSION_MINOR - minor version number +# LAPACKE_VERSION_PATCH - patch version number +# LAPACKE_VERSION_STRING - version number as a string (ex: "0.2.18") + +#============================================================================= +# Copyright 2016 Hans J. Johnson +# +# Distributed under the OSI-approved BSD License (the "License") +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +#============================================================================= +# +set(LAPACKE_SEARCH_PATHS + ${LAPACKE_DIR} + $ENV{LAPACKE_DIR} + $ENV{CMAKE_PREFIX_PATH} + ${CMAKE_PREFIX_PATH} + /usr + /usr/local + /usr/local/opt/lapack ## Mac Homebrew install path + /opt/LAPACKE +) +message(STATUS "LAPACKE_SEARCH_PATHS: ${LAPACKE_SEARCH_PATHS}") + +set(CMAKE_PREFIX_PATH ${LAPACKE_SEARCH_PATHS}) +list(REMOVE_DUPLICATES CMAKE_PREFIX_PATH) + +## First try to find LAPACKE with NO_MODULE, +## As of 20160706 version 0.2.18 there is limited cmake support for LAPACKE +## that is not as complete as this version, if found, use it +## to identify the LAPACKE_VERSION_STRING and improve searching. +find_package(LAPACKE NO_MODULE QUIET) +if(LAPACKE_FOUND) + if(EXISTS ${LAPACKE_DIR}/lapacke-config-version.cmake) + include(${LAPACKE_DIR}/lapacke-config-version.cmake) + set(LAPACKE_VERSION_STRING ${PACKAGE_VERSION}) + unset(PACKAGE_VERSION) # Use cmake conventional naming + endif() + find_package(LAPACK NO_MODULE QUIET) #Require matching versions here! + find_package(BLAS NO_MODULE QUIET) #Require matching versions here! +endif() + +################################################################################################## +### First search for headers +find_path(LAPACKE_CBLAS_INCLUDE_DIR + NAMES cblas.h + PATHS ${LAPACKE_SEARCH_PATHS} + PATH_SUFFIXES include include/lapack) +find_path(LAPACKE_LAPACKE_INCLUDE_DIR + NAMES lapacke.h + PATHS ${LAPACKE_SEARCH_PATHS} + PATH_SUFFIXES include) + +################################################################################################## +### Second, search for libraries +set(PATH_SUFFIXES_LIST + lib64 + lib +) +find_library(LAPACKE_LIB + NAMES lapacke + PATHS ${LAPACKE_SEARCH_PATHS} + PATH_SUFFIXES ${PATH_SUFFIXES_LIST}) +find_library(CBLAS_LIB + NAMES cblas + PATHS ${LAPACKE_SEARCH_PATHS} + PATH_SUFFIXES ${PATH_SUFFIXES_LIST}) +find_library(LAPACK_LIB + NAMES lapack + PATHS ${LAPACKE_SEARCH_PATHS} + PATH_SUFFIXES ${PATH_SUFFIXES_LIST}) +find_library(BLAS_LIB + NAMES blas + PATHS ${LAPACKE_SEARCH_PATHS} + PATH_SUFFIXES ${PATH_SUFFIXES_LIST}) + +## TODO: Get version components +# ------------------------------------------------------------------------ +# Extract version information +# ------------------------------------------------------------------------ + +# WARNING: We may not be able to determine the version of some LAPACKE +set(LAPACKE_VERSION_MAJOR 0) +set(LAPACKE_VERSION_MINOR 0) +set(LAPACKE_VERSION_PATCH 0) +if(LAPACKE_VERSION_STRING) + string(REGEX REPLACE "([0-9]+).([0-9]+).([0-9]+)" "\\1" LAPACKE_VERSION_MAJOR "${LAPACKE_VERSION_STRING}") + string(REGEX REPLACE "([0-9]+).([0-9]+).([0-9]+)" "\\2" LAPACKE_VERSION_MINOR "${LAPACKE_VERSION_STRING}") + string(REGEX REPLACE "([0-9]+).([0-9]+).([0-9]+)" "\\3" LAPACKE_VERSION_PATCH "${LAPACKE_VERSION_STRING}") +endif() + +#====================== +# Checks 'REQUIRED', 'QUIET' and versions. +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LAPACKE FOUND_VAR LAPACKE_FOUND + REQUIRED_VARS LAPACKE_CBLAS_INCLUDE_DIR + LAPACKE_LAPACKE_INCLUDE_DIR + LAPACKE_LIB + LAPACK_LIB + CBLAS_LIB + BLAS_LIB + VERSION_VAR LAPACKE_VERSION_STRING +) + +if (LAPACKE_FOUND) + set(LAPACKE_INCLUDE_DIRS ${LAPACKE_CBLAS_INCLUDE_DIR} ${LAPACKE_CBLAS_INCLUDE_DIR}) + list(REMOVE_DUPLICATES LAPACKE_INCLUDE_DIRS) + if("${CMAKE_C_COMPILER_ID}" MATCHES ".*Clang.*" OR + "${CMAKE_C_COMPILER_ID}" MATCHES ".*GNU.*" OR + "${CMAKE_C_COMPILER_ID}" MATCHES ".*Intel.*" + ) #NOT MSVC + set(MATH_LIB m) + endif() + list(APPEND LAPACKE_LIBRARIES ${LAPACKE_LIB} ${LAPACK_LIB} ${BLAS_LIB} ${CBLAS_LIB}) + # Check for a common combination, and find required gfortran support libraries + + if(1) + if("${CMAKE_C_COMPILER_ID}" MATCHES ".*Clang.*" AND "${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU") + message(STATUS "\n\n WARNING: ${CMAKE_C_COMPILER} identified as ${CMAKE_C_COMPILER_ID}\n" + "AND: ${CMAKE_Fortran_COMPILER} identified as ${CMAKE_Fortran_COMPILER_ID}\n" + "\n" + "may be require special configurations. The most common is the need to" + "explicitly link C programs against the gfortran support library.") + + endif() + else() + ## This code automated code is hard to determine if it is robust in many different environments. + # Check for a common combination, and find required gfortran support libraries + if("${CMAKE_C_COMPILER_ID}" MATCHES ".*Clang.*" AND "${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU") + include(FortranCInterface) + FortranCInterface_VERIFY() + if(NOT FortranCInterface_VERIFIED_C) + message(FATAL_ERROR "C and fortran compilers are not compatible:\n${CMAKE_Fortran_COMPILER}:${CMAKE_C_COMPILER}") + endif() + + execute_process(COMMAND ${CMAKE_Fortran_COMPILER} -print-file-name=libgfortran.a OUTPUT_VARIABLE FORTRANSUPPORTLIB ERROR_QUIET) + string(STRIP ${FORTRANSUPPORTLIB} FORTRANSUPPORTLIB) + if(EXISTS "${FORTRANSUPPORTLIB}") + list(APPEND LAPACKE_LIBRARIES ${FORTRANSUPPORTLIB}) + message(STATUS "Appending fortran support lib: ${FORTRANSUPPORTLIB}") + else() + message(FATAL_ERROR "COULD NOT FIND libgfortran.a support library:${FORTRANSUPPORTLIB}:") + endif() + endif() + endif() + list(APPEND LAPACKE_LIBRARIES ${MATH_LIB}) +endif() + +mark_as_advanced( + LAPACKE_FOUND + LAPACKE_INCLUDE_DIRS + LAPACKE_LIBRARIES + LAPACKE_VERSION_MAJOR + LAPACKE_VERSION_MINOR + LAPACKE_VERSION_PATCH + LAPACKE_VERSION_STRING +) + +## For debugging +message(STATUS "LAPACKE_FOUND :${LAPACKE_FOUND}: - set to true if the library is found") +message(STATUS "LAPACKE_INCLUDE_DIRS :${LAPACKE_INCLUDE_DIRS}: - list of required include directories") +message(STATUS "LAPACKE_LIBRARIES :${LAPACKE_LIBRARIES}: - list of libraries to be linked") +message(STATUS "LAPACKE_VERSION_MAJOR :${LAPACKE_VERSION_MAJOR}: - major version number") +message(STATUS "LAPACKE_VERSION_MINOR :${LAPACKE_VERSION_MINOR}: - minor version number") +message(STATUS "LAPACKE_VERSION_PATCH :${LAPACKE_VERSION_PATCH}: - patch version number") +message(STATUS "LAPACKE_VERSION_STRING :${LAPACKE_VERSION_STRING}: - version number as a string") diff --git a/cmake/README.md b/cmake/README.md new file mode 100644 index 0000000..be07c7c --- /dev/null +++ b/cmake/README.md @@ -0,0 +1,7 @@ +Modern lapacke provides a lapacke-config.cmake (minimum version unconfirmed, but 3.7.1 does) +so FindLAPACKE.cmake wouldn't be neccesary, (see #38) +however this isn't reliable for older/other lapacke versions, so we provide a FindModule from: https://github.com/mrirecon/bart/blob/master/cmake/FindLAPACKE.cmake. + +Download with `curl -O https://raw.githubusercontent.com/mrirecon/bart/master/cmake/FindLAPACKE.cmake` + +This FindLapacke handles properly the case a lapacke-config.cmake exists in the system. diff --git a/cmake/proxTVConfig.cmake.in b/cmake/proxTVConfig.cmake.in new file mode 100644 index 0000000..19a8112 --- /dev/null +++ b/cmake/proxTVConfig.cmake.in @@ -0,0 +1,13 @@ +@PACKAGE_INIT@ + +set_and_check(proxTV_TARGETS "${CMAKE_CURRENT_LIST_DIR}/proxTVTargets.cmake") + +if(NOT TARGET proxTV::proxTV) + include(${proxTV_TARGETS}) +endif() + +include(CMakeFindDependencyMacro) +find_dependency(LAPACKE) +find_dependency(LAPACK) +find_dependency(OpenMP) +find_dependency(Threads) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..d33abd9 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,91 @@ +set(LIB_INSTALL_DIR lib CACHE STRING "Install folder for lib (relative") +set(INCLUDE_INSTALL_DIR include CACHE STRING "Install folder for headers (relative") +set(EXECUTABLE_INSTALL_DIR bin CACHE STRING "Install folder for executables (relative") + +set(headers + condat_fast_tv.h + general.h + johnsonRyanTV.h + LPopt.h + TVmacros.h + TVopt.h + utils.h + ) + +set(sources + condat_fast_tv.cpp + johnsonRyanTV.cpp + LPopt.cpp + TV2Dopt.cpp + TV2DWopt.cpp + TVgenopt.cpp + TVL1opt.cpp + TVL1opt_hybridtautstring.cpp + TVL1opt_kolmogorov.cpp + TVL1opt_tautstring.cpp + TVL1Wopt.cpp + TVL2opt.cpp + TVLPopt.cpp + TVNDopt.cpp + utils.cpp + ) + +add_library(proxTV ${sources}) + +target_compile_definitions(proxTV PUBLIC NOMATLAB) + +# Create directory with headers at build and install time. +file(COPY ${headers} + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/include") +install(FILES ${headers} + DESTINATION ${INCLUDE_INSTALL_DIR}) + +target_include_directories(proxTV PUBLIC + $ + $ + ${LAPACKE_INCLUDE_DIRS} + ) + +# Lapacke, Lapack, pthreads +target_link_libraries(proxTV PUBLIC + ${CMAKE_THREAD_LIBS_INIT} + ${LAPACK_LIBRARIES} + ${LAPACKE_LIBRARIES} + ) + +# OpenMP +target_compile_options(proxTV PUBLIC ${OpenMP_CXX_FLAGS}) +target_link_libraries(proxTV PUBLIC ${OpenMP_CXX_LIBRARIES}) + +install(TARGETS proxTV EXPORT proxTVTargets + LIBRARY DESTINATION ${LIB_INSTALL_DIR} + ARCHIVE DESTINATION ${LIB_INSTALL_DIR} + RUNTIME DESTINATION ${EXECUTABLE_INSTALL_DIR} + INCLUDES DESTINATION ${INCLUDE_INSTALL_DIR} + ) + +# This saves as to develop a hacky FindproxTV for other to use the library. +# A regular find_package(proxTV) will look for these targets. +# It needs an extra proxTVConfig.cmake to handle dependencies (provided in cmake directory) +install(EXPORT proxTVTargets + FILE proxTVTargets.cmake + NAMESPACE proxTV:: + DESTINATION ${LIB_INSTALL_DIR}/cmake/proxTV + ) + +# Generate and install proxTVConfigVersion.cmake +include(CMakePackageConfigHelpers) +write_basic_package_version_file("proxTVConfigVersion.cmake" + VERSION ${proxTV_VERSION} + COMPATIBILITY SameMajorVersion + ) +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/proxTVConfigVersion.cmake + DESTINATION ${LIB_INSTALL_DIR}/cmake/proxTV + ) + +# Install proxTVConfig.cmake +install(FILES + ${CMAKE_SOURCE_DIR}/cmake/proxTVConfig.cmake + DESTINATION ${LIB_INSTALL_DIR}/cmake/proxTV + ) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..1541962 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,21 @@ +set(test_names + test_tv1_2d + #other_test + ) + +add_executable(test_tv1_2d "test_tv1_2d.cpp") +add_test(NAME tv1_2d COMMAND test_tv1_2d) + +# To add more tests: append a name to test_names, and add_executable/add_test with that name +# Tests can be run with `ctest` in the build directory, +# it accepts regex, and different verbosity: ctest -R tv1 -VV +# add_executable(other_test "a_test_source_file.cpp") +# add_test(NAME a_name_that_makes_sense COMMAND other_test) + +# Link proxTV and include_directory for each executable. +foreach(test_executable ${test_names}) + target_include_directories(${test_executable} PUBLIC + $) + target_compile_definitions(${test_executable} PUBLIC NOMATLAB) + target_link_libraries(${test_executable} PUBLIC proxTV) +endforeach() diff --git a/test/project_using_proxTV/CMakeLists.txt b/test/project_using_proxTV/CMakeLists.txt new file mode 100644 index 0000000..555ff14 --- /dev/null +++ b/test/project_using_proxTV/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 2.8.12) +project(dummy) +find_package(proxTV) +add_executable(dummy dummy.cpp) +# All the requirements of proxTV: compiler definitions, include_directories, linker flags (INTERFACE) are propagated to the target. +target_link_libraries(dummy PUBLIC proxTV::proxTV ) diff --git a/test/project_using_proxTV/README.md b/test/project_using_proxTV/README.md new file mode 100644 index 0000000..3cdac16 --- /dev/null +++ b/test/project_using_proxTV/README.md @@ -0,0 +1,12 @@ +This a minimal project using proxTV as a third-party. +To compile it, it requires that proxTV is installed somewhere in your system. +The installation folder can be modified when configuring proxTV with the option + + -DCMAKE_INSTALL_PREFIX:PATH="/proxTV_install_folder" + +The default is usually the system folder `/usr`. Please read https://github.com/albarji/proxTV/README.md#c-interface for more info. + +Once installed in your system, if it is not in your system path, provide the folder where the `proxTVConfig.cmake` file is located with the option `-proxTV_DIR`: + + mkdir ~/dummy_project ; cd ~/dummy_project + cmake /path/proxTV/test/project_using_proxTV/ -DproxTV_DIR:PATH="/proxTV_install_folder/lib/cmake/proxTV" diff --git a/test/project_using_proxTV/dummy.cpp b/test/project_using_proxTV/dummy.cpp new file mode 100644 index 0000000..876a299 --- /dev/null +++ b/test/project_using_proxTV/dummy.cpp @@ -0,0 +1,26 @@ +#include "TVopt.h" + + +int main() +{ +// int DR2_TV(size_t M, size_t N, double*unary, double W1, double W2, double norm1, double norm2, double*s, int nThreads, int maxit, double* info); + + size_t M = 1; + size_t N = 1; + double* unary = new double; + double W1 = 1.0; + double W2 = 1.0; + double norm1 = 1.0; + double norm2 = 1.0; + double* s = new double; + int nThreads = 1; + int maxit = 1; + double* info = new double; + int r = DR2_TV(M, N, unary, W1, W2, norm1, norm2, s, nThreads, maxit, info); + + delete unary; + delete s; + delete info; + + return r; +} diff --git a/test/test_tv1_2d.cpp b/test/test_tv1_2d.cpp new file mode 100644 index 0000000..876a299 --- /dev/null +++ b/test/test_tv1_2d.cpp @@ -0,0 +1,26 @@ +#include "TVopt.h" + + +int main() +{ +// int DR2_TV(size_t M, size_t N, double*unary, double W1, double W2, double norm1, double norm2, double*s, int nThreads, int maxit, double* info); + + size_t M = 1; + size_t N = 1; + double* unary = new double; + double W1 = 1.0; + double W2 = 1.0; + double norm1 = 1.0; + double norm2 = 1.0; + double* s = new double; + int nThreads = 1; + int maxit = 1; + double* info = new double; + int r = DR2_TV(M, N, unary, W1, W2, norm1, norm2, s, nThreads, maxit, info); + + delete unary; + delete s; + delete info; + + return r; +} From bdf2a8431af3ca43c127edc5fd6bad30e466463a Mon Sep 17 00:00:00 2001 From: Pablo Hernandez-Cerdan Date: Tue, 6 Nov 2018 10:55:02 -0500 Subject: [PATCH 02/13] ENH: Eigen working Only two lapack functions are involved cmake: Add compile definition depending on LAPACK/EIGEN option: proxTV_USE_LAPACK (for consistency) compile definition: PROXTV_USE_LAPACK All instances of lapack calls have Eigen alternative Working! --- .gitmodules | 0 CMakeLists.txt | 18 +++- cmake/proxTVConfig.cmake.in | 8 +- src/CMakeLists.txt | 23 ++++- src/TVL1Wopt.cpp | 19 ++-- src/TVL1opt.cpp | 22 +++-- src/TVL2opt.cpp | 15 ++- src/TVLPopt.cpp | 25 ++++- src/general.h | 15 +-- src/lapackFunctionsWrap.cpp | 39 ++++++++ src/lapackFunctionsWrap.h | 153 +++++++++++++++++++++++++++++++ test/CMakeLists.txt | 11 +++ test/test_use_eigen.cpp | 178 ++++++++++++++++++++++++++++++++++++ 13 files changed, 481 insertions(+), 45 deletions(-) create mode 100644 .gitmodules create mode 100644 src/lapackFunctionsWrap.cpp create mode 100644 src/lapackFunctionsWrap.h create mode 100644 test/test_use_eigen.cpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/CMakeLists.txt b/CMakeLists.txt index 086d985..8204331 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/cmake) #------------------------------------------------------------------------------ option(proxTV_INSTALL_DEVELOPMENT "Install development files" ON) option(ENABLE_TESTING "Compile tests" OFF) +option(proxTV_USE_LAPACK "Use LAPACK and LAPACKE instead of EIGEN" ON) #------------------------------------------------------------------------------ # Set a default build type if none was specified @@ -40,11 +41,18 @@ endif() #------------------------------------------------------------------------------ # External dependencies -find_package(LAPACKE REQUIRED) -message(STATUS "Lapacke libraries: ${LAPACKE_LIBRARIES}") - -find_package(LAPACK REQUIRED) -message(STATUS "Lapack libraries: ${LAPACK_LIBRARIES}") +if(proxTV_USE_LAPACK) + find_package(LAPACKE REQUIRED) + message(STATUS "Lapacke libraries: ${LAPACKE_LIBRARIES}") + + find_package(LAPACK REQUIRED) + message(STATUS "Lapack libraries: ${LAPACK_LIBRARIES}") +else() + find_package(Eigen3 REQUIRED) + get_target_property(EIGEN_INCLUDE_DIR Eigen3::Eigen + INTERFACE_INCLUDE_DIRECTORIES) + message(STATUS "Eigen Found: ${EIGEN_INCLUDE_DIR}") +endif() set(THREADS_PREFER_PTHREAD_FLAG 1) find_package(Threads) diff --git a/cmake/proxTVConfig.cmake.in b/cmake/proxTVConfig.cmake.in index 19a8112..2a641a7 100644 --- a/cmake/proxTVConfig.cmake.in +++ b/cmake/proxTVConfig.cmake.in @@ -7,7 +7,11 @@ if(NOT TARGET proxTV::proxTV) endif() include(CMakeFindDependencyMacro) -find_dependency(LAPACKE) -find_dependency(LAPACK) +if(@proxTV_USE_LAPACK@) + find_dependency(LAPACKE) + find_dependency(LAPACK) +else() + find_dependency(Eigen3) +endif() find_dependency(OpenMP) find_dependency(Threads) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d33abd9..e672361 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,6 +6,7 @@ set(headers condat_fast_tv.h general.h johnsonRyanTV.h + lapackFunctionsWrap.h LPopt.h TVmacros.h TVopt.h @@ -15,6 +16,7 @@ set(headers set(sources condat_fast_tv.cpp johnsonRyanTV.cpp + lapackFunctionsWrap.cpp LPopt.cpp TV2Dopt.cpp TV2DWopt.cpp @@ -33,6 +35,9 @@ set(sources add_library(proxTV ${sources}) target_compile_definitions(proxTV PUBLIC NOMATLAB) +if(proxTV_USE_LAPACK) + target_compile_definitions(proxTV PUBLIC PROXTV_USE_LAPACK) +endif() # Create directory with headers at build and install time. file(COPY ${headers} @@ -43,16 +48,26 @@ install(FILES ${headers} target_include_directories(proxTV PUBLIC $ $ - ${LAPACKE_INCLUDE_DIRS} ) -# Lapacke, Lapack, pthreads +# Pthreads target_link_libraries(proxTV PUBLIC ${CMAKE_THREAD_LIBS_INIT} - ${LAPACK_LIBRARIES} - ${LAPACKE_LIBRARIES} ) +# Lapacke, Lapack +if(proxTV_USE_LAPACK) + target_include_directories(proxTV PUBLIC + ${LAPACKE_INCLUDE_DIRS} + ) + target_link_libraries(proxTV PUBLIC + ${LAPACK_LIBRARIES} + ${LAPACKE_LIBRARIES} + ) +else() + target_link_libraries(proxTV PUBLIC Eigen3::Eigen) +endif() + # OpenMP target_compile_options(proxTV PUBLIC ${OpenMP_CXX_FLAGS}) target_link_libraries(proxTV PUBLIC ${OpenMP_CXX_LIBRARIES}) diff --git a/src/TVL1Wopt.cpp b/src/TVL1Wopt.cpp index afbe566..f367fe2 100644 --- a/src/TVL1Wopt.cpp +++ b/src/TVL1Wopt.cpp @@ -111,9 +111,13 @@ int PN_TV1_Weighted(double *y,double *lambda,double *x,double *info,int n,double aux2[i] = -1; } aux[nn-1] = 2; +#ifdef PROXTV_USE_LAPACK dpttrf_(&nnp,aux,aux2,&rc); /* Solve Choleski-like linear system to obtain unconstrained solution */ dpttrs_(&nnp, &one, aux, aux2, w, &nnp, &rc); +#else + dpttrf_plus_dpttrs_eigen(&nnp, aux, aux2, w); +#endif /* above assume we solved DD'u = Dy */ /* we wanted to solve DD'Wu = Dy; so now obtain u by dividing by W */ @@ -191,17 +195,18 @@ int PN_TV1_Weighted(double *y,double *lambda,double *x,double *info,int n,double #endif /* Factorize reduced Hessian */ nIp = nI; - dpttrf_(&nIp,aux,aux2,&rc); - #ifdef DEBUG - fprintf(DEBUG_FILE,"c=["); for(i=0;i #include -/* Mex and LAPACK includes */ #ifdef NOMATLAB -#undef lapack_int -#define lapack_int int -extern "C" { - void dpttrs_(lapack_int* n, lapack_int* nrhs, const double* d, const double* e, double* b, lapack_int* ldb, - lapack_int *info ); - void dpttrf_( lapack_int* n, double* d, double* e, lapack_int *info ); -} inline double mxGetInf() { return INFINITY; } -#else -#include "mex.h" -#include "lapack.h" -#include "matrix.h" -#define lapack_int ptrdiff_t #endif +#include "lapackFunctionsWrap.h" + /* Uncomment to print debug messages to a debug file */ //#define DEBUG /* Choose here the name of the debug file */ diff --git a/src/lapackFunctionsWrap.cpp b/src/lapackFunctionsWrap.cpp new file mode 100644 index 0000000..ec719a0 --- /dev/null +++ b/src/lapackFunctionsWrap.cpp @@ -0,0 +1,39 @@ +/** + Definitions of functions wrapping lapack. + Declarations depends on compilations options PROXTV_USE_LAPACK + + @author Pablo Hernandez-Cerdan +*/ + +#include "lapackFunctionsWrap.h" + +#ifndef PROXTV_USE_LAPACK // USE_EIGEN +#include +void dpttrf_plus_dpttrs_eigen( lapack_int* n, double* d, double* e, double *b) +{ + using EigenMatrix = Eigen::MatrixXd; + using EigenVector = Eigen::VectorXd; + using EigenVectorMap = Eigen::Map; + // Eigen has to create the full matrix from the diagonal d and subdiagonal e + int mSize = *n; + EigenMatrix eigenM(mSize,mSize); + EigenVectorMap diag(d, mSize); + EigenVectorMap subAndUpperDiag(e, mSize - 1); + EigenVectorMap inputB_outputX(b, mSize); + // Populate matrix + eigenM.diagonal() = diag; + eigenM.diagonal( 1) = subAndUpperDiag; + eigenM.diagonal(-1) = subAndUpperDiag; + + // Factorize using ldlt (ldl is also possible, faster but less accurate) + // A = LDL' + Eigen::LDLT factorization(eigenM); + // A*X = b + // This modifies the input/output pointer: b + inputB_outputX = factorization.solve(inputB_outputX); + // This modifies the input/output pointers: d and e + EigenMatrix factorized = factorization.matrixLDLT(); + diag = factorized.diagonal(); + subAndUpperDiag = factorized.diagonal(-1); +} +#endif diff --git a/src/lapackFunctionsWrap.h b/src/lapackFunctionsWrap.h new file mode 100644 index 0000000..368b048 --- /dev/null +++ b/src/lapackFunctionsWrap.h @@ -0,0 +1,153 @@ +/** + Lapack functions wrapping + This file creates a NAME_wrap layer on top of lapack functions with name: NAME + The wrap function will call lapack or eigen functions depending on the compilation options + WITH LAPACK: + Declare extern NAME_ functions that will be provided by LAPACKE + WITH EIGEN: + Declare NAME_eigen, equivalent to lapack but using EIGEN. + + @author Pablo Hernandez-Cerdan +*/ +#ifdef NOMATLAB +#undef lapack_int +#define lapack_int int +#else // WITH_MATLAB +#include "mex.h" +#ifdef PROXTV_USE_LAPACK +#include "lapack.h" +#endif +#define lapack_int ptrdiff_t +#endif + +#ifdef PROXTV_USE_LAPACK +extern "C" { + /** + * DPTTRS solves a tridiagonal system of the form + * A * X = B + * using the L*D*L' factorization of A computed by DPTTRF. D is a + * diagonal matrix specified in the vector D, L is a unit bidiagonal + * matrix whose subdiagonal is specified in the vector E, and X and B + * are N by NRHS matrices. + * + * Arguments + * ========= + * + * @param n (input) INTEGER + * The order of the tridiagonal matrix A. N >= 0. + * + * @param nrhs (input) INTEGER + * The number of right hand sides, i.e., the number of columns + * of the matrix B. NRHS >= 0. + * + * @param d (input) DOUBLE PRECISION array, dimension (N) + * The n diagonal elements of the diagonal matrix D from the + * L*D*L' factorization of A. + * + * @param e (input) DOUBLE PRECISION array, dimension (N-1) + * The (n-1) subdiagonal elements of the unit bidiagonal factor + * L from the L*D*L' factorization of A. E can also be regarded + * as the superdiagonal of the unit bidiagonal factor U from the + * factorization A = U'*D*U. + * + * @param b (input/output) DOUBLE PRECISION array, dimension (LDB,NRHS) + * On entry, the right hand side vectors B for the system of + * linear equations. + * On exit, the solution vectors, X. + * + * @param ldb (input) INTEGER + * The leading dimension of the array B. LDB >= max(1,N). + * + * @param info (output) INTEGER + * = 0: successful exit + * < 0: if INFO = -k, the k-th argument had an illegal value + * + */ + void dpttrs_(lapack_int* n, lapack_int* nrhs, const double* d, + const double* e, double* b, lapack_int* ldb, lapack_int *info ); + /** + * DPTTRF computes the L*D*L' factorization of a real symmetric + * positive definite tridiagonal matrix A. The factorization may also + * be regarded as having the form A = U'*D*U. + * + * Arguments + * ========= + * + * @param n (input) INTEGER + * The order of the matrix A. N >= 0. + * + * @param d (input/output) DOUBLE PRECISION array, dimension (N) + * On entry, the n diagonal elements of the tridiagonal matrix + * A. On exit, the n diagonal elements of the diagonal matrix + * D from the L*D*L' factorization of A. + * + * @param e (input/output) DOUBLE PRECISION array, dimension (N-1) + * On entry, the (n-1) subdiagonal elements of the tridiagonal + * matrix A. On exit, the (n-1) subdiagonal elements of the + * unit bidiagonal factor L from the L*D*L' factorization of A. + * E can also be regarded as the superdiagonal of the unit + * + * @param b (input/output) DOUBLE PRECISION array, dimension (LDB,NRHS) + * On entry, the right hand side vectors B for the system of + * linear equations. + * On exit, the solution vectors, X. + * bidiagonal factor U from the U'*D*U factorization of A. + * + * @param info (output) INTEGER + * = 0: successful exit + * < 0: if INFO = -k, the k-th argument had an illegal value + * > 0: if INFO = k, the leading minor of order k is not + * positive definite; if k < N, the factorization could not + * be completed, while if k = N, the factorization was + * completed, but D(N) <= 0. + */ + void dpttrf_( lapack_int* n, double* d, double* e, lapack_int *info ); +} + +#else // USE_EIGEN + +/** + * DPTTRS solves a tridiagonal system of the form + * A * X = B + * using the L*D*L' factorization of A computed by DPTTRF. D is a + * diagonal matrix specified in the vector D, L is a unit bidiagonal + * matrix whose subdiagonal is specified in the vector E, and X and B + * are N by NRHS matrices. + * + * DPTTRF computes the L*D*L' factorization of a real symmetric + * positive definite tridiagonal matrix A. The factorization may also + * be regarded as having the form A = U'*D*U. + * + * Arguments + * ========= + * + * @param n (input) INTEGER + * The order of the matrix A. N >= 0. + * + * @param d (input) DOUBLE PRECISION array, dimension (N) + * On entry, the n diagonal elements of the tridiagonal matrix + * A. On exit, the n diagonal elements of the diagonal matrix + * D from the L*D*L' factorization of A. + * + * @param e (input) DOUBLE PRECISION array, dimension (N-1) + * On entry, the (n-1) subdiagonal elements of the tridiagonal + * matrix A. On exit, the (n-1) subdiagonal elements of the + * unit bidiagonal factor L from the L*D*L' factorization of A. + * E can also be regarded as the superdiagonal of the unit + * + * @param b (input/output) DOUBLE PRECISION array, dimension (LDB,NRHS) + * On entry, the right hand side vectors B for the system of + * linear equations. + * On exit, the solution vectors, X. + * + * @param info (output) INTEGER ----- UNUSED ----- + * = 0: successful exit + * < 0: if INFO = -k, the k-th argument had an illegal value + * > 0: if INFO = k, the leading minor of order k is not + * positive definite; if k < N, the factorization could not + * be completed, while if k = N, the factorization was + * completed, but D(N) <= 0. + */ +void dpttrf_plus_dpttrs_eigen( lapack_int* n, double* d, double* e, double* b); + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1541962..7c02bbf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,6 +6,17 @@ set(test_names add_executable(test_tv1_2d "test_tv1_2d.cpp") add_test(NAME tv1_2d COMMAND test_tv1_2d) +if(proxTV_USE_LAPACK) + find_package(Eigen3) + if (TARGET Eigen3::Eigen) + list(APPEND test_names test_use_eigen) + add_executable(test_use_eigen "test_use_eigen.cpp") + add_test(NAME use_eigen COMMAND test_use_eigen) + target_link_libraries(test_use_eigen PUBLIC Eigen3::Eigen) + else() + message(WARNING "Eigen3 not found, test comparing lapack and eigen is disabled.") + endif() +endif() # To add more tests: append a name to test_names, and add_executable/add_test with that name # Tests can be run with `ctest` in the build directory, # it accepts regex, and different verbosity: ctest -R tv1 -VV diff --git a/test/test_use_eigen.cpp b/test/test_use_eigen.cpp new file mode 100644 index 0000000..3acaf3e --- /dev/null +++ b/test/test_use_eigen.cpp @@ -0,0 +1,178 @@ +#include +#include +#include "general.h" + +using EigenMatrix = Eigen::MatrixXd; +using EigenVector = Eigen::VectorXd; + +EigenMatrix oneD() +{ + EigenMatrix m(1,4); + m(0,0) = 3; + m(0,1) = 2.5; + m(0,2) = -1; + m(0,3) = 1.5; + return m; +} + +EigenMatrix lapack_oneD() +{ + auto eigenM = oneD(); + int n = eigenM.cols(); + std::cout << "Cols: " << n << std::endl; + int nn = n ; + int rc = nn; + int nnp = nn; + int one = 1; + double *w = nullptr; + double *aux = nullptr; + double *aux2 = nullptr; + w = (double*)malloc(sizeof(double)*nn); + aux = (double*)malloc(sizeof(double)*nn); + aux2 = (double*)malloc(sizeof(double)*nn); + for(int i=0;i ldltOfM(eigenM); + EigenVector v = ldltOfM.solve(b); + std::cout << v << std::endl; + // EigenVector v = eigenM.colPivHouseholderQr().solve(b); + EigenMatrix ldltMatrix = ldltOfM.matrixLDLT(); + std::cout << ldltMatrix << std::endl; + EigenMatrix lMatrix = ldltOfM.matrixL(); + std::cout << lMatrix << std::endl; + std::cout << "Diagonal of LDLT:" << std::endl; + std::cout << ldltMatrix.diagonal() << std::endl; + std::cout << "SubDiagonal of LDLT:" << std::endl; + std::cout << ldltMatrix.diagonal(-1) << std::endl; + + return v; +} + +/// The idea is that input and output are raw pointers to couple with existing code +EigenMatrix eigen_interface_raw_pointers_oneD() +{ + // Create the typemaps + using EigenMatrixMap = Eigen::Map; + using EigenMatrixReadOnlyMap = Eigen::Map; + using EigenVectorMap = Eigen::Map; + using EigenVectorReadOnlyMap = Eigen::Map; + // Populate values using Eigen (just because easier) + EigenMatrix eigenM(4,4); + { + EigenVector diag(eigenM.cols()); + diag.setConstant(2); + EigenVector subUpperDiag(eigenM.cols()); + subUpperDiag.setConstant(-1); + eigenM.diagonal() = diag; + eigenM.diagonal(1) = subUpperDiag; + eigenM.diagonal(-1) = subUpperDiag; + } + std::cout << "Matrix eigenM:\n" << eigenM << std::endl; + EigenVector b(eigenM.cols()); + EigenMatrix oneM = oneD(); + for (int i = 0; i < oneM.cols(); ++i) { + b[i] = oneM(0, i+1) - oneM(0, i); /* Dy */ + } + // Set input raw pointers + int nn = eigenM.cols(); + double *w = (double*)malloc(sizeof(double)*nn); + for(int i=0;i Date: Wed, 7 Nov 2018 17:39:15 -0500 Subject: [PATCH 03/13] cmake: options are protected with ifs BUG: Add source_dir to include_directories Fix cmake: remove installing config files in src/CMakeLists Handled in top CMakelists.txt --- CMakeLists.txt | 14 ++++++++++---- src/CMakeLists.txt | 7 +------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8204331..8ea98b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,9 +12,15 @@ message(STATUS " description: ${proxTV_DESCRIPTION}") list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/cmake) #------------------------------------------------------------------------------ -option(proxTV_INSTALL_DEVELOPMENT "Install development files" ON) -option(ENABLE_TESTING "Compile tests" OFF) -option(proxTV_USE_LAPACK "Use LAPACK and LAPACKE instead of EIGEN" ON) +if(NOT DEFINED proxTV_INSTALL_DEVELOPMENT) + option(proxTV_INSTALL_DEVELOPMENT "Install development files" ON) +endif() +if(NOT DEFINED proxTV_ENABLE_TESTING) + option(proxTV_ENABLE_TESTING "Compile tests" OFF) +endif() +if(NOT DEFINED proxTV_USE_LAPACK) + option(proxTV_USE_LAPACK "Use LAPACK and LAPACKE instead of EIGEN" ON) +endif() #------------------------------------------------------------------------------ # Set a default build type if none was specified @@ -67,7 +73,7 @@ add_subdirectory(src) #------------------------------------------------------------------------------ # Testing -if(ENABLE_TESTING) +if(proxTV_ENABLE_TESTING) enable_testing() add_subdirectory(test) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e672361..81a678f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,6 +47,7 @@ install(FILES ${headers} target_include_directories(proxTV PUBLIC $ + $ $ ) @@ -98,9 +99,3 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/proxTVConfigVersion.cmake DESTINATION ${LIB_INSTALL_DIR}/cmake/proxTV ) - -# Install proxTVConfig.cmake -install(FILES - ${CMAKE_SOURCE_DIR}/cmake/proxTVConfig.cmake - DESTINATION ${LIB_INSTALL_DIR}/cmake/proxTV - ) From ea3aecaca062dd3a0593c6e31ba5f7762e21b98f Mon Sep 17 00:00:00 2001 From: Pablo Hernandez-Cerdan Date: Tue, 9 Apr 2019 13:13:30 -0400 Subject: [PATCH 04/13] CMake: Add -fPIC Triggered when wrapping ITK ```bash /usr/bin/ld: _deps/proxtv_fetch-build/src/libitkproxTV-5.0.a(lapackFunctionsWrap.cpp.o): relocation R_X86_64_PC32 against symbol `_ZTVSt9bad_alloc@@GLIBCXX_3.4' can not be used when making a shared object; recompile with -fPIC ``` --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ea98b6..e069d00 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,9 @@ message(STATUS " description: ${proxTV_DESCRIPTION}") # Update CMake module path to lookup proxTV custom CMake modules list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +# Add -fPIC +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + #------------------------------------------------------------------------------ if(NOT DEFINED proxTV_INSTALL_DEVELOPMENT) option(proxTV_INSTALL_DEVELOPMENT "Install development files" ON) From 2640519572400526c2eca64329df61a1040b6294 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Tue, 9 Apr 2019 15:59:12 -0400 Subject: [PATCH 05/13] BUG: OpenMP is optional but there is no guard to link its libraries --- src/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 81a678f..c40a2af 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -70,8 +70,10 @@ else() endif() # OpenMP -target_compile_options(proxTV PUBLIC ${OpenMP_CXX_FLAGS}) -target_link_libraries(proxTV PUBLIC ${OpenMP_CXX_LIBRARIES}) +if(${OpenMP_FOUND}}) + target_compile_definitions(proxTV _OPENMP) + target_link_libraries(proxTV PRIVATE OpenMP::OpenMP_CXX) +endif() install(TARGETS proxTV EXPORT proxTVTargets LIBRARY DESTINATION ${LIB_INSTALL_DIR} From f1094d2543e1395d2bf5e49eea24193a71bc582c Mon Sep 17 00:00:00 2001 From: Pablo Hernandez-Cerdan Date: Wed, 10 Apr 2019 17:37:27 -0400 Subject: [PATCH 06/13] CMake: Use WINDOWS_EXPORT_ALL_SYMBOLS for proxTV lib --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c40a2af..41e7b6b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -33,7 +33,7 @@ set(sources ) add_library(proxTV ${sources}) - +set_target_properties(proxTV PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS 1) target_compile_definitions(proxTV PUBLIC NOMATLAB) if(proxTV_USE_LAPACK) target_compile_definitions(proxTV PUBLIC PROXTV_USE_LAPACK) From 68dd8ab1ff7e5c0ab13fd8562df05d516ff8651f Mon Sep 17 00:00:00 2001 From: Pablo Hernandez-Cerdan Date: Thu, 11 Apr 2019 11:35:51 -0400 Subject: [PATCH 07/13] COMP: Fix multiple c++ warnings Using CXX_CMAKE_FLAGS+="-Wall -Wextra" Plus Windows specific warnings --- src/CMakeLists.txt | 4 +-- src/LPopt.cpp | 9 ++++--- src/TV2DWopt.cpp | 16 +++++------ src/TV2Dopt.cpp | 55 +++++++++++++++++++++----------------- src/TVL1Wopt.cpp | 8 ++++-- src/TVL1opt.cpp | 8 ++++-- src/TVL1opt_tautstring.cpp | 5 ++++ src/TVL2opt.cpp | 8 ++++-- src/TVLPopt.cpp | 36 +++++++++++++++---------- src/TVNDopt.cpp | 32 +++++++++++++++------- src/condat_fast_tv.cpp | 2 +- src/general.h | 2 -- 12 files changed, 114 insertions(+), 71 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 41e7b6b..aad66aa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -70,8 +70,8 @@ else() endif() # OpenMP -if(${OpenMP_FOUND}}) - target_compile_definitions(proxTV _OPENMP) +if(OpenMP_CXX_FOUND) + # target_compile_definitions(proxTV PRIVATE _OPENMP) target_link_libraries(proxTV PRIVATE OpenMP::OpenMP_CXX) endif() diff --git a/src/LPopt.cpp b/src/LPopt.cpp index 4203f1b..e70d981 100644 --- a/src/LPopt.cpp +++ b/src/LPopt.cpp @@ -211,7 +211,10 @@ int PN_LPinf(double *y,double lambda,double *x,double *info,int n,Workspace *ws) */ int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspace *ws,int positive,double objGap){ double *g=NULL,*d=NULL,*xnorm=NULL,*auxv=NULL; - double stop,stop2,q,nx,f,fupdate,aux,c,den,xp1vGrad,gRd,delta,prevDelta,improve,rhs,grad0,gap,epsilon; + double stop,stop2,q,nx,f,aux,c,den,xp1vGrad,gRd,delta,prevDelta,rhs,gap,epsilon; + double fupdate = 0; + double improve = 0; + double grad0 = 0; int *inactive=NULL,*signs=NULL; int i,j,iters,recomp,found,nI; short updateKind; @@ -745,7 +748,7 @@ int PN_LPp(double *y,double lambda,double *x,double *info,int n,double p,Workspa @argument lambda multiplier of the Lp norm @argument norm precomputed Lp norm of x **/ -double PN_LPpGap(double *x, double *y, double *diff, int n, double q, double lambda, double norm) { +double PN_LPpGap(double *, double *y, double *diff, int n, double q, double lambda, double norm) { /* Compute dual norm */ double dualnorm = LPnorm(diff,n,q); @@ -1016,7 +1019,7 @@ void solveLinearLP(double *z, int n, double p, double lambda, double *s) { #endif // The solution approximately lies at the opposite corner of the l1 ball double largest = 0, val; - int ilargest; + int ilargest = 0; // Find largest entry of z, in absolute value for ( i = 0 ; i < n ; i++ ) { diff --git a/src/TV2DWopt.cpp b/src/TV2DWopt.cpp index 76cb5af..b860cfc 100644 --- a/src/TV2DWopt.cpp +++ b/src/TV2DWopt.cpp @@ -46,7 +46,7 @@ void DR_rowsPass(size_t M, size_t N, double* input, double* output, double* ref, int DR2L1W_TV(size_t M, size_t N, double*unary, double*W1, double*W2, double*s, int nThreads, int maxit, double* info) { - int i; + size_t i; double *t = NULL; double *tb = NULL; Workspace **ws = NULL; @@ -67,7 +67,7 @@ int DR2L1W_TV(size_t M, size_t N, double*unary, double*W1, double*W2, double*s, if (nThreads < 1) nThreads = 1; omp_set_num_threads(nThreads); - maxDim = (M > N) ? M : N; + maxDim = int( (M > N) ? M : N ); // Alloc memory for algorithm */ t = (double*) malloc(sizeof(double)*M*N); @@ -152,7 +152,6 @@ int DR2L1W_TV(size_t M, size_t N, double*unary, double*W1, double*W2, double*s, void DR_columnsPass(size_t M, size_t N, double* input, double* output, double* W, Workspace **ws) { #pragma omp parallel shared(M,N,input,output,W,ws) default(none) { - int i,j; // Get thread number int id = omp_get_thread_num(); // Get corresponding workspace @@ -161,7 +160,7 @@ void DR_columnsPass(size_t M, size_t N, double* input, double* output, double* W // Run 1-d solvers in parallel on each column of the input #pragma omp for - for (j=0; j < N; j++) { + for (int j=0; j < N; j++) { resetWorkspace(wsi); // Array for weights double* wline = getDoubleWorkspace(wsi); @@ -205,7 +204,7 @@ void DR_rowsPass(size_t M, size_t N, double* input, double* output, double* ref, // Array for weights double* wline = getDoubleWorkspace(wsi); // Prepare weights - int idx; + size_t idx; for ( idx = j, i = 0 ; i < N-1 ; i++, idx+=M ) wline[i] = W[idx]; // Prepare inputs, considering displacement from reference signal @@ -229,13 +228,12 @@ void DR_rowsPass(size_t M, size_t N, double* input, double* output, double* ref, @param W weights of the TV regularization @param ws Workspace to use for the computation */ -void DR_proxDiff(size_t n, double* input, double* output, double* W, Workspace *ws) { - int i; +void DR_proxDiff(size_t n, double* input, double* output, double* W, Workspace *) { // Compute proximity - tautString_TV1_Weighted(input, W, output, n); + tautString_TV1_Weighted(input, W, output, (int) n); // Return differences between input and proximity output - for (i=0; i < n; i++) + for (size_t i=0; i < n; i++) output[i] = input[i] - output[i]; } diff --git a/src/TV2Dopt.cpp b/src/TV2Dopt.cpp index a89045f..29c1e25 100644 --- a/src/TV2Dopt.cpp +++ b/src/TV2Dopt.cpp @@ -168,9 +168,13 @@ int PD2_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double #ifdef DEBUG fprintf(DEBUG_FILE,"··········Penalty 0··········\n"); #endif - d = dims[0]-1; + d = int(dims[0]-1); /* Run 1-dimensional prox operator over each 1-dimensional slice along the specified dimension (parallelized) */ +#ifdef DEBUG #pragma omp parallel shared(ws,nSlices,ns,d,incs,x,p,lambdas,z,norms,DEBUG_FILE) private(j,k,idx1,idx2) default(none) +#else + #pragma omp parallel shared(ws,nSlices,ns,d,incs,x,p,lambdas,z,norms) private(j,k,idx1,idx2) default(none) +#endif { /* Get thread number */ int id = omp_get_thread_num(); @@ -217,10 +221,14 @@ int PD2_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double #ifdef DEBUG fprintf(DEBUG_FILE,"··········Penalty 1··········\n"); #endif - d = dims[1]-1; + d = int(dims[1]-1); /* Run 1-dimensional prox operator over each 1-dimensional slice along the specified dimension (parallelized) */ +#ifdef DEBUG #pragma omp parallel shared(ws,nSlices,ns,d,incs,x,q,lambdas,z,norms,DEBUG_FILE) private(j,k,idx1,idx2) default(none) +#else + #pragma omp parallel shared(ws,nSlices,ns,d,incs,x,q,lambdas,z,norms) private(j,k,idx1,idx2) default(none) +#endif { /* Get thread number */ int id = omp_get_thread_num(); @@ -353,12 +361,10 @@ int DR2_TV(size_t M, size_t N, double*unary, double W1, double W2, double norm1, double norm2, double*s, int nThreads, int maxit, double* info) { - int i; - double *ytr = NULL; + size_t i; double *t = NULL; double *tb = NULL; Workspace **ws = NULL; - int maxDim; #define FREE \ if(t) free(t); \ @@ -373,7 +379,7 @@ int DR2_TV(size_t M, size_t N, double*unary, double W1, double W2, if (nThreads < 1) nThreads = 1; omp_set_num_threads(nThreads); - maxDim = (M > N) ? M : N; + int maxDim = int( (M > N) ? M : N ); // Alloc memory for algorithm */ t = (double*) malloc(sizeof(double)*M*N); @@ -459,7 +465,6 @@ int DR2_TV(size_t M, size_t N, double*unary, double W1, double W2, void DR_columnsPass(size_t M, size_t N, double* input, double* output, double W, double norm, Workspace **ws) { #pragma omp parallel shared(M,N,input,output,W,norm,ws) default(none) { - int j; // Get thread number int id = omp_get_thread_num(); // Get corresponding workspace @@ -468,7 +473,7 @@ void DR_columnsPass(size_t M, size_t N, double* input, double* output, double W, // Run 1-d solvers in parallel on each column of the input #pragma omp for - for (j=0; j < N; j++) { + for (int j=0; j < N; j++) { resetWorkspace(wsi); // Prepare inputs memcpy(wsi->in, input+(M*j), sizeof(double)*M); @@ -510,7 +515,7 @@ void DR_rowsPass(size_t M, size_t N, double* input, double* output, double* ref, for (j=0; j < M; j++) { resetWorkspace(wsi); // Prepare inputs, considering displacement from reference signal - int idx; + size_t idx; for ( idx = j, i = 0 ; i < N ; i++, idx+=M ) wsi->in[i] = ref[idx]-input[idx]; // Compute prox difference for this row @@ -536,13 +541,12 @@ difference between input and output of prox. @param norm degree of TV @param ws Workspace to use for the computation */ -void DR_proxDiff(size_t n, double* input, double* output, double W, double norm, Workspace *ws) { - int i; +void DR_proxDiff(size_t n, double* input, double* output, double W, double norm, Workspace *) { // Compute proximity - TV(input, W, output, NULL, n , norm, NULL); + TV(input, W, output, NULL, (int) n , norm, NULL); // Return differences between input and proximity output - for (i=0; i < n; i++) + for (size_t i=0; i < n; i++) output[i] = input[i] - output[i]; } @@ -585,9 +589,10 @@ void DR_proxDiff(size_t n, double* input, double* output, double W, double norm, info -- information structure about optimization */ int CondatChambollePock2_TV(size_t M, size_t N, double*Y, double lambda, double*X, short alg, int maxit, double* info) { - double sigma, tau, theta, gamma, normalizer, tmp; + double sigma, tau, theta, normalizer, tmp; + double gamma = -1; double *U1 = NULL, *U2 = NULL, *Xt = NULL, *Z = NULL; - int i, j; + size_t i, j; #define FREE \ if(U1) free(U1); \ @@ -695,12 +700,12 @@ int CondatChambollePock2_TV(size_t M, size_t N, double*Y, double lambda, double* // Combiner step: Z = Xt + \theta (Xt - X); for ( i = 0 ; i < M*N ; i++ ) Z[i] = Xt[i] + theta * (Xt[i] - X[i]); - // Update algorithm parameters (only in accelerated Chambolle-Pock) - if ( alg == ALG_CHAMBOLLE_POCK_ACC ) { + // Update algorithm parameters (only in accelerated Chambolle-Pock) + if ( alg == ALG_CHAMBOLLE_POCK_ACC ) { tau *= theta; sigma /= theta; theta = 1. / sqrt(1 + 2 * gamma * tau); // 1/sqrt(1 + 2*gamma*tau) - } + } // Compute stopping tolerance before copying X stop = 0; normalizer = 0; @@ -787,7 +792,7 @@ int CondatChambollePock2_TV(size_t M, size_t N, double*Y, double lambda, double* int Yang2_TV(size_t M, size_t N, double*Y, double lambda, double*X, int maxit, double* info) { double *U1 = NULL, *U2 = NULL, *Z1 = NULL, *Z2 = NULL; double rho; - int i, j; + size_t i, j; Workspace *ws = NULL; #define FREE \ @@ -807,7 +812,7 @@ int Yang2_TV(size_t M, size_t N, double*Y, double lambda, double*X, int maxit, d rho = 10; // Alloc memory - int size = (M > N) ? M : N; + int size = int( (M > N) ? M : N ); U1 = (double*)calloc(M*N,sizeof(double)); U2 = (double*)calloc(M*N,sizeof(double)); Z1 = (double*)malloc(sizeof(double)*M*N); @@ -838,7 +843,7 @@ int Yang2_TV(size_t M, size_t N, double*Y, double lambda, double*X, int maxit, d for ( j = 0 ; j < N ; j++ ) ws->in[j] = -1. / rho * U1[j*M+i] + X[j*M+i]; resetWorkspace(ws); - TV(ws->in, lambda/rho, ws->out, NULL, N, 1, ws); + TV(ws->in, lambda/rho, ws->out, NULL, (int) N, 1, ws); // Recover data for ( j = 0 ; j < N ; j++ ) Z1[j*M+i] = ws->out[j]; @@ -849,7 +854,7 @@ int Yang2_TV(size_t M, size_t N, double*Y, double lambda, double*X, int maxit, d // Copy column data to workspace for ( j = 0 ; j < M ; j++ ) ws->in[j] = -1. / rho * U2[i*M+j] + X[i*M+j]; - TV(ws->in, lambda/rho, ws->out, NULL, M, 1, ws); + TV(ws->in, lambda/rho, ws->out, NULL, (int) M, 1, ws); // Recover data memcpy(Z2+i*M, ws->out, sizeof(double)*M); } @@ -907,7 +912,7 @@ int Yang2_TV(size_t M, size_t N, double*Y, double lambda, double*X, int maxit, d int Kolmogorov2_TV(size_t M, size_t N, double*Y, double lambda, double*X, int maxit, double* info) { double sigma, tau, theta, tmp, normalizer; double *U = NULL, *Xprev = NULL, *row=NULL, *rowout=NULL, *TMP=NULL; - int i, j; + size_t i, j; size_t NM = N*M; #define FREE \ @@ -966,7 +971,7 @@ int Kolmogorov2_TV(size_t M, size_t N, double*Y, double lambda, double*X, int ma // Dual prox for cols for ( j = 0 ; j < NM ; j+=M ) { // Normal prox - TV(TMP+j, lambda/sigma, U+j, NULL, M, 1, NULL); + TV(TMP+j, lambda/sigma, U+j, NULL, (int) M, 1, NULL); // Moreau's identity for ( i = 0 ; i < M ; i++ ) (U+j)[i] = sigma*((TMP+j)[i] - (U+j)[i]); @@ -988,7 +993,7 @@ int Kolmogorov2_TV(size_t M, size_t N, double*Y, double lambda, double*X, int ma for ( j = 0 ; j < N ; j++ ) row[j] = TMP[j*M+i]; // Prox operator - TV(row, lambda/(1.+1./tau), rowout, NULL, N, 1, NULL); + TV(row, lambda/(1.+1./tau), rowout, NULL, (int) N, 1, NULL); // Recover output for ( j = 0 ; j < N ; j++ ) X[j*M+i] = rowout[j]; diff --git a/src/TVL1Wopt.cpp b/src/TVL1Wopt.cpp index f367fe2..0f507bf 100644 --- a/src/TVL1Wopt.cpp +++ b/src/TVL1Wopt.cpp @@ -36,10 +36,12 @@ */ int PN_TV1_Weighted(double *y,double *lambda,double *x,double *info,int n,double sigma,Workspace *ws){ int i,ind,nI,recomp,found,iters,nn=n-1; - double lambdaMax,tmp,fval0,fval1,gRd,delta,grad0,stop,stopPrev,improve,rhs,maxStep,prevDelta; + double lambdaMax,tmp,fval0,fval1,gRd,delta,stop,stopPrev,improve,rhs,prevDelta; + double grad0 = 0.; + double maxStep = -DBL_MAX; double *w=NULL,*g=NULL,*d=NULL,*aux=NULL,*aux2=NULL; int *inactive=NULL; - lapack_int one=1,rc,nnp=nn,nIp; + lapack_int nnp=nn,nIp; /* Macros */ #define GRAD2GAP(g,w,gap,i) \ @@ -112,6 +114,8 @@ int PN_TV1_Weighted(double *y,double *lambda,double *x,double *info,int n,double } aux[nn-1] = 2; #ifdef PROXTV_USE_LAPACK + lapack_int one = 1; + lapack_int rc; dpttrf_(&nnp,aux,aux2,&rc); /* Solve Choleski-like linear system to obtain unconstrained solution */ dpttrs_(&nnp, &one, aux, aux2, w, &nnp, &rc); diff --git a/src/TVL1opt.cpp b/src/TVL1opt.cpp index 3ed35f0..5eb5391 100644 --- a/src/TVL1opt.cpp +++ b/src/TVL1opt.cpp @@ -36,10 +36,12 @@ */ int PN_TV1(double *y,double lambda,double *x,double *info,int n,double sigma,Workspace *ws){ int i,ind,nI,recomp,found,iters,nn=n-1; - double lambdaMax,tmp,fval0,fval1,gRd,delta,grad0,stop,stopPrev,improve,rhs,maxStep,prevDelta; + double lambdaMax,tmp,fval0,fval1,gRd,delta,stop,stopPrev,improve,rhs,prevDelta; + double grad0 = 0; + double maxStep = -DBL_MAX; double *w=NULL,*g=NULL,*d=NULL,*aux=NULL,*aux2=NULL; int *inactive=NULL; - lapack_int one=1,rc,nnp=nn,nIp; + lapack_int nnp=nn,nIp; /* Macros */ @@ -113,6 +115,8 @@ int PN_TV1(double *y,double lambda,double *x,double *info,int n,double sigma,Wor } aux[nn-1] = 2; #ifdef PROXTV_USE_LAPACK + lapack_int one; + lapack_int rc; dpttrf_(&nnp,aux,aux2,&rc); /* Solve Choleski-like linear system to obtain unconstrained solution */ dpttrs_(&nnp, &one, aux, aux2, w, &nnp, &rc); diff --git a/src/TVL1opt_tautstring.cpp b/src/TVL1opt_tautstring.cpp index 91f16b5..da81aa3 100644 --- a/src/TVL1opt_tautstring.cpp +++ b/src/TVL1opt_tautstring.cpp @@ -133,6 +133,7 @@ inline void freePB(Buffer* pb) { pb->first = pb->segments; \ pb->last = pb->first-1; + /******************************************** Taut-string functions ********************************************/ @@ -295,10 +296,12 @@ int classicTautString_TV1_offset(double *signal, int n, double lam, double *prox // Update majorant segment.incx = 1; segment.slope = segment.incy = signal[i]; +#pragma warning(suppress: 4244) concavemajorantadd(majorant, dirsegment, saux, iaux); // Update minorant segment.incx = 1; segment.slope = segment.incy = signal[i]; +#pragma warning(suppress: 4244) convexminorantadd(minorant, dirsegment, saux, iaux); // Update last explored point lastexplored.x++; @@ -316,10 +319,12 @@ int classicTautString_TV1_offset(double *signal, int n, double lam, double *prox // Update majorant with last segment segment.incx = 1; segment.slope = segment.incy = signal[n-1] + lam; +#pragma warning(suppress: 4244) concavemajorantadd(majorant, dirsegment, saux, iaux); // Update minorant with last segment segment.incx = 1; segment.slope = segment.incy = signal[n-1] - lam; +#pragma warning(suppress: 4244) convexminorantadd(minorant, dirsegment, saux, iaux); // At this point, because the endpoint of the tube is the same diff --git a/src/TVL2opt.cpp b/src/TVL2opt.cpp index 3ccd87c..af36ee3 100644 --- a/src/TVL2opt.cpp +++ b/src/TVL2opt.cpp @@ -36,7 +36,7 @@ int more_TV2(double *y,double lambda,double *x,double *info,int n){ int nn=n-1,i; double stop,tmp,lam,pNorm,qNorm,pNormSq,dist; double *Dy,*alpha,*beta,*minus,*p,*aux; - lapack_int one=1,rc,nnp=nn; + lapack_int nnp=nn; /* Macros */ @@ -105,6 +105,8 @@ int more_TV2(double *y,double lambda,double *x,double *info,int n){ #ifdef PROXTV_USE_LAPACK + lapack_int one = 1; + lapack_int rc; /* Compute tridiagonal factorization of Hessian */ dpttrf_(&nnp,alpha,beta,&rc); /* Obtain p by solving Cholesky system */ @@ -195,7 +197,7 @@ int morePG_TV2(double *y,double lambda,double *x,double *info,int n,Workspace *w int nn=n-1,i,iters; double stop,tmp,lam,pNorm,qNorm,pNormSq,dist; double *Dy=NULL,*alpha=NULL,*beta=NULL,*minus=NULL,*p,*aux; - lapack_int one=1,rc,nnp=nn; + lapack_int nnp=nn; /* Macros */ @@ -354,6 +356,8 @@ int morePG_TV2(double *y,double lambda,double *x,double *info,int n,Workspace *w memcpy((void*)aux,(void*)Dy,sizeof(double)*nn); #ifdef PROXTV_USE_LAPACK + lapack_int one = 1; + lapack_int rc; /* Compute tridiagonal factorization of Hessian */ dpttrf_(&nnp,alpha,beta,&rc); /* Obtain p by solving Cholesky system */ diff --git a/src/TVLPopt.cpp b/src/TVLPopt.cpp index 96c3b90..cea2f18 100644 --- a/src/TVLPopt.cpp +++ b/src/TVLPopt.cpp @@ -36,10 +36,11 @@ */ int GP_TVp(double *y,double lambda,double *x,double *info,int n,double p,Workspace *ws){ double *w=NULL,*aux=NULL,*aux2=NULL,*g; - double q,tmp,stop,dual,bestdual,lambdaMax,lambdaIni,lambdaCurrent,mu,musqrt,beta; + double q,tmp,stop,dual,bestdual,lambdaMax,lambdaIni,lambdaCurrent; + (void)(tmp); // Because tmp is unused in the macro int iter,stuck,nn,i,lambdaStep; Workspace *wsinner=NULL; - lapack_int one=1,rc,nnp; + lapack_int nnp; /* Problem constants */ #define L 4 // Lipschitz constant @@ -113,6 +114,8 @@ int GP_TVp(double *y,double lambda,double *x,double *info,int n,double p,Workspa aux[nn-1] = 2; nnp=nn; #ifdef PROXTV_USE_LAPACK + lapack_int one=1; + lapack_int rc; dpttrf_(&nnp,aux,aux2,&rc); /* Solve Choleski-like linear system to obtain unconstrained solution */ dpttrs_(&nnp, &one, aux, aux2, w, &nnp, &rc); @@ -155,12 +158,6 @@ int GP_TVp(double *y,double lambda,double *x,double *info,int n,double p,Workspa {CANCEL("error when invoking Lp ball projection subroutine",info)} for(i=0;iwarm = 0; @@ -397,7 +401,11 @@ int PDR_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double } /* Parallelize */ +#ifdef DEBUG #pragma omp parallel shared(ws,nSlices,ns,incs,x,p,q,lambdas,z,norms,npen,dims,DEBUG_FILE) private(d,i,j,k,idx1,idx2) default(none) +#else +#pragma omp parallel shared(ws,nSlices,ns,incs,x,p,q,lambdas,z,norms,npen,dims) private(d,i,j,k,idx1,idx2) default(none) +#endif { /* Get thread number */ int id = omp_get_thread_num(); @@ -408,7 +416,7 @@ int PDR_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double #ifdef DEBUG fprintf(DEBUG_FILE,"··········Penalty %d··········\n",i); #endif - d = dims[i]-1; + d = int( dims[i]-1); int top=nSlices[d]; wsi->warm = 0; @@ -596,7 +604,11 @@ double TVval(double *x,double *lambdas,double *norms,double *dims,int *ns,int nd /* Parallelize calculation of value */ + #ifdef DEBUG #pragma omp parallel shared(x,ws,nSlices,ns,incs,lambdas,norms,npen,dims,DEBUG_FILE) private(d,i,j,k,idx1,idx2) default(none) + #else + #pragma omp parallel shared(x,ws,nSlices,ns,incs,lambdas,norms,npen,dims) private(d,i,j,k,idx1,idx2) default(none) + #endif { /* Get thread number */ int id = omp_get_thread_num(); @@ -611,7 +623,7 @@ double TVval(double *x,double *lambdas,double *norms,double *dims,int *ns,int nd #ifdef DEBUG fprintf(DEBUG_FILE,"··········Penalty %d··········\n",i); #endif - d = dims[i]-1; + d = int(dims[i]-1); int top=nSlices[d]; @@ -678,7 +690,8 @@ double TVval(double *x,double *lambdas,double *norms,double *dims,int *ns,int nd int Yang3_TV(size_t M, size_t N, size_t O, double*Y, double lambda, double*X, int maxit, double* info) { double *U1 = NULL, *U2 = NULL, *U3 = NULL, *Z1 = NULL, *Z2 = NULL, *Z3 = NULL; double rho; - int i, j, k, idx; + size_t i, j, k; + size_t idx; Workspace *ws = NULL; #define FREE \ @@ -700,8 +713,9 @@ int Yang3_TV(size_t M, size_t N, size_t O, double*Y, double lambda, double*X, in rho = 10; // Alloc memory - int size = (M > N) ? M : N; size = (O > size) ? O : size; - int totalSize = M*N*O; + size_t size_long = (M > N) ? M : N ; size_long = (O > size_long) ? O : size_long; + int size = int(size_long); + size_t totalSize = M*N*O; U1 = (double*)calloc(totalSize,sizeof(double)); U2 = (double*)calloc(totalSize,sizeof(double)); U3 = (double*)calloc(totalSize,sizeof(double)); @@ -738,7 +752,7 @@ int Yang3_TV(size_t M, size_t N, size_t O, double*Y, double lambda, double*X, in ws->in[k] = -1. / rho * U1[idx] + X[idx]; } resetWorkspace(ws); - TV(ws->in, lambda/rho, ws->out, NULL, M, 1, ws); + TV(ws->in, lambda/rho, ws->out, NULL, (int) M, 1, ws); // Recover data memcpy(Z1 + M * ( i + N * j ), ws->out, sizeof(double)*M); } @@ -753,7 +767,7 @@ int Yang3_TV(size_t M, size_t N, size_t O, double*Y, double lambda, double*X, in ws->in[i] = -1. / rho * U2[idx] + X[idx]; } resetWorkspace(ws); - TV(ws->in, lambda/rho, ws->out, NULL, N, 1, ws); + TV(ws->in, lambda/rho, ws->out, NULL, (int) N, 1, ws); // Recover data for ( i = 0 ; i < N ; i++ ) { idx = k + M * ( i + N * j ); @@ -771,7 +785,7 @@ int Yang3_TV(size_t M, size_t N, size_t O, double*Y, double lambda, double*X, in ws->in[j] = -1. / rho * U3[idx] + X[idx]; } resetWorkspace(ws); - TV(ws->in, lambda/rho, ws->out, NULL, O, 1, ws); + TV(ws->in, lambda/rho, ws->out, NULL, (int) O, 1, ws); // Recover data for ( j = 0 ; j < O ; j++ ) { idx = k + M * ( i + N * j ); diff --git a/src/condat_fast_tv.cpp b/src/condat_fast_tv.cpp index 8b17065..ed0c25d 100644 --- a/src/condat_fast_tv.cpp +++ b/src/condat_fast_tv.cpp @@ -195,7 +195,7 @@ void TV1D_denoise_tautstring(double* input, double* output, int width, const dou z[c+i]=y_low[index[c+i]=index_low[s_low+i]]; c = c + c_low-s_low; int j=0; - float a; + floattype a; i=1; while (i<=c) { a = (z[i]-z[i-1]) / (index[i]-index[i-1]); diff --git a/src/general.h b/src/general.h index 2ab8644..bd3efd8 100644 --- a/src/general.h +++ b/src/general.h @@ -26,8 +26,6 @@ inline double mxGetInf() { return INFINITY; } /* Choose here the name of the debug file */ #ifdef DEBUG static FILE* DEBUG_FILE = fopen("debug.tmp","w"); -#else - static FILE* DEBUG_FILE = NULL; #endif #define DEBUG_N 10 /* Maximum vector length to print in debug messages */ From 187f868861f53b74fd00eeb40eb7ba83c2b7809d Mon Sep 17 00:00:00 2001 From: Pablo Hernandez-Cerdan Date: Thu, 11 Apr 2019 14:59:10 -0400 Subject: [PATCH 08/13] COMP: Remove ifdef DEBUG for pragma omp --- src/TV2Dopt.cpp | 34 ---------------------------------- src/TVNDopt.cpp | 47 ----------------------------------------------- 2 files changed, 81 deletions(-) diff --git a/src/TV2Dopt.cpp b/src/TV2Dopt.cpp index 29c1e25..0c5ccfb 100644 --- a/src/TV2Dopt.cpp +++ b/src/TV2Dopt.cpp @@ -170,11 +170,7 @@ int PD2_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double #endif d = int(dims[0]-1); /* Run 1-dimensional prox operator over each 1-dimensional slice along the specified dimension (parallelized) */ -#ifdef DEBUG - #pragma omp parallel shared(ws,nSlices,ns,d,incs,x,p,lambdas,z,norms,DEBUG_FILE) private(j,k,idx1,idx2) default(none) -#else #pragma omp parallel shared(ws,nSlices,ns,d,incs,x,p,lambdas,z,norms) private(j,k,idx1,idx2) default(none) -#endif { /* Get thread number */ int id = omp_get_thread_num(); @@ -191,16 +187,6 @@ int PD2_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double for(k=0,idx2=0 ; kin[k] = x[idx1+idx2]+p[idx1+idx2]; - #ifdef DEBUG - { - int dbgi; - fprintf(DEBUG_FILE,"Slice %d: ",j); - for(dbgi=0;dbgiin[dbgi]); - fprintf(DEBUG_FILE,"\n"); - } - #endif - /* Apply 1-dimensional solver */ resetWorkspace(wsi); TV(wsi->in, lambdas[0], wsi->out, NULL, ns[d], norms[0], wsi); @@ -218,17 +204,10 @@ int PD2_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double /* Prox step for the second penalty term (if any) */ if(npen >= 2){ - #ifdef DEBUG - fprintf(DEBUG_FILE,"··········Penalty 1··········\n"); - #endif d = int(dims[1]-1); /* Run 1-dimensional prox operator over each 1-dimensional slice along the specified dimension (parallelized) */ -#ifdef DEBUG - #pragma omp parallel shared(ws,nSlices,ns,d,incs,x,q,lambdas,z,norms,DEBUG_FILE) private(j,k,idx1,idx2) default(none) -#else #pragma omp parallel shared(ws,nSlices,ns,d,incs,x,q,lambdas,z,norms) private(j,k,idx1,idx2) default(none) -#endif { /* Get thread number */ int id = omp_get_thread_num(); @@ -245,16 +224,6 @@ int PD2_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double for(k=0,idx2=0 ; kin[k] = z[idx1+idx2] + q[idx1+idx2]; - #ifdef DEBUG - { - int dbgi; - fprintf(DEBUG_FILE,"Slice %d: ",j); - for(dbgi=0;dbgiin[dbgi]); - fprintf(DEBUG_FILE,"\n"); - } - #endif - /* Apply 1-dimensional solver */ resetWorkspace(wsi); TV(wsi->in, lambdas[1], wsi->out, NULL, ns[d], norms[1], wsi); @@ -295,9 +264,6 @@ int PD2_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double /* Termination check */ if(iters >= MAX_ITERS_PD){ - #ifdef DEBUG - fprintf(DEBUG_FILE,"(PD2_TV) WARNING: maximum number of iterations reached (%d).\n",MAX_ITERS_PD); - #endif if(info) info[INFO_RC] = RC_ITERS; } else if(info) info[INFO_RC] = RC_OK; diff --git a/src/TVNDopt.cpp b/src/TVNDopt.cpp index 1fc04ec..bda1c66 100644 --- a/src/TVNDopt.cpp +++ b/src/TVNDopt.cpp @@ -161,11 +161,7 @@ int PD_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double } /* Parallelize */ -#ifdef DEBUG - #pragma omp parallel shared(ws,nSlices,ns,incs,x,p,lambdas,z,norms,npen,dims,DEBUG_FILE) private(d,i,j,k,idx1,idx2) default(none) -#else #pragma omp parallel shared(ws,nSlices,ns,incs,x,p,lambdas,z,norms,npen,dims) private(d,i,j,k,idx1,idx2) default(none) -#endif { /* Get thread number */ int id = omp_get_thread_num(); @@ -173,9 +169,6 @@ int PD_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double /* Prox step for every penalty term */ for(i=0;iin[k] = z[i][idx1+idx2]; - #ifdef DEBUG - { - int dbgi; - fprintf(DEBUG_FILE,"Slice %d: ",j); - for(dbgi=0;dbgiin[dbgi]); - fprintf(DEBUG_FILE,"\n"); - } - #endif - /* Apply 1-dimensional solver */ resetWorkspace(wsi); TV(wsi->in, lambdas[i], wsi->out, NULL, ns[d], norms[i], wsi); @@ -241,9 +224,6 @@ int PD_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double /* Termination check */ if(iters >= MAX_ITERS_PD){ - #ifdef DEBUG - fprintf(DEBUG_FILE,"(PD_TV) WARNING: maximum number of iterations reached (%d).\n",MAX_ITERS_PD); - #endif if(info) info[INFO_RC] = RC_ITERS; } else if(info) info[INFO_RC] = RC_OK; @@ -401,11 +381,7 @@ int PDR_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double } /* Parallelize */ -#ifdef DEBUG -#pragma omp parallel shared(ws,nSlices,ns,incs,x,p,q,lambdas,z,norms,npen,dims,DEBUG_FILE) private(d,i,j,k,idx1,idx2) default(none) -#else #pragma omp parallel shared(ws,nSlices,ns,incs,x,p,q,lambdas,z,norms,npen,dims) private(d,i,j,k,idx1,idx2) default(none) -#endif { /* Get thread number */ int id = omp_get_thread_num(); @@ -413,9 +389,6 @@ int PDR_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double /* Prox step for every penalty term */ for(i=0;iin[k] = z[i][idx1+idx2]; - #ifdef DEBUG - { - int dbgi; - fprintf(DEBUG_FILE,"Slice %d: ",j); - for(dbgi=0;dbgiin[dbgi]); - fprintf(DEBUG_FILE,"\n"); - } - #endif - /* Apply 1-dimensional solver */ resetWorkspace(wsi); if(norms[i] == 1) @@ -493,9 +456,6 @@ int PDR_TV(double *y,double *lambdas,double *norms,double *dims,double *x,double /* Termination check */ if(iters >= MAX_ITERS_DR){ - #ifdef DEBUG - fprintf(DEBUG_FILE,"(PDR_TV) WARNING: maximum number of iterations reached (%d).\n",MAX_ITERS_DR); - #endif if(info) info[INFO_RC] = RC_ITERS; } else if(info) info[INFO_RC] = RC_OK; @@ -604,11 +564,7 @@ double TVval(double *x,double *lambdas,double *norms,double *dims,int *ns,int nd /* Parallelize calculation of value */ - #ifdef DEBUG - #pragma omp parallel shared(x,ws,nSlices,ns,incs,lambdas,norms,npen,dims,DEBUG_FILE) private(d,i,j,k,idx1,idx2) default(none) - #else #pragma omp parallel shared(x,ws,nSlices,ns,incs,lambdas,norms,npen,dims) private(d,i,j,k,idx1,idx2) default(none) - #endif { /* Get thread number */ int id = omp_get_thread_num(); @@ -620,9 +576,6 @@ double TVval(double *x,double *lambdas,double *norms,double *dims,int *ns,int nd /* Value for every penalty term */ for(i=0;i Date: Mon, 15 Jul 2019 15:06:11 -0400 Subject: [PATCH 09/13] COMP: OpenMP_CXX.lib does not exist with Visual Studio --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aad66aa..dbf5036 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -70,7 +70,7 @@ else() endif() # OpenMP -if(OpenMP_CXX_FOUND) +if(OpenMP_CXX_FOUND AND NOT MSVC) # target_compile_definitions(proxTV PRIVATE _OPENMP) target_link_libraries(proxTV PRIVATE OpenMP::OpenMP_CXX) endif() From 4be92275e3df07880c858c9f509e5f5d1d1a5211 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Thu, 1 Aug 2019 17:32:58 -0400 Subject: [PATCH 10/13] Address Visual Studio int64 to int possible loss of data warning _deps\proxtv_fetch-src\src\TVL1opt_tautstring.cpp(336): warning C4244: '=': conversion from '__int64' to 'int', possible loss of data --- src/TVL1opt_tautstring.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TVL1opt_tautstring.cpp b/src/TVL1opt_tautstring.cpp index da81aa3..303ddc0 100644 --- a/src/TVL1opt_tautstring.cpp +++ b/src/TVL1opt_tautstring.cpp @@ -290,7 +290,7 @@ int classicTautString_TV1_offset(double *signal, int n, double lam, double *prox // Iterate along the signal length Segment *saux; - int i, iaux; + size_t i, iaux; double *pwriter = prox; for ( i = 1 ; i < n-1 ; i++ ) { // Update majorant From 465dfd4cfc5a51fc8533030383989dac2901d834 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Fri, 2 Aug 2019 10:42:52 -0400 Subject: [PATCH 11/13] Fix Visual Studio divide by zero warning suppression Note: LAMBDA_STEPS_TVLP is a a preprocessor definition set to 1. --- src/TVLPopt.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/TVLPopt.cpp b/src/TVLPopt.cpp index cea2f18..f43dcff 100644 --- a/src/TVLPopt.cpp +++ b/src/TVLPopt.cpp @@ -228,13 +228,17 @@ int GP_TVp(double *y,double lambda,double *x,double *info,int n,double p,Workspa while(stop < STOP_TVLP){ /* If met, move to following lambda step, if any */ lambdaStep++; + // Avoid divide by zero + #if LAMBDA_STEPS_TVLP > 1 if(lambdaStep < LAMBDA_STEPS_TVLP){ -#pragma warning(suppress: 4723) lambdaCurrent = pow(10, log10(lambdaIni) + lambdaStep * log10(LAMBDA_REDUCTION_TVLP) / ((double)(LAMBDA_STEPS_TVLP-1)) ); GRAD2GAP(w,g,stop,lambdaCurrent,p,n,i,tmp) stuck = 0; bestdual = DBL_MAX; } else break; + #else + break; + #endif } #ifdef TIMING @@ -521,13 +525,16 @@ int OGP_TVp(double *y,double lambda,double *x,double *info,int n,double p,Worksp while(stop < STOP_TVLP){ /* If met, move to following lambda step, if any */ lambdaStep++; + #if LAMBDA_STEPS_TVLP > 1 if(lambdaStep < LAMBDA_STEPS_TVLP){ -#pragma warning(suppress: 4723) lambdaCurrent = pow(10, log10(lambdaIni) + lambdaStep * log10(LAMBDA_REDUCTION_TVLP) / ((double)(LAMBDA_STEPS_TVLP-1)) ); GRAD2GAP(w,g,stop,lambdaCurrent,p,n,i,tmp) stuck = 0; bestdual = DBL_MAX; } else break; + #else + break; + #endif } #ifdef TIMING @@ -815,13 +822,16 @@ int FISTA_TVp(double *y,double lambda,double *x,double *info,int n,double p,Work while(stop < STOP_TVLP){ /* If met, move to following lambda step, if any */ lambdaStep++; + #if LAMBDA_STEPS_TVLP > 1 if(lambdaStep < LAMBDA_STEPS_TVLP){ -#pragma warning(suppress: 4723) lambdaCurrent = pow(10, log10(lambdaIni) + lambdaStep * log10(LAMBDA_REDUCTION_TVLP) / ((double)(LAMBDA_STEPS_TVLP-1)) ); GRAD2GAP(w,g,stop,lambdaCurrent,p,n,i,tmp) stuck = 0; bestdual = DBL_MAX; } else break; + #else + break; + #endif } #ifdef TIMING From 4a3df0848eff022c9d1723ec5ce0d8e32e4c7bbe Mon Sep 17 00:00:00 2001 From: Pablo Hernandez-Cerdan Date: Thu, 30 Jan 2020 19:51:30 +0100 Subject: [PATCH 12/13] CMake: Allow to use existing Eigen3 target instead of find_package This allows building proxTV with Eigen, when Eigen is an internal target, instead of only allowing find_package(Eigen) Closes https://github.com/InsightSoftwareConsortium/ITKTotalVariation/issues/21 --- CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e069d00..8ac215e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,7 +57,12 @@ if(proxTV_USE_LAPACK) find_package(LAPACK REQUIRED) message(STATUS "Lapack libraries: ${LAPACK_LIBRARIES}") else() - find_package(Eigen3 REQUIRED) + # No need to find Eigen3 if the Eigen3 target exists already. + # This happens when a project is fetching Eigen before fetching proxTV with add_subdirectory + # Particularly, this allows to build the module inside ITK when Module_TotalVariation is ON + if(NOT TARGET Eigen3::Eigen3) + find_package(Eigen3 REQUIRED) + endif() get_target_property(EIGEN_INCLUDE_DIR Eigen3::Eigen INTERFACE_INCLUDE_DIRECTORIES) message(STATUS "Eigen Found: ${EIGEN_INCLUDE_DIR}") From 0130fca231dda1ec3de13bcc888b576055d6d3d5 Mon Sep 17 00:00:00 2001 From: Pablo Hernandez-Cerdan Date: Thu, 20 Feb 2020 11:39:32 +0100 Subject: [PATCH 13/13] BUG: CMake: Allow user to change install directories with variables: ```cmake proxTV_INSTALL_INCLUDE_DIR proxTV_INSTALL_LIB_DIR proxTV_INSTALL_BIN_DIR proxTV_INSTALL_CMAKE_DIR ``` These variables where ignored, and other CACHE variables were used. `proxTV_INSTALL_CMAKE_DIR` was added with a default value. --- CMakeLists.txt | 12 +++++++++--- src/CMakeLists.txt | 20 ++++++++------------ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ac215e..f1f396c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,7 +46,13 @@ endif() if(NOT DEFINED proxTV_INSTALL_INCLUDE_DIR) set(proxTV_INSTALL_INCLUDE_DIR include) endif() +if(NOT DEFINED proxTV_INSTALL_CMAKE_DIR) + set(proxTV_INSTALL_CMAKE_DIR ${proxTV_INSTALL_LIB_DIR}/cmake/proxTV) +endif() +message(STATUS "proxTV_INSTALL_INCLUDE_DIR: ${proxTV_INSTALL_INCLUDE_DIR}") +message(STATUS "proxTV_INSTALL_LIB_DIR: ${proxTV_INSTALL_LIB_DIR}") +message(STATUS "proxTV_INSTALL_CMAKE_DIR: ${proxTV_INSTALL_CMAKE_DIR}") #------------------------------------------------------------------------------ # External dependencies @@ -120,7 +126,7 @@ if(proxTV_INSTALL_DEVELOPMENT) configure_package_config_file( cmake/proxTVConfig.cmake.in ${install_config} - INSTALL_DESTINATION ${proxTV_INSTALL_LIB_DIR}/cmake/proxTV + INSTALL_DESTINATION ${proxTV_INSTALL_CMAKE_DIR} NO_CHECK_REQUIRED_COMPONENTS_MACRO ) @@ -128,14 +134,14 @@ if(proxTV_INSTALL_DEVELOPMENT) install(EXPORT proxTVTargets FILE proxTVTargets.cmake NAMESPACE proxTV:: - DESTINATION ${proxTV_INSTALL_LIB_DIR}/cmake/proxTV + DESTINATION ${proxTV_INSTALL_CMAKE_DIR} COMPONENT Development ) # Install config files install( FILES ${config_version_file} ${install_config} - DESTINATION ${proxTV_INSTALL_LIB_DIR}/cmake/proxTV + DESTINATION ${proxTV_INSTALL_CMAKE_DIR} COMPONENT Development ) endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dbf5036..f320421 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,3 @@ -set(LIB_INSTALL_DIR lib CACHE STRING "Install folder for lib (relative") -set(INCLUDE_INSTALL_DIR include CACHE STRING "Install folder for headers (relative") -set(EXECUTABLE_INSTALL_DIR bin CACHE STRING "Install folder for executables (relative") - set(headers condat_fast_tv.h general.h @@ -43,12 +39,12 @@ endif() file(COPY ${headers} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/include") install(FILES ${headers} - DESTINATION ${INCLUDE_INSTALL_DIR}) + DESTINATION ${proxTV_INSTALL_INCLUDE_DIR}) target_include_directories(proxTV PUBLIC $ $ - $ + $ ) # Pthreads @@ -76,10 +72,10 @@ if(OpenMP_CXX_FOUND AND NOT MSVC) endif() install(TARGETS proxTV EXPORT proxTVTargets - LIBRARY DESTINATION ${LIB_INSTALL_DIR} - ARCHIVE DESTINATION ${LIB_INSTALL_DIR} - RUNTIME DESTINATION ${EXECUTABLE_INSTALL_DIR} - INCLUDES DESTINATION ${INCLUDE_INSTALL_DIR} + LIBRARY DESTINATION ${proxTV_INSTALL_LIB_DIR} + ARCHIVE DESTINATION ${proxTV_INSTALL_LIB_DIR} + RUNTIME DESTINATION ${proxTV_INSTALL_BIN_DIR} + INCLUDES DESTINATION ${proxTV_INSTALL_INCLUDE_DIR} ) # This saves as to develop a hacky FindproxTV for other to use the library. @@ -88,7 +84,7 @@ install(TARGETS proxTV EXPORT proxTVTargets install(EXPORT proxTVTargets FILE proxTVTargets.cmake NAMESPACE proxTV:: - DESTINATION ${LIB_INSTALL_DIR}/cmake/proxTV + DESTINATION ${proxTV_INSTALL_CMAKE_DIR} ) # Generate and install proxTVConfigVersion.cmake @@ -99,5 +95,5 @@ write_basic_package_version_file("proxTVConfigVersion.cmake" ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/proxTVConfigVersion.cmake - DESTINATION ${LIB_INSTALL_DIR}/cmake/proxTV + DESTINATION ${proxTV_INSTALL_CMAKE_DIR} )