diff --git a/.docker/Dockerfile b/.docker/Dockerfile index 08be8db4..fd2397a6 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -16,8 +16,8 @@ RUN git clone --depth=1 --branch 3.7.02 https://github.com/kokkos/kokkos.git && -DBUILD_SHARED_LIBS=ON \ -DCMAKE_CXX_STANDARD=17 \ ../ && \ - make -j$(nproc) && \ - make install && \ + cmake --build . -- -j$(nproc) && \ + cmake --install . && \ cd / && \ rm -rf kokkos @@ -25,8 +25,8 @@ RUN cd MParT_ && \ mkdir build && \ cd build && \ cmake -DMPART_BUILD_TESTS=OFF -DPYTHON_EXECUTABLE=`which python` -DMPART_FETCH_DEPS=OFF ../ && \ - make -j$(nproc) && \ - make install && \ + cmake --build . -- -j$(nproc) && \ + cmake --install . && \ cd / && \ rm -rf MParT_ diff --git a/.github/environment.yml b/.github/environment.yml index e14fe0d1..6f8ad3e8 100644 --- a/.github/environment.yml +++ b/.github/environment.yml @@ -15,3 +15,4 @@ dependencies: - pytorch - cxx-compiler - dill + - cmake =3.31 diff --git a/.github/workflows/build-bindings.yml b/.github/workflows/build-bindings.yml index 91337b47..ba6a688b 100644 --- a/.github/workflows/build-bindings.yml +++ b/.github/workflows/build-bindings.yml @@ -8,13 +8,12 @@ on: pull_request: {} env: - CONDA_CACHE_NUMBER: 0 # increase to reset cache manually + CONDA_CACHE_NUMBER: 0 # increase to reset cache manually jobs: build-tests: runs-on: ubuntu-latest steps: - - name: Store Date shell: bash -l {0} run: echo "DATE=$(date +'%Y%m%d')" >> $GITHUB_ENV @@ -25,26 +24,26 @@ jobs: path: mpart - name: Use Conda - uses: conda-incubator/setup-miniconda@v2 + uses: conda-incubator/setup-miniconda@v3 with: - miniforge-variant: Mambaforge - miniforge-version: latest - activate-environment: test - use-mamba: true auto-update-conda: true python-version: "3.8" + activate-environment: "test" - name: Cache Conda Deps uses: actions/cache@v3 id: cache-conda with: - path: /usr/share/miniconda3/envs/test + path: /usr/share/miniconda/envs/test key: cache-conda-${{ hashFiles('mpart/.github/environment.yml') }}-${{ env.DATE }}-${{ env.CONDA_CACHE_NUMBER }}-BINDINGS - name: Install Conda Dependencies shell: bash -l {0} - run: mamba env update -n test -f $GITHUB_WORKSPACE/mpart/.github/environment.yml + run: conda env update -n test -f $GITHUB_WORKSPACE/mpart/.github/environment.yml if: steps.cache-conda.outputs.cache-hit != 'true' + - uses: julia-actions/setup-julia@v2 + with: + version: "1.10" - name: Setup Julia run: | @@ -62,7 +61,7 @@ jobs: id: cache-kokkos with: path: "${{ github.workspace }}/KOKKOS_INSTALL" - key: kokkos4.2.00 + key: kokkos4.5.01 - if: ${{steps.cache-kokkos.outputs.cache-hit != 'true'}} name: Checkout Kokkos @@ -70,7 +69,7 @@ jobs: with: repository: kokkos/kokkos path: kokkos - ref: '4.2.00' + ref: "4.5.01" - if: ${{steps.cache-kokkos.outputs.cache-hit != 'true'}} name: Install Kokkos @@ -83,7 +82,8 @@ jobs: -DCMAKE_CXX_STANDARD=17 \ -DCMAKE_INSTALL_PREFIX=$GITHUB_WORKSPACE/KOKKOS_INSTALL/ \ ../ - sudo make -j2 install + sudo cmake --build . -- -j2 + sudo cmake --install . - name: Configure MParT shell: bash -l {0} @@ -97,7 +97,7 @@ jobs: - name: Build MParT shell: bash -l {0} - run: cd $GITHUB_WORKSPACE/mpart/build; make -j2 install + run: cd $GITHUB_WORKSPACE/mpart/build; cmake --build . -- -j2; cmake --install . - name: Run Python Tests continue-on-error: true @@ -132,4 +132,4 @@ jobs: if: always() with: check_name: "Test Results with Bindings" - junit_files: ${{ github.workspace }}/test-results-*.xml \ No newline at end of file + files: ${{ github.workspace }}/test-results-*.xml diff --git a/.github/workflows/build-doc.yml b/.github/workflows/build-doc.yml index 16e54588..a0a5ff9b 100644 --- a/.github/workflows/build-doc.yml +++ b/.github/workflows/build-doc.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Use Conda - uses: conda-incubator/setup-miniconda@v2 + uses: conda-incubator/setup-miniconda@v3 with: auto-update-conda: true python-version: "3.11" @@ -46,8 +46,8 @@ jobs: - name: Build Docs shell: bash -l {0} run: | - cd ${{ github.workspace }} && cd build - make sphinx + cd ${{ github.workspace }} && cd build + make sphinx - name: Push to gh-pages uses: peaceiris/actions-gh-pages@v3.6.1 diff --git a/.github/workflows/build-external-lib-tests.yml b/.github/workflows/build-external-lib-tests.yml index e58f2c03..11738e1a 100644 --- a/.github/workflows/build-external-lib-tests.yml +++ b/.github/workflows/build-external-lib-tests.yml @@ -8,7 +8,7 @@ on: pull_request: {} env: - CONDA_CACHE_NUMBER: 0 # increase to reset cache manually + CONDA_CACHE_NUMBER: 0 # increase to reset cache manually jobs: build-tests: @@ -24,14 +24,11 @@ jobs: path: mpart - name: Use Conda - uses: conda-incubator/setup-miniconda@v2 + uses: conda-incubator/setup-miniconda@v3 with: - miniforge-variant: Mambaforge - miniforge-version: latest - activate-environment: test - use-mamba: true auto-update-conda: true python-version: "3.8" + activate-environment: "test" - name: Cache Conda Deps uses: actions/cache@v3 @@ -45,7 +42,7 @@ jobs: id: cache-kokkos with: path: "${{ github.workspace }}/KOKKOS_INSTALL" - key: kokkos4.2.00 + key: kokkos4.5.01 - if: ${{steps.cache-kokkos.outputs.cache-hit != 'true'}} name: Checkout Kokkos @@ -53,7 +50,7 @@ jobs: with: repository: kokkos/kokkos path: kokkos - ref: '4.2.00' + ref: "4.5.01" - if: ${{steps.cache-kokkos.outputs.cache-hit != 'true'}} name: Install Kokkos @@ -86,11 +83,11 @@ jobs: - name: Run Tests shell: bash -l {0} - run: cd $GITHUB_WORKSPACE/mpart/build; ./RunTests --kokkos-threads=2 --reporter junit -o test-results-external.xml + run: cd $GITHUB_WORKSPACE/mpart/build; ./RunTests --kokkos-num-threads=2 --reporter junit -o test-results-external.xml - name: Publish Unit Test Results uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: check_name: "Test Results with Externally Built Libraries" - junit_files: ${{ github.workspace }}/mpart/build/test-results-external.xml \ No newline at end of file + junit_files: ${{ github.workspace }}/mpart/build/test-results-external.xml diff --git a/.github/workflows/build-tests.yml b/.github/workflows/build-tests.yml index 2ba6d1bf..c7fb9859 100644 --- a/.github/workflows/build-tests.yml +++ b/.github/workflows/build-tests.yml @@ -14,18 +14,29 @@ jobs: - name: Checkout uses: actions/checkout@v1 + - name: Install compiler + uses: rlalik/setup-cpp-compiler@master + with: + compiler: g++-12 + + - name: Install CMake + uses: lukka/get-cmake@latest + with: + cmakeVersion: 3.25.2 + ninjaVersion: 1.13.1 + - name: Run CMake run: | cd ${{ github.workspace }} mkdir build cd build - cmake -DKokkos_ENABLE_THREADS=ON -DKokkos_ENABLE_SERIAL=ON ../ + cmake -GNinja -DKokkos_ENABLE_THREADS=ON -DKokkos_ENABLE_SERIAL=ON ../ - name: Build - run: cd build; make -j2 + run: cd build; cmake --build . -- -j2 - name: Run Tests - run: cd build; ./RunTests --kokkos-threads=2 --reporter junit -o test-results.xml + run: cd build; ./RunTests --kokkos-num-threads=2 --reporter junit -o test-results.xml - name: Publish Unit Test Results uses: EnricoMi/publish-unit-test-result-action@v1 @@ -33,5 +44,3 @@ jobs: with: check_name: "Test Results" files: build/test-results.xml - - diff --git a/.github/workflows/pypi-deploy.yml b/.github/workflows/pypi-deploy.yml index bb0915f9..48c59fb9 100644 --- a/.github/workflows/pypi-deploy.yml +++ b/.github/workflows/pypi-deploy.yml @@ -37,7 +37,7 @@ jobs: - name: Build sdist run: pipx run build --sdist - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: path: dist/*.tar.gz diff --git a/CMakeLists.txt b/CMakeLists.txt index 861f4d65..a550b354 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.13...3.31) project(MParT VERSION 2.2.1) message(STATUS "Will install MParT to ${CMAKE_INSTALL_PREFIX}") @@ -88,7 +88,7 @@ if(NOT Kokkos_FOUND) FetchContent_Declare( kokkos GIT_REPOSITORY https://github.com/kokkos/kokkos - GIT_TAG 4.2.00 + GIT_TAG 4.5.01 GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(kokkos) @@ -96,7 +96,7 @@ if(NOT Kokkos_FOUND) message(FATAL_ERROR "Could not find Kokkos library and MPART_FETCH_DEPS=OFF, so CMake will not attempt to fetch and install Kokkos itself.") endif() else() - message(STATUS "Found Kokkos!") + message(STATUS "Found Kokkos: ${Kokkos_DIR}") endif() if(${COMPILER_IS_NVCC}) @@ -297,7 +297,7 @@ if(MPART_BUILD_TESTS) FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git - GIT_TAG v3.2.1 + GIT_TAG v3.4.0 ) FetchContent_MakeAvailable(Catch2) diff --git a/MParT/Initialization.h b/MParT/Initialization.h index 5f3cc264..e6b7c8cb 100644 --- a/MParT/Initialization.h +++ b/MParT/Initialization.h @@ -1,120 +1,128 @@ #ifndef MPART_INITIALIZATION_H #define MPART_INITIALIZATION_H - #include -#include -#include -#include #include #include +#include +#include +#include #if defined(MPART_ENABLE_GPU) -#include #include +#include #include -#endif +#endif -namespace mpart{ +namespace mpart { - /** @defgroup InitializationHelpers +/** @defgroup InitializationHelpers - @code{.cpp} +@code{.cpp} #include "MParT/Initialization.h" int main( int argc, char* argv[] ) { - mpart::Initialize(argc,argv); +mpart::Initialize(argc,argv); - return 0; +return 0; } - @endcode - */ - - /** - @brief Call Kokkos::Finalize - @ingroup InitializationHelpers - @details the mpart::Initialize function will set add this function to the `atexit` list so that it is called at program termination. - */ - void Finalize(); - - // Simply holds a private variable isInitialized. A static instance of this function is used in the GetInitializeStatusObject object. - struct InitializeStatus{ - - bool Get(){return isInitialized;}; - void Set(){isInitialized = true;} - - #if defined(MPART_ENABLE_GPU) - cublasHandle_t& GetCublasHandle(){return blasHandle;} - cusolverDnHandle_t& GetCusolverHandle(){return solverHandle;} - #endif - private: - bool isInitialized = false; - - #if defined(MPART_ENABLE_GPU) - cublasHandle_t blasHandle; - cusolverDnHandle_t solverHandle; - #endif - }; - - // Holds a static InitializeStatus object - InitializeStatus& GetInitializeStatusObject(); - - /** - @brief Calls Kokkos::initialize if it hasn't been called yet. - @ingroup InitializationHelpers - @details - This function provides a thin wrapper around the Kokkos::initialize function that can be called multiple times without error. - This function also adds Kokkos::finalize to `atexit` so that manually calling Kokkos::finalize is not necessary. - - Note that Kokkos::initialize can only be called once and this function will print a warning message if called multiple times. The warning - indicates that any changes to Kokkos parameters, like `--kokkos-threads` in subsequent calls will have no impact. Only the - parameters passed the to the first mpart::Initialize call will be used. - - The warning messages can be silenced by setting the `MPART_WARNINGS` environment variable to `OFF`. - - @tparam Arguments - @param args Parameters to be passed on to Kokkos::initialize. - */ - template - void Initialize(FirstArgType& arg1, Arguments... args) - { - if(!GetInitializeStatusObject().Get()){ - - // Initialize kokkos - Kokkos::initialize(arg1, args...); +@endcode +*/ + +/** + @brief Call Kokkos::Finalize + @ingroup InitializationHelpers + @details the mpart::Initialize function will set add this function to the + `atexit` list so that it is called at program termination. + */ +void Finalize(); + +// Simply holds a private variable isInitialized. A static instance of this +// function is used in the GetInitializeStatusObject object. +struct InitializeStatus { + bool Get() { return isInitialized; }; + void Set() { isInitialized = true; } #if defined(MPART_ENABLE_GPU) - // Set up the cublas handles - cublasCreate(&GetInitializeStatusObject().GetCublasHandle()); - cusolverDnCreate(&GetInitializeStatusObject().GetCusolverHandle()); + cublasHandle_t& GetCublasHandle() { return blasHandle; } + cusolverDnHandle_t& GetCusolverHandle() { return solverHandle; } #endif + private: + bool isInitialized = false; - // Make sure Kokkos::finalize() is called at program exit. - std::atexit(&mpart::Finalize); +#if defined(MPART_ENABLE_GPU) + cublasHandle_t blasHandle; + cusolverDnHandle_t solverHandle; +#endif +}; + +// Holds a static InitializeStatus object +InitializeStatus& GetInitializeStatusObject(); + +/** + @brief Calls Kokkos::initialize if it hasn't been called yet. + @ingroup InitializationHelpers + @details + This function provides a thin wrapper around the Kokkos::initialize function + that can be called multiple times without error. This function also adds + Kokkos::finalize to `atexit` so that manually calling Kokkos::finalize is not + necessary. + + Note that Kokkos::initialize can only be called once and this function will + print a warning message if called multiple times. The warning indicates that + any changes to Kokkos parameters, like `--kokkos-num-threads` in subsequent + calls will have no impact. Only the parameters passed the to the first + mpart::Initialize call will be used. + + The warning messages can be silenced by setting the `MPART_WARNINGS` + environment variable to `OFF`. + + @tparam Arguments + @param args Parameters to be passed on to Kokkos::initialize. + */ +template +void Initialize(FirstArgType& arg1, Arguments... args) { + if (!GetInitializeStatusObject().Get()) { + // Initialize kokkos + Kokkos::initialize(arg1, args...); - // Update the state to remember that we called Initialize - GetInitializeStatusObject().Set(); +#if defined(MPART_ENABLE_GPU) + // Set up the cublas handles + cublasCreate(&GetInitializeStatusObject().GetCublasHandle()); + cusolverDnCreate(&GetInitializeStatusObject().GetCusolverHandle()); +#endif - }else{ + // Make sure Kokkos::finalize() is called at program exit. + std::atexit(&mpart::Finalize); - const char* warningStr = std::getenv("MPART_WARNINGS"); + // Update the state to remember that we called Initialize + GetInitializeStatusObject().Set(); - bool shouldPrint = true; - if(warningStr != nullptr){ - std::string warningFlag = warningStr; + } else { + const char* warningStr = std::getenv("MPART_WARNINGS"); - // Convert to lowercase so we catch OFF, off, Off, or any combination of capitalization. - std::transform(warningFlag.begin(), warningFlag.end(), warningFlag.begin(), [](unsigned char c){ return std::tolower(c); }); + bool shouldPrint = true; + if (warningStr != nullptr) { + std::string warningFlag = warningStr; - if(warningFlag=="off") - shouldPrint = false; - } + // Convert to lowercase so we catch OFF, off, Off, or any combination of + // capitalization. + std::transform(warningFlag.begin(), warningFlag.end(), + warningFlag.begin(), + [](unsigned char c) { return std::tolower(c); }); - if(shouldPrint) - std::cout << "WARNING: mpart::Initialize has already been called. Any changes to runtime settings (e.g., \"--kokkos-threads\") will not go into effect." << std::endl; - } + if (warningFlag == "off") shouldPrint = false; } -} // namespace mpart + if (shouldPrint) + std::cout + << "WARNING: mpart::Initialize has already been called. Any " + "changes to runtime settings (e.g., \"--kokkos-num-threads\") " + "will not go into effect." + << std::endl; + } +} + +} // namespace mpart #endif \ No newline at end of file diff --git a/README.md b/README.md index 01dd1fde..02cf4ba4 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,15 @@ [![DOI](https://joss.theoj.org/papers/10.21105/joss.04843/status.svg)](https://doi.org/10.21105/joss.04843) # MParT: A Monotone Parameterization Toolkit + A CPU/GPU performance-portable library for parameterizing and constructing monotone functions in the context of measure transport and regression. ## Documentation + See [measuretransport.github.io/MParT/](https://measuretransport.github.io/MParT/) for more extensive documentation. +# Citation + +``` +@article{Parno2022, doi = {10.21105/joss.04843}, url = {https://doi.org/10.21105/joss.04843}, year = {2022}, publisher = {The Open Journal}, volume = {7}, number = {80}, pages = {4843}, author = {Parno, Matthew and Rubio, Paul-Baptiste and Sharp, Daniel and Brennan, Michael and Baptista, Ricardo and Bonart, Henning and Marzouk, Youssef}, title = {MParT: Monotone Parameterization Toolkit}, journal = {Journal of Open Source Software} } +``` diff --git a/docs/source/installation.rst b/docs/source/installation.rst index b57924f4..7033f844 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -42,7 +42,7 @@ MParT uses CMake to handle dependencies and compiler configurations. A basic b .. make install -This will compile the main c++ :code:`mpart` library as well as any other language bindings that can be automatically configured. If you are compiling on a multicore machine, you can use :code:`make -j N_JOBS install`, where :code:`N_JOBS` is the number of processes the computer can compile with in parallel. +This will compile the main c++ :code:`mpart` library as well as any other language bindings that can be automatically configured. If you are compiling on a multicore machine, you can use :code:`cmake --build . -- -j N_JOBS`, where :code:`N_JOBS` is the number of processes the computer can compile with in parallel. This installation should also automatically install and build Kokkos, Eigen, Cereal, Pybind11, and Catch2, assuming they aren't installed already. If CMake has trouble finding prior installations of these, then you can configuring CMake using: @@ -94,7 +94,7 @@ Or, with the additional specification of the number of Kokkos threads to use: .. code-block:: - ./RunTests --kokkos-threads=4 + ./RunTests --kokkos-num-threads=4 Environment Paths diff --git a/pyproject.toml b/pyproject.toml index 56a8c3ad..03344549 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,7 +2,7 @@ requires = [ "setuptools", "scikit-build>=0.13", - "cmake", + "cmake<=3.31", "ninja", ] build-backend = "setuptools.build_meta" @@ -16,7 +16,7 @@ license={file="LICENSE.txt"} readme="README.md" requires-python = ">=3.7" description="A Monotone Parameterization Toolkit" -version="2.2.2" +version="2.2.3" keywords=["Measure Transport", "Monotone", "Transport Map", "Isotonic Regression", "Triangular", "Knothe-Rosenblatt"] [project.urls] diff --git a/tests/RunTests.cpp b/tests/RunTests.cpp index 051bd5dc..4e3cfff6 100644 --- a/tests/RunTests.cpp +++ b/tests/RunTests.cpp @@ -2,25 +2,26 @@ #include "MParT/Initialization.h" -int main( int argc, char* argv[] ) { - mpart::Initialize(argc,argv); +int main(int argc, char* argv[]) { + mpart::Initialize(argc, argv); - Catch::Session session; // There must be exactly one instance + Catch::Session session; // There must be exactly one instance - int cores = 0; // Some user variable you want to be able to set + int cores = 0; // Some user variable you want to be able to set // Build a new parser on top of Catch2's using namespace Catch::Clara; - auto cli - = session.cli() | Opt( cores, "kokkos-thread" ) ["--kokkos-threads"]("Number of cores to use with Kokkos."); + auto cli = + session.cli() | Opt(cores, "kokkos-thread")["--kokkos-num-threads"]( + "Number of cores to use with Kokkos."); // Now pass the new composite back to Catch2 so it uses that - session.cli( cli ); + session.cli(cli); // Let Catch2 (using Clara) parse the command line - int returnCode = session.applyCommandLine( argc, argv ); - if( returnCode != 0 ) // Indicates a command line error - return returnCode; + int returnCode = session.applyCommandLine(argc, argv); + if (returnCode != 0) // Indicates a command line error + return returnCode; session.run(); } \ No newline at end of file